Setting global variable not available outside function

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! :stuck_out_tongue:

Is there any other way that I can safely return the data without getting into the EEPROM? Thanks!

I can't see why you would need EEPROM for this.

Please post the fixed code you're using.

Can you test with strcpy instead of strcat?

Edit: Damnit I was way too slow. And wildbill also added more info as to why strcat wasn't working :frowning:

@ tstanescu:

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?

EEPROM.. out of desperation! Fixed code?

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

Sorry, can you please explain a bit further?

Ah no I think I might have been wrong it's 1 AM here. Strcat might actually override the null terminator before adding a new one /0

1 Like

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

Cut it down to something a bit smaller and serial.print some more debug.

I'm not clear now what's wrong, but I suspect it's some minor string handling issue.

1 Like

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 :slight_smile:

1 Like

Your buffers are large,
Try using strlcpy / strlcat / snprintf that protect against buffer overflow.
see strlcpy_strlcat.ino
and Rules explorer for snprintf

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
1 Like