HELP with GPS and MQTT

Hi everyone, I am fairly new to Arduino so sorry if I seem like I don't know what I'm doing. I am creating a system with two arduino Rev2 wifi boards, one has a GPS module attached to it, and the other has LED lights. In the first board, the code uses a formula to calculate speed using the latitude and longtitude coordinates. It sends the speed over MQTT as a topic to the second board, which should then turn on lights in a certain sequence based on the speed of the GPS (it is meant to simulate a traffic light). The GPS works and is able to send the coordinates, and the second arudino is able to receive and print them. My problem is with turning on the lights. I'm not sure how to turn the topic into a float and then regulate the lights based on it, since what I have now isn't working. I think that it has something to do with the fact that the values are constantly updating so it doesn't have time to turn on the lights. Does anybody have any advice? I have included the code for the MQTT subscriber (board with the lights) below.

#include <ArduinoMqttClient.h>
#include <WiFiNINA.h>
#include "arduino_secrets.h"
#include <arduino.h>

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID
char pass[] = SECRET_PASS;    // your network password

// Traffic light pins
int redCAR = 2;
int yellowCAR = 4;
int greenCAR = 6;
int redPED = 8;
int greenPED = 10;

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

const char broker[] = "test.mosquitto.org";
int        port     = 1883;
const char topic[]  = "latitude";
const char topic2[]  = "longtitude";
const char topic3[] = "speed";
float speeed = 0;

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);

  pinMode(redCAR, OUTPUT);
  pinMode(yellowCAR, OUTPUT);
  pinMode(greenCAR, OUTPUT);
  pinMode(redPED, OUTPUT);
  pinMode(greenPED, OUTPUT);

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  // attempt to connect to Wifi network:
  Serial.print("Attempting to connect to SSID: ");
  Serial.println(ssid);
  while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
    // failed, retry
    Serial.print(".");
    delay(5000);
  }

  Serial.println("You're connected to the network");
  Serial.println();

  Serial.print("Attempting to connect to the MQTT broker: ");
  Serial.println(broker);

  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());

    while (1);
  }

  Serial.println("You're connected to the MQTT broker!");
  Serial.println();

  // set the message receive callback
  mqttClient.onMessage(onMqttMessage);

  Serial.print("Subscribing to topic: ");
  Serial.println(topic);
  Serial.println();

  // subscribe to a topic
  mqttClient.subscribe(topic);
  mqttClient.subscribe(topic2);
  mqttClient.subscribe(topic3);

  // topics can be unsubscribed using:
  // mqttClient.unsubscribe(topic);

  Serial.print("latitude: ");
  Serial.println(topic);
  Serial.print("longtitude: ");
  Serial.println(topic2);
  Serial.print("speed: ");
  Serial.println(topic3);

  Serial.println();
}

void loop() {
  // call poll() regularly to allow the library to receive MQTT messages and
  // send MQTT keep alive which avoids being disconnected by the broker
  mqttClient.poll();
  delay (500);
 //  if (speeed > 6.7056) {//(15 mph in meters/second)
//    greenPED = HIGH;
//    redPED = LOW;
 //   redCAR = HIGH;
//    yellowCAR = LOW;
 //   greenCAR = LOW;
//    delay (6000);
 // }
 // else if (speeed < 6.7056) {
 //   greenPED = LOW;
 //   redPED = HIGH;
 //   redCAR = LOW;
//    yellowCAR = LOW;
//    greenCAR = HIGH;
//    delay (4000);
//    greenPED = LOW;
 //   redPED = HIGH;
//    redCAR = LOW;
//    yellowCAR = HIGH;
//    greenCAR = LOW;
 //   delay (6000);
 /// }
 // delay (5000);
}

void onMqttMessage(int messageSize) {
  // we received a message, print out the topic and contents
  Serial.println("Received a message with topic '");
  Serial.print(mqttClient.messageTopic());
  Serial.print("', length ");
  Serial.print(messageSize);
  Serial.println(" bytes:");

  double atof(topic3); = speeed 
  //String speeed = String(topic3);
 // float speeeed = atof(speeed.c_str()); // Convert string to float
 // Serial.println(speeeed); // Print the converted float
 /// Serial.print(speeed);
 /// Serial.print("hello");
 /// Serial.print(speeed);


  // use the Stream interface to print the contents
  while (mqttClient.available()) {
    Serial.print((char)mqttClient.read());
  }
  Serial.println();
  Serial.println();
}

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the < CODE/ > icon above the compose window) to make it easier to read and copy for examination

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

Its kind of weird that the library has methods to return the message size and topic but not the message content itself but instead resorts to using available();. I tend to use the pubSubClient library myself. I think what is needed is to first read the complete message into a buffer and then convert. You should probably remove:

double atof(topic3); = speeed 

and do something like this:

void onMqttMessage(int messageSize) {
  // we received a message, print out the topic and contents
  Serial.println("Received a message with topic '");
  Serial.print(mqttClient.messageTopic());
  Serial.print("', length ");
  Serial.print(messageSize);
  Serial.println(" bytes:");

  // Create a buffer of messageSize + 1 (allow for message + null byte at the end)
  char msgbuf[messageSize + 1];

  // Initialize the buffer to null
  memset(msgbuf, '\0', messageSize+1);

  // Read in the message
  for (size_t i=0; i<messageSize+1; i++){
    msgbuf[i] = mqttClient.read();
  }

  // Print the message if you like
  Serial.println(msgbuf);

  // Convert the message to a value
  speeed = atof(msgbuf);

 
  Serial.println();
  Serial.println();
}

You could use the while loop as per the original example, but then you would need to implement a counter to make sure that the buffer is not inadvertently overrun. By using a for loop, we read in the exact number of characters that we are expecting and once messageSize characters have been read, we then convert the buffer to a float value. If no conversion can be formed (message does not represent a valid value) it will return zero.

Another approach might be to define the buffer as a string type e.g. String msgStr = ""; . The memset line is then not required. Each time around the loop just push the character into the string with msgStr += mqttClient.read()); and at the end of the loop convert it with speeed = msgStr.toFloat();. Not everyone likes using type String though and a char array is more efficient. The choice is yours.

Other may have different approaches.

1 Like

With that library, the MqttClient class inherits from Client:
class MqttClient : public Client {
And Client inherits from Stream (at least on AVR boards):
class Client : public Stream {

So, you can use readBytes() to read the entire buffer at once:
mqttClient.readBytes(msgbuf, messageSize);

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.