Hi everyone, I've put together some simple code that displays sensor data on a MAX7219 LED Matrix . The data is received through MQTT. I have been trying to save my variables using strcpy, but the data doesn't seem to be accessible in the displayMessage() or anywhere else outside the method. I've been on this for a few days and I thought my last resort was to ask for help before giving up and saving the values to EEPROM! Thanks in advance for any help!
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include "EspMQTTClient.h"
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CLK_PIN 14
#define DATA_PIN 13
#define CS_PIN 15
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
uint8_t scrollSpeed = 50; // default frame delay value
uint16_t scrollPause = 2000; // in milliseconds
// Global message buffers shared by Serial and Scrolling functions
#define BUF_SIZE 255
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "" };
bool newMessageAvailable = false;
char tempMessage[BUF_SIZE] = { "" };
char humidityMessage[BUF_SIZE] = { "" };
char dateMessage[BUF_SIZE] = { "" };
//char noiseMessage[BUF_SIZE] = { "" };
//char pm25Message[BUF_SIZE] = { "" };
EspMQTTClient client(
"Living_Room",
"12345678",
"broker.mqttdashboard.com", // MQTT Broker server ip
//"MQTTUsername", // Can be omitted if not needed
//"MQTTPassword", // Can be omitted if not needed
"TestClient123a2", // Client name that uniquely identify your device
1883 // The MQTT port, default to 1883. this line can be omitted
);
void setup()
{
curMessage[0] = newMessage[0] = '\0';
Serial.begin(115200);
P.begin();
P.displayText("Waiting for MQTT data...", PA_LEFT, scrollSpeed, scrollPause, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
void onTempMessageReceived(const String & payload) {
Serial.print("message received from temp: " + payload); //This works perfectly.
strcpy(tempMessage, payload.c_str());
sprintf(tempMessage, "%s\xB0""C - ", payload.c_str());
Serial.print(tempMessage); //Also works
strcat(newMessage, tempMessage); //Add the sensor value to the message that will be displayed
}
void onHumidityMessageReceived(const String & payload) {
//Serial.print("message received from humidity: " + payload);
strcpy(humidityMessage, payload.c_str());
sprintf(humidityMessage, "%s\xB0""C - ", payload.c_str());
strcat(newMessage, humidityMessage);
}
void onDateMessageReceived(const String & payload) {
//Serial.print("message received from date: " + payload);
strcpy(dateMessage, payload.c_str()); //copy payload to curMessage
sprintf(dateMessage, "%s\xB0""C - ", payload.c_str());
strcat(newMessage, dateMessage);
}
void onConnectionEstablished() //Called by EspMQTTClient.h
{
client.subscribe("tiberius/temp", onTempMessageReceived);
client.subscribe("tiberius/humidity", onHumidityMessageReceived);
client.subscribe("tiberius/date", onDateMessageReceived);
strcpy(curMessage, newMessage); //copy payload to curMessage to avoid duplicates
newMessage[0] = '\0'; //clearing newMessage
newMessageAvailable = true;
}
void displayMessage() { //This is where I display the message on the LED Matrix
Serial.println("New data received:"); //Prints normally
Serial.println(newMessage); //Shows blank
Serial.println(curMessage); //Shows blank
Serial.print("Date Message");
Serial.println(dateMessage); //Serial print shows blank
P.displayText(curMessage, PA_LEFT, scrollSpeed, scrollPause, PA_SCROLL_LEFT); //Doesn't display anything since curMessage is blank
newMessageAvailable = false;
}
void scrollText(void)
{
if (P.displayAnimate())
{
if (newMessageAvailable)
{
displayMessage();
}
P.displayReset();
}
}
void loop()
{
client.loop();
scrollText();
}
I am not sure what is going on - I suggest you write a simpler example to be sure that the MQTT stuff is functional.
However, I note that you are using strcat repeatedly on newMessage and never clean it up, so it will inevitably overflow overwriting other variables, so your outcomes are going to be unexpected at best.
Thanks for your reply Bill, I have modifed the code so that newMessage gets cleared after storing it.
If I modify strcpy(curMessage, newMessage); to strcpy(curMessage, "testing"); whenever I get an MQTT message, it gets properly displayed. If I modify it to strcpy(curMessage, tempMessage); whenever I get an MQTT message, it shows a blank message, just like with newMessage - but then where did the data that I saved in onTempMessageReceived go? . It seems the issue is within the onTempMessageReceived, onHumidityMessageReceived, and onDateMessageReceived functions, because I can access those variables without any issues... I am really curious on why this is happening because it smells like a learning experience!
Is there any other way that I can safely return the data without getting into the EEPROM? Thanks!
All of your questions and answers mention strcpy, but you are using strcat. Bill also explained what was happening with the overflow due to the strcat, could you comment on that please?
In the function onTempMessageReceived the message received MQTT function seems to ignore any global variable setting outside it's scope.. strcpy(tempMessage, payload.c_str());
doesn't seem to be saving the variable, I have also tried to replace the payload with text and the issue persists...
Hey byte! So the program logic is to concat various mqtt messages into a bigger string that gets displayed on the led matrix, but the strcat isn't needed in the example below, which is the most simplified version of the code after setup() - but it seems that now I can't even get the displayMessage function to work
void onTempMessageReceived(const String & payload) {
Serial.print("message received from temp: " + payload); //This works perfectly.
strcpy(tempMessage, payload.c_str());
Serial.print(tempMessage); //Updated tempMessage properly
}
void onConnectionEstablished() //Called by EspMQTTClient.h
{
client.subscribe("tiberius/temp", onTempMessageReceived);
strcpy(curMessage, tempMessage); //copy payload to curMessage to avoid duplicates
newMessage[0] = '\0'; //clearing new message
newMessageAvailable = true;
}
void displayMessage() { //This is where I display the message on the LED Matrix
Serial.println("New data received:"); //Doesn't show!?
Serial.println(newMessage); //Shows blank
Serial.println(tempMessage); //Shows blank
P.displayText(curMessage, PA_LEFT, scrollSpeed, scrollPause, PA_SCROLL_LEFT); //Doesn't display anything since curMessage is blank
newMessageAvailable = false;
}
void scrollText(void)
{
if (P.displayAnimate())
{
if (newMessageAvailable)
{
displayMessage();
}
P.displayReset();
}
}
void loop()
{
client.loop();
scrollText();
}
You append to your new message which means you add chars after the null terminator. The null terminator signifies the end of a string. Not sure if that's your issue, but I think it might be
Hehe no worries, yeah the strcat needs to be cleared after the message has been put together, if not it just adds the new message on top of the old one! Appreciate the help
So I figured out why the variables weren't showing. The loop onConnectionEstablished shouldn't have newMessageAvailable = true; rather within the function that gets called. eg onTempMessageReceived. This seems to be related with the MQTT library that I'm using! Thanks everyone for your help.. this was a weird one
If you are writing to an led matrix you might find SafeString library fixed width print useful. (see the Fixed Width Formatting section in the SafeString tutorial)
As well as protecting against buffer overflows it pads to fixed width (not min width like snprintf) and has detailed error messages.
The SafeString print( ) function also has the advantage that is works for floats on UNO (AVR) where as sprintf/snprintf does not.
// download SafeString V4.1.5+ library from the Arduino Library manager or from
// https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
#include "SafeString.h"
void setup() {
Serial.begin(115200); //for debug
for (int i = 10; i > 0; i--) {
delay(500);
Serial.print(i); Serial.print(' ');
}
Serial.println("ESP32 started");
SafeString::setOutput(Serial); // enable error msgs
char c[7];
double d = 17.22;
cSFA(sfC, c); // wrap the char[] in a SafeString for processin
sfC = d; // default 2 dec.
Serial.print(F("d=")); Serial.println(c);
// OR for more control use SafeString.print( )
sfC = ""; //clear it
sfC.print(d, 1); // one dec
Serial.print(F("d=")); Serial.println(c);
sfC = "";
sfC.print(d, 5, 7); // 5decs padded to width 7, width too large for char[ ] <<< Error msg printed here by SafeString.
Serial.print(F("d=")); Serial.println(c);
sfC = "";
sfC.print(d, 5, 5); // 5decs padded to width 5, dec reduced to fit in requested width
Serial.print(F("d=")); Serial.println(c);
}
void loop() {
if (SafeString::errorDetected()) {
Serial.print(F("Error in a SafeString operation"));
}
}
Sample output
d=17.22
d=17.2
Error: sfC.print() needs capacity of 7 too add number formatted to fixed width:7
sfC cap:6 len:0 ''
d=
d=17.22
Error in a SafeString operation