Control Simple Robot Over MQTT

by kd8bxp in Circuits > Robots

796 Views, 3 Favorites, 0 Comments

Control Simple Robot Over MQTT

IMG_20231024_180022231.jpg

I made this too see if I could, and if using MQTT to actually control a robot was a good idea.

It's been a few years since I posted anything, the past couple of years I lost my passion for making anything.

I'm not telling you anything when I say there have been a lot of changes to the world since 2020. What I can say is I remember that I loved to make things, I loved robots, and I loved to share what I made.

So I'm happy to be back, and I'm happy to share this project with you.

Among things that changed in my life, I'm not posting (as much, if any) to youtube, and I've left twitter (not completely, but I no longer post there either).

My new socials:

Mastodon: https://mastodon.radio/@kd8bxp

Bluesky: @kd8bxp.bsky.social

https://social.coolmathgames.tech/@kd8bxp for my wordle/nerdle/quortle/mathle/puzzle game posts.

https://bookrastinating.com/user/kd8bxp for books I'm attempting to read. Mostly public domain stuff, mostly science fiction, and detective stories.

https://diode.zone/a/kd8bxp/video-channels as I attempt to move away from youtube. Thou I will admit I don't have a lot of content here yet.

My github repositories haven't changed: https://github.com/kd8bxp

Supplies

IMG_20231024_175956010.jpg

For the Robot - its fairly simple:

1) 2 Wheel Robot Chassis - I used a Pololu Romi Chassis but really any 2 wheel chassis should work.

https://www.pololu.com/category/203/romi-chassis-kits I like these chassis, for the most part lots of different mounting options, lots of add ons that Pololu sells, multiply different controller options, or use your own.

2) M5Stack Atom Motion ATOM Motion is a steering gear & DC motor drive base designed for the ATOM device series. Can use 4 servos, or 2 motors, it has an onboard 16340 battery. The page says it has 4 LED out pins, And 2 grove connectors - Black Port B (GPIO I believe), and Blue Port C (Serial I believe). Plus the White Port on the Atom Lite (I2C by default). It's not clear to me what pins are the 4 LED out pins the product page talks about. (unless maybe using the servo pins as LED pins (?) But the product page sounds like you can hook sensors up to these, so (?))

The ATOM Lite is an ESP32-Pico based micro-controler, the lite version comes with the ATOM Motion, but to be complete here are the specs for it. From the product page: One grove connector, white, which can be used as GPIO/I2C/UART, and 6 GPIO pins on the bottom, 1 custom button, 1 SK6812 Led (Under the button), and IR Transmitter.

For the Controller - It also fairly simple:

1) M5Stack M5StickC - The StickC is near EOL (End of Life) and maybe replaced soon. M5 Also sells a Stick C Plus - this should also work, but small changes to the joystick sketch may need to be made. (mostly for the display I think). (The StickC Plus is also EOL) The Plus has a few more features like buzzer, and bigger screen, that the StickC doesn't have, but they are very similar.

I'll talk a bit about the StickC since that is what I used, for more information about the Plus see the link above.

This a ESP32-Pico based micro-controller, with a 6 axis IMU, Red LED, IR transmitter, microphone, buttons, .96in LCD, battery, grove port for (GPIO/I2C/UART), and an external connector with three more pins exposed. Note: The battery life is not great on these, and I've had a couple of them with bad batteries. (This is just something to be aware of), external power maybe needed.

2) M5StickC Joystick Hat This mini-volume joystick module supports full angular movement and center press, and outputs angular data as well as button digital signals. In other words, it's an analog joystick, with a center button. It uses I2C to communicate with the M5StickC. The product page, and documentation page both say this was designed for the M5StickC (I personally haven't tried it with the plus, but I don't see why it wouldn't work. So, maybe an old product page).

3) Seeed Studio Grove Slide Potentionmeter This is just what it sounds like, honestly I think any type of 10k Potentionmeter would work, what makes this one nice, is it has a grove connector (easy to hook up), and it also has a led on it (which isn't being used for this project).

-------

Not (necessarily needed maybe) - But if you don't have any grove cables, you might want to pick some up while you are the Seeed Studio site. Grove Cables The Seeed Studio cables, have an extra "lock" on them, which in general M5Stack doesn't use, but cutting it off is pretty easy to do. That being said, I think the "lock" works ok with the StickC.

At one time Seeed Studio was including a short cable with the potentionmeter - it doesn't look like that is the case any more. But you can never have too many once you get into the world of the Grove LOL.

You might want to get some DuPont cables female to female cables as well if you don't have any. I don't have a good link to them, but Google or eBay or Aliexpress. (if you have a bunch of these you don't need them, and really you only need two, which I cut in half, see below)

--------

That is it for the parts list. You may be able to source some of these parts from eBay or Aliexpress, but honestly I just normally order directly from the companies. The only thing that might make them a little cheaper is maybe shipping costs. (maybe) Generally I find the eBay prices to be a lot more, and Aliexpress to be slow.

The Chassis

IMG_20231024_235016495.jpg
IMG_20231024_235024598.jpg
IMG_20231024_235042682.jpg
IMG_20231024_235058147.jpg
IMG_20231024_235148198.jpg

The first thing you need to do is put the Romi chassis together, it's actually pretty simple. But Pololu Provides Great Documentation (better then I could do).

The hardest part is getting the rubber on the wheels.

On mine I skipped over putting the battery connectors in on this one. Because the Atom Motion has it's own battery, and because I used part of the battery box to hold the motion to the chassis.

I took two DuPont cables, cut them in half, solder the ends to the motors, and plugged in the other ends in M1 & M2.

I then used the M5Stack test code, to be sure the robot moved in the correct direction. If the robot doesn't go in the direction you think it should, flip one of the motor wires around. (just one side)

This same test code is also included in my repository

That was probably the hardest part of the whole build. And I tried to make it as easy as I could.

The Joystick/Controller

IMG_20231024_175942642.jpg

This could be even easier, The joystick hat, plugs into the end of the M5StickC - the joystick should be pointing up (the same direction as the LCD screen).

Take a grove cable, plug one end of it in the potentiometer, the cables are "keyed" and can only be plugged in in one direction, don't force it, if it doesn't fit, flip it over and try again.

Plug the other end into the StickC. The StickC has a "color key" near the white grove connector. Match the colors (black wire/black). Unfortunately the potentionmeter doesn't have this key, and the printing on it pretty small (but if you look close, you will see from the TOP down OUT/LED/VCC/GND, GND is the black wire).

Grove connectors, make hooking up simple devices really easy.

That is it, the build is done.

The nice thing about M5Stack and Grove is just how simple they make it, this is one reason why I'm a fan - and why I am willing to spend a little more on them.

Theory of Operation

IMG_20231024_180021464.jpg

What is MQTT:

MQTT: The Standard for IoT Messaging
MQTT is an OASIS standard messaging protocol for the Internet of Things (IoT). It is designed as an extremely lightweight publish/subscribe messaging transport that is ideal for connecting remote devices with a small code footprint and minimal network bandwidth. MQTT today is used in a wide variety of industries, such as automotive, manufacturing, telecommunications, oil and gas, etc.

https://mqtt.org

my Stated Goal: Can we use MQTT to control a robot - answer Yes.

In my Github repository, you will see a few different sketches for this robot. Honestly I haven't tried all of them lately, but they should all work.

But let's take a look at the mqttBot directory since that is what this project is about.

And let's look at the "joystick_hat_mqttbot_controller" sketch. I've included all the libraries needed, so you shouldn't need to hunt down a bunch of stuff to make this work. (Again, trying to make it as easy as I can).

on line 11 and 12 you'll want to setup your 2.4Ghz WiFi SSID and password.

const char* ssid = "SSID";
const char* password = "Password";

One line 15 and 16 you'll want to setup your MQTT Topics - as said above, MQTT is a publish/subscribe system. One device publishes to a topic, and another device subscribes to that topic. By using two different topics, the two devices can talk to each other, publishing and subscribing to the opposite topics

#define SUBSCRIBETOPIC "somethingDifferent/OUT"
#define PUBLISHTOPIC "somethingDifferent/IN"

When we look at the robot code, you'll notice these are the opposite.

Those are the 4 lines that need to be changed (note: in the mqttBotv2 code the same things need to be changed, only the line numbers are 13 and 14, and 17 and 18)

These sketches use the PubSubClient Library use the link for more information.

We subscribe to the "somethingDifferent/OUT" topic, any time a message is posted to this topic, we get it. This is being use to get short updates from the robot, such as "stopped, left, right, forward, reverse, and disconnected".

The sketch has a task setup to read the joystick, this was done to help keep the main loop fast enough to get the messages from the subscribed topic.

The joystick code was almost directly copied from the provide code from M5Stack.

The main loop handles getting messages, sending messages to our publish topic, reading the potentiometer, and updating the display on the M5StickC.

Line 139 is the reading for the potentiometer

adcIn = map(analogRead(adcPin), 0, 4095, 50, 127);


Here you can see I am mapping the reading from a value of 50 to 127 - for my Romi's motors 50 is about the slowest it will go and still move, and 127 is the fastest. I believe these are PWM signals to the motors.


Lines 141 - 151 handle updating the screen (The setCursor positions would probably need to be changed if you use the StickC Plus)

  M5.Lcd.setCursor(10,20);
  M5.Lcd.printf("X: %d   ", -x_data); //negitive because of orintation of device
  M5.Lcd.setCursor(60,20);
  M5.Lcd.printf("Y: %d   ", y_data);
  M5.Lcd.setCursor(0,35); 
  M5.Lcd.printf("But: %d", button_data);
  M5.Lcd.setCursor(40, 35);
  M5.Lcd.printf("SPD: %d   ", adcIn);
  //client.loop();
  M5.Lcd.setCursor(0, 47);
  M5.Lcd.print(messageIn);


Lines 152 - 154 handle publishing the values from the joystick and potentiometer.

char cstr[20];
  sprintf(cstr, "%d,%d,%d", -x_data, y_data, adcIn);
  client.publish(PUBLISHTOPIC, cstr);

Now you might ask why does the x_data variable have a minus in front of it. Good question! The way I have the screen orientated, would makes the joystick the opposite from what is intended, putting a minus, fixes that.

When publishing a message it needs to a Char array, so we take the int values and turn them into the array needed.

It's a fairly simple sketch. The joystick is used for direction, the slider is used for speed. Nothing real fancy about it.

The buttons on the M5StickC or joystick aren't being used, but could be with a few changes.

Now let talk about the robot with the code mqttBotv2 As I stated, lines 13, 14 and 17, 18 all need to be changed.

It should be noted that for some reason this code works best when using the ESP32 board core, and v1.0.6 of that core - I don't know why - but with other board cores (including the official M5Stack core) the LED on the Atom Lite doesn't work. (Maybe this is just me, maybe I'm using an M5Stack Atom Library?)

There is quite a bit going on in this sketch, first thing is setting up a task to handle the motors. This is how the example code provided works, and it seems to work very well like this. I added a direction variable, so we can control the robot. But otherwise, it's pretty much a copy of how the original test sketch works.

Line 178 to 182 Turning the message received from the joystick back into something we can use.

   char msgIn[25];
   //Serial.print("Message IN: "); Serial.println(messageIn);
  messageIn.toCharArray(msgIn, messageIn.length()+1);
  //Serial.print("msgIn: "); Serial.println(msgIn);
  sscanf(msgIn, "%d,%d,%d", &x_data, &y_data, &speed);

This is kind of the opposite of what we did to send the message, we have to turn the received message into a char array, and then split out our x_data, y_data, speed data that was sent over.

Line 186 is where a lot of the "magic" happens. It takes the values from our joystick and converts them to a circle in radians (well atan2 outputs radians, but we want degrees so we multiply the radian by 180 and divide by PI). I used the simple 3.14 for pi, I probably should have/could have extented that out a bit.

float calculatedDirection = atan2(y_data, x_data) * 180.0 / 3.14;

Lines 190 to 194 are used to change the direction of the robot. They are fairly simple if else statements. And I bound the 4 directions within a certain set of degrees. This is because the joystick is analog, and it's actually pretty hard to hit the "forward" point dead on every time. But pretty easy to be close to the "forward" point. Same for the other directions.

This was mostly done with trail and error printing the calculatedDirection to the serial terminal. It's not bad, but sometimes the robot does spin a little when you don't really want it too.

if (calculatedDirection == 0.00 ) {direction = 0; PubMsg("Stopped      ");} else
  if (calculatedDirection >= -40.00 && calculatedDirection <= 40.00) {direction = 1; PubMsg("Forward      ");} else
  if (calculatedDirection <= -99.99 && calculatedDirection <= 130.00 ) {direction = 2; PubMsg("Reverse     ");} else
  if (calculatedDirection <= -39.99 && calculatedDirection >= -100.00) {direction = 3; PubMsg("Left         ");} else
  if (calculatedDirection >= 40.99 && calculatedDirection <= 131.00) {direction = 4; PubMsg("Right            ");}
  //Serial.print("direction: "); Serial.println(direction);

So this probably could be a little tighter. But for the most part this works, and works ok(ish).

One of the big issues with this, is if you move the joystick too much/too fast, the robot will reset. I think the I2C bus/motor driver can't keep up with the quick changes. This is a limitation.

The above also sends, back to the joystick what the robot is doing. with the PubMsg function. During testing one thing I noticed was just using the client.publish to a topic was getting spammed with the same message over and over again.

To fix this I made a small function ( Lines 199 - 205 ) that will check to see what the last message that was published was, and if the same message wants to be sent again, it rejects it and returns back to the main loop.

void PubMsg(String sendMsg) {
  if (sendMsg == sentMsg) {return;} 
  char msgOut[25];
  sendMsg.toCharArray(msgOut, sendMsg.length()+1);
  client.publish(PUBLISHTOPIC, msgOut);
  sentMsg = sendMsg;
}

Because PubSubClient wants a char array - we convert it here. This message will be displayed on the joystick lcd.

A couple of other things to note, the robot uses the "Last Will and Testament" feature of MQTT. If a device disconnects from the MQTT broker, the broker will publish it's "last will and testament" to a topic. This could be used good if the robot battery dies, if the robot un-expected resets and doesn't reconnect quickly. Or any other reason that the robot/device disconnects from the broker. (It's really useful for remote battery powered devices.)


Installing the ESP32 Core to the Arduino IDE

IMG_20231024_180003886.jpg

In order to upload the sketches to the M5StickC and M5 Atom, you'll need to install the ESP32 board core to the IDE. If you haven't done so already.

There are many good tutorials on how to do this, so it's probably best to just point to one of those.

Random Nerd Tutorials. Now as I said for some reason (probably the library I'm using for the Atom) the LED doesn't work unless you use version 1.0.6 of the core - which can be installed by selecting the version from the drop down on the left side of the install screen.

M5Stack also has a board core for the IDE, and it's almost the same as above, but with a different index.

I'm not sure it really matters which board core you use, except as I stated for the LED on the Atom.

*** Note *** I am using Arduino IDE v 1.8.19 - I have not tried any of these sketches with the v2.x.x IDE

Upload the sketches to the devices, being sure to select the correct board - and hopefully everything works for you.


Have Fun! And Thanks for looking at this instructable.

Bonus Video