Control Simple Robot Over MQTT

59931

Intro: Control Simple Robot Over MQTT

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

STEP 1: The Chassis

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.

STEP 2: The Joystick/Controller

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.

STEP 3: Theory of Operation

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.)


STEP 4: Installing the ESP32 Core to the Arduino IDE

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.

STEP 5: Bonus Video

Comments