This project was built on a CamJam EduKit 3 though it should work on most any Raspberry Pi robots.
This code gives a robotics engineer the ability to communicate with their robot via an MQTT broker. In doing so:
- any MQTT capable language can be used to add modules/intelligence to this robot (modules can be either local or remote)
- any MQTT capable language running on a remote machine can be used to command, listen to sensor signals, and/or listen to logging information.
This project allows the control of any Raspberry Pi robot (for example one built from a CamJam EduKti 3, no affiliation) through an MQTT message broker. It has been written in C# for performance reasons, though from the design architecture, it should be able to be extended by use of any programming language that understands MQTT.
This project was created so that I can teach kids how to program. The idea is to have them use either MIT App Inventor or Thunkable from their tablet to:
- Control all actuators in their robot
- Receive logging information from their robot
- Receive sensor signals from the robot
And although this project originally was conceived for either MIT App Inventor and Thunkable, by its architecture, any other programming language with an appropriate MQTT library can be used to both/either add modules and/or communicate with the robot.
To get this code up and running, you will need:
- An Assembled Robot: I used the CamJam EduKit 3, which uses double H-Bridges to control the motors. Any robot should work, as long as it is configured correctly. See Configuration section below.
- A Raspberry Pi: I've been using a Raspberry Pi 3B+ with lots of success
- An MQTT Message Broker: It can be running either locally in the robot (such as mosquitto) or in a remote server, accessible to all devices.
- Dotnet Core 3.1 installed in the Raspberry Pi: This is needed to both compile and publish the executables.
Installing MQTT
To install the mosquitto server and client in your Raspberry Pi, just install both the server and client:
sudo apt-get install mosquitto mosquitto-clients
To install dotnet
in your Raspberry Pi, follow the instructions found here.
To build the code, simply do the following:
cd /your/project/directory
dotnet build
dotnet publish -o /your/target/directory
For example, assuming that the source code is located in the ~/BlueCamJam
folder, and that you want to place the excutable in a folder named ~/Elliebot
do:
cd ~/BlueCamJam
dotnet build
dotnet publish -o ~/EllieBot
This will create a folder in your home directory named EllieBot
. In it, you will find the executable.
To configure your robot, run the EllieBot
executable for the first time to create a default configuration file.
To do so, simply type:
~/EllieBot/EllieBot
If this is your first time running EllieBot, then you will see it print a message, create a configuration file, and exit. You can find the configuration file in ~/EllieBot/elliebot_config.json
.
The elliebot_config.json
file is used to configure your robot.
Your configuration file will probably look something like this:
{
"DebuggingLevel": "INFO",
"MqttConnectionDescription": {
"Port": 1883,
"Host": "localhost",
"TopicForCommands": "com.efhilton.elliebot.topic.commands",
"TopicForLogging": "com.efhilton.elliebot.topic.logs",
"TopicForSensorData": "com.efhilton.elliebot.topic.sensors"
},
"DriveTrainDescription": {
"LeftMotorUniqueId": "com.efhilton.elliebot.id.motors.left",
"RightMotorUniqueId": "com.efhilton.elliebot.id.motors.right"
},
"Actuators": {
"HBridgeMotorDescriptions": [
{
"UniqueId": "com.efhilton.elliebot.id.motors.left",
"ForwardPinNumber": 8,
"BackwardPinNumber": 7
},
{
"UniqueId": "com.efhilton.elliebot.id.motors.right",
"ForwardPinNumber": 10,
"BackwardPinNumber": 9
}
],
"OutputPinDescriptions": [
{
"UniqueId": "com.efhilton.elliebot.id.leds.lightyellow",
"OutputPinNumber": 18
}
]
},
"Sensors": {
"Hcsr04sDescriptions": [
{
"UniqueId": "com.efhilton.elliebot.id.sensors.front.range",
"TriggerPinNumber": 4,
"EchoPinNumber": 17
}
],
"InputPinTriggerDescriptions": [
{
"UniqueId": "com.efhilton.elliebot.id.sensors.front.right.collision",
"InputPinNumber": 27
},
{
"UniqueId": "com.efhilton.elliebot.id.sensors.front.left.collision",
"InputPinNumber": 22
}
]
}
}
Here, the settings are described in the following sections.
The following influence general behavior of the robot.
Setting | Example | Description |
---|---|---|
DebuggingLevel |
INFO |
Sets the debugging level to one of FINEST, FINE, DEBUG, INFO, WARN, ERROR. Messages are reported via the Logging MQTT topic. |
The following influence general settings for the MQTT connection.
Setting | Example | Description |
---|---|---|
MqttConnectionDescription.Port |
1883 |
The port via which to connect to the MQTT broker |
MqttConnectionDescription.Host |
localhost |
The server hosting the MQTT broker (it could be local or remote) |
MqttConnectionDescription.TopicForCommands |
com.efhilton.elliebot.topic.commands |
The MQTT topic via which commands will be received. Any device/module can add/listen to commands in this topic. |
MqttConnectionDescription.TopicForLogging |
com.efhilton.elliebot.topic.logs |
The MQTT topic via which commands will be received. Any device and/or module can add/listen to events in this topic. |
MqttConnectionDescription.TopicForSensorData |
com.efhilton.elliebot.topic.sensors |
The MQTT topic via which sensor data will be transmitted. Any device and/or module can add/listen to data in this topic. |
The following are used to configure the general drive train characteristics of this robot to provide context needed by the Forward, Backward, CCW, CW, Stop, and Tank commands. Specifically, a left motor and a right motor must be associated with a unique ID, which shall be defined later in the Actuators section.
Setting | Example | Description |
---|---|---|
DriveTrainDescription.LeftMotorUniqueId |
com.efhilton.elliebot.id.motors.left |
Declares the left motor id. This id should be defined in the Actuator section. |
DriveTrainDescription.RightMotorUniqueId |
com.efhilton.elliebot.id.motors.right |
Declares the right motor id. This id should be defined in the Actuator section. |
The following describes actuators configurations. Actuators are any lights, motors, etc. Any number of them can be defined. Actuators are generally accessible via the Commands MQTT topic.
Setting | Example | Description |
---|---|---|
Actuators.HBridgeMotorDescriptions.UniqueId |
com.efhilton.elliebot.id.motors.left |
HBridge motor unique identifier for a given motor. This motor is controlled via a simple HBridge, which uses a forward pin, and a backward pin. In this configuration, no Enable pin is used, yet the program will simulate a PWM signal to control the pins as necessary. |
Actuators.HBridgeMotorDescriptions.ForwardPinNumber |
8 | The GPIO pin which will drive this motor forward. |
Actuators.HBridgeMotorDescriptions.BackwardPinNumber |
7 | The GPIO pin which will drive the motor Backward. |
OutputPinDescriptions.UniqueId |
com.efhilton.elliebot.id.leds.lightyellow |
The unique identifier for an output pin |
OutputPinDescriptions.OutputPinNumber |
18 | The GPIO output pin. |
Registered sensors will be monitored and their value reported via the Sensor Data MQTT topic.
Setting | Example | Description |
---|---|---|
Sensors.Hcsr04sDescriptions.UniqueId |
com.efhilton.elliebot.id.sensors.front.range |
The unique identifier for a HCSR04s Ultrasonic Range sensor |
Sensors.Hcsr04sDescriptions.TriggerPinNumber |
4 | The GPIO pin which is used to trigger the range finding in the HCSR04s sensor. |
Sensors.Hcsr04sDescriptions.EchoPinNumber |
17 | The GPIO pin that receives the return pulse from the HCSR04s sensor. |
Sensors.InputPinTriggerDescriptions.UniqueId |
com.efhilton.elliebot.id.sensors.front.right.collision |
The Unique Identifier for an input pin trigger. |
Sensors.InputPinTriggerDescriptions.InputPinNumber |
27 | The GPIO pin which receives the change in value (high or low) |
⚠️ WARNING: For simplicity, and with the assumption that this code is running inside a controlled network, all security mechanisms have been disabled. You have been warned!
⚠️ WARNING Please make sure that the settings match your hardware. Failure to do so could damage your robot!
Once you've configured your robot as described above, you are ready to run the code.
To do so, simply type:
~/EllieBot/EllieBot
Your robot is now ready to receive commands.
This section describes the syntax needed to command your robot.
Commands are sent as json packets over the MQTT Commands topic. They have the syntax as follows:
{
"Command":"<some command>",
"Arguments": [
"<arg1>", ... , "<argN>"
]
}
For example, to drive your robot like a tank, simply set the forward and ccw effort by using the com.efhilton.elliebot.cmd.go.tank
command as follows:
{
"Command":"com.efhilton.elliebot.cmd.go.tank",
"Arguments": [
"0.1","-0.5"
]
}
Here, the end effect is that the tank will advance forward at a very slow speed while turning clockwise (roughly speaking, the left motor will operate at 60% duty cycle, going forward, while the right motor operates at 40% duty cycle, going backward).
The following commands are recognized by the robot.
Command | Arguments | Example | Description |
---|---|---|---|
com.efhilton.elliebot.cmd.go.tank |
<forward_effort>,<ccw_effort> |
{"Command":"com.efhilton.elliebot.cmd.go.tank", "Arguments": ["0.5","-0.5"]} |
Sets the forward speed of the robot as well as the rate of rotation. For example, setting the <forward_effort> to zero, and the <ccw_effort> to one will cause the robot to spin counterclockwise about its vertical axis only. Acceptable values range from [-1, 1] for both arguments. |
com.efhilton.elliebot.cmd.go.back |
<abs_dutycycle> |
{"Command":"com.efhilton.elliebot.cmd.go.back", "Arguments": ["0.5"]} |
Sets the backward speed to the specified absolute duty cycle. The <abs_dutycycle> argument will dictate how much effort to use on this command. Valid arguments are in the range of [0,1] . |
com.efhilton.elliebot.cmd.go.forward |
<abs_dutycycle> |
{"Command":"com.efhilton.elliebot.cmd.go.forward", "Arguments": ["0.5"]} |
Sets the forward speed to the specified absolute duty cycle. The <abs_dutycycle> argument will dictate how much effort to use on this command. Valid arguments are in the range of [0,1] . |
com.efhilton.elliebot.cmd.go.ccw |
<abs_dutycycle> |
{"Command":"com.efhilton.elliebot.cmd.go.ccw", "Arguments": ["0.5"]} |
Sets the counterclockwise speed to the specified absolute duty cycle. The <abs_dutycycle> argument will dictate how much effort to use on this command. Valid arguments are in the range of [0,1] . |
com.efhilton.elliebot.cmd.go.cw |
<abs_dutycycle> |
{"Command":"com.efhilton.elliebot.cmd.go.cw", "Arguments": ["0.5"]} |
Sets the clockwise speed to the specified absolute duty cycle. The <abs_dutycycle> argument will dictate how much effort to use on this command. Valid arguments are in the range of [0,1] . |
com.efhilton.elliebot.cmd.go.stop |
none | {"Command":"com.efhilton.elliebot.cmd.go.stop", "Arguments": []} |
Stops all motion of the robot. |
com.efhilton.elliebot.cmd.led.on |
{"Command":"com.efhilton.elliebot.cmd.led.on", "Arguments": ["com.efhilton.elliebot.id.leds.lightyellow" ]} |
Turns on the pin identified by the unique identifier. In the case of this example, this turns on the yellow light identified by com.efhilton.elliebot.id.leds.lightyellow . |
|
com.efhilton.elliebot.cmd.led.off |
{"Command":"com.efhilton.elliebot.cmd.led.off", "Arguments": ["com.efhilton.elliebot.id.leds.lightyellow" ]} |
Turns off the pin identified by the unique identifier. In the case of this example, this turns off the yellow light identified by com.efhilton.elliebot.id.leds.lightyellow . |
|
com.efhilton.elliebot.cmd.pwm.set |
{"Command":"com.efhilton.elliebot.cmd.pwm.set", "Arguments": ["com.efhilton.elliebot.id.motors.left", "0.4" ]} |
Sets the PWM duty cycle for the identified HBridge Motor. In the case of this example, this sets the duty cycle for the left motor identified by com.efhilton.elliebot.id.motors.left . |
Sensor data is transmitted as a JSON packet over the Sensor Data MQTT topic. The packet looks as follows:
{
"UniqueId":"<unique_id>",
"Data":["<datum_1>",...,"<datum_N>"]
}
Example:
{
"UniqueId":"my.temperature.sensor",
"Data":["23.0"]
}
Here:
UniqueId
is a unique identifier for the sensor, in this casemy.temperature.sensor
Data
is an array of data received from that sensor. For example, a temperature sensor would return one datum, whereas an array of temperature sensors would return an appropriately sized array. In this case, this sensor is reporting a 23 degree celsius teperature.
It is important to note that any device/module can generate and/or consume sensor data.
Logging messages are transmitted as JSON packets over the Logs MQTT topic. The packet looks as follows:
{
"Level":"<log_level_as_int>",
"LevelStr": "<log_level_as_human_readable_str>",
"Message": "<log_message>"
}
For example:
{
"Level":"2",
"LevelStr": "DEBUG",
"Message": "Battery level is now at 50%"
}
Here:
Level
: A number ranging from 0-5, representing a FINEST, FINE, DEBUG, INFO, WARN, ERROR, respectively.LevelStr
: A human readable level string, for example "FINEST", "FINE", "DEBUG", "INFO", "WARN", and "ERROR".Message
: The event message. For example "Battery level is low".
It is important to note that any device/module can generate and/or consume logging information.
Client Code, MIT App Inventor
A quick way to test out your robot is to create a simple application in MIT App Inventor.
Here, I created a simple joystick application which makes use of the com.efhilton.elliebot.cmd.go.tank
command. I had a lot of fun driving this robot all around the house, using my phone. I drove it around all forms of obstacles, and timed myself as a personal challenge.
The MIT App Inventor code can be found here. The Design screen implements a simple joystick control as follows: .
I used:
- the built in "Ball" widget, used that as the joystick's head. You drag the ball across the screen, and its relative position from center is converted into control signals for the robot.
- a text box to enter the IP address of the robot
- a checkbox to connect and disconnect from the robot
- two sliders, which I use to control the horizontal and vertical sensitivity of the joystick
- the
UrsPahoClient
, which is a fantastic little MQTT client plugin that you can download from the Ullis Roboter Seite. - a log window which listens to the logs topic.
- a sensor display widget
- a switch to turn a light on and off.
The blocks for this project are as follows .
This project is governed by the MIT License. All rights reserved.
The main author for this project is Edgar Hilton, [email protected].
Please don't hesitate to reach out to me, [email protected] if you have any questions, comments, or ideas on how to enhance this project.
If you find this code useful, and if you either want to feed my growing love of Raspberry Pi hardware, or if you want to buy me a cup of coffee, then consider making a Paypal donation to my Paypal.
Copyright 2021 Edgar Hilton
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
In other words, use this software at your own risk.
I am not affiliated in any way with The Pi Hut, its affiliates, or affilliates of ITS affiliates.