IF and ELSE statement gets reversed after some time ?

I have a problem with the following function:

void receiveDataFromAPI() {

    if (newData == true) {

         Serial.println(receivedChars);
         
         JsonObject& root = jBuffer2.parseObject(receivedChars);
         
         String lightstatus = root[String("lightstatus")];
         String autolight = root[String("autolight")];
         newData = false; 

         float hum = dht.readHumidity();
         int lightlevel = analogRead(pResistor);
    
         if(autolight == "ON"){

              Serial.println("ON"); // test purposes
              digitalWrite(LEDBLUE, HIGH);
              digitalWrite(LEDYELLOW, LOW);
                 
              if (lightlevel < 550){
                    digitalWrite(ledPin, HIGH);         
                }
              else{
                    digitalWrite(ledPin, LOW);
                  }
          
          }
          else{   
                Serial.println("OFF"); // test purposes
                digitalWrite(LEDYELLOW, HIGH);
                digitalWrite(LEDBLUE, LOW); 
                
                  if(lightstatus == "ON"){    
                      digitalWrite(ledPin, HIGH);
                  }     
                  else{
                     digitalWrite(ledPin, LOW);
                  }
            
          }
         
         Serial1.print("<");
         data.printTo(Serial1);
         Serial1.print(">");                   
    }

    delay(10);
    
}

The goal is to turn on the blue LED when the “autolight” variable is “ON”. If the “autolight” variable is “OFF”, the orange LED should turn on.

Everything works just fine when I run the code.

BUT after 2-3 minutes, the IF and ELSE statements just stop working. I do still reveive the value of “autolight”.

I even placed 2 Serial.println’s to test if the function even reaches the IF and ELSE statement. Even though the value of “autolight” was “ON” (100% sure), the code just jumped to the ELSE statement ?

It is almost impossible to provide hep unless you post the complete program

This is the AduinoMega code:

#include <ArduinoJson.h>
#include <SoftwareSerial.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include "DHT.h"

// LCD
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

//Hum and temp sensor
#define DHTPIN 6  
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// Green and red LED
const int LEDRED = 27;
const int LEDGREEN = 25;

// Blue and yellow LED
const int LEDBLUE = 29;
const int LEDYELLOW = 31;


//photoresistor
const int pResistor = A0;
const int ledPin= 23;   
int lightValue;


// get nodemcu data variables
const byte numChars = 110;
char receivedChars[numChars];
boolean newData = false;
 

// json data
DynamicJsonBuffer jBuffer;
DynamicJsonBuffer jBuffer2;
JsonObject& data = jBuffer.createObject();


void setup() {
  
  dht.begin();
  lcd.begin(16, 2);

  // Green and red LED
  pinMode(LEDRED,OUTPUT);
  pinMode(LEDGREEN,OUTPUT);

  // Blue and yellow LED
  pinMode(LEDBLUE,OUTPUT);
  pinMode(LEDYELLOW,OUTPUT);
  pinMode(motorButton, INPUT);

  // Photoresistor
  pinMode(ledPin, OUTPUT);
  pinMode(pResistor, INPUT);


  Serial.begin(9600);
  Serial1.begin(9600);  
}


void sendSensorData(){
  
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    int lightlevel = analogRead(pResistor);
    lcd.setCursor(0,0);
    lcd.print("Temp.: ");
    lcd.print(t);
    lcd.print(" C");
    lcd.setCursor(0,1);
    lcd.print("Humi.: ");
    lcd.print(h);
    lcd.print(" %");
   
    data["id"] = 1;
    data["temp"] = t;
    data["hum"] = h;
    data["lightlevel"] = lightlevel;
   
}





void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void receiveDataFromAPI() {

    if (newData == true) {

         Serial.println(receivedChars);
         
         JsonObject& root = jBuffer2.parseObject(receivedChars);
         
         String lightstatus = root[String("lightstatus")];
         String autolight = root[String("autolight")];
         newData = false; 

         float hum = dht.readHumidity();
         int lightlevel = analogRead(pResistor);
    
         if(autolight == "ON"){

              Serial.println("ON"); // test purposes
              digitalWrite(LEDBLUE, HIGH);
              digitalWrite(LEDYELLOW, LOW);
                 
              if (lightlevel < 550){
                    digitalWrite(ledPin, HIGH);         
                }
              else{
                    digitalWrite(ledPin, LOW);
                  }
          
          }
          else{   
                Serial.println("OFF"); // test purposes
                digitalWrite(LEDYELLOW, HIGH);
                digitalWrite(LEDBLUE, LOW); 
                
                  if(lightstatus == "ON"){    
                      digitalWrite(ledPin, HIGH);
                  }     
                  else{
                     digitalWrite(ledPin, LOW);
                  }
            
          }
         
         Serial1.print("<");
         data.printTo(Serial1);
         Serial1.print(">");                   
    }

    delay(10);
    
}




void loop() {

    recvWithStartEndMarkers();
    receiveDataFromAPI();
 
    sendSensorData();

  
}

It may be a memory problem, due to your memory size for parsing using ArduinoJson library. Did you use the assistant to compute the memory size you need?

Can you provide an example of the json data you want to analyze? If yes, just paste it into the assistant and it will generate the necessary parsing code together with the required memory size.

This is the String that I receive from my esp:

{"id":1,"lightstatus":"OFF","humgoal":80,"autolight":"ON"}

Even though the value from autolight is "ON", after a while the Arduino code will jump to the ELSE statement.

lesept: It may be a memory problem, due to your memory size for parsing using ArduinoJson library. Did you use the assistant to compute the memory size you need?

Can you provide an example of the json data you want to analyze? If yes, just paste it into the assistant and it will generate the necessary parsing code together with the required memory size.

Just tried your proposal but after around 5 minutes it still jumped to the ELSE statement :(

JasperJP: I even placed 2 Serial.println's to test if the function even reaches the IF and ELSE statement. Even though the value of "autolight" was "ON" (100% sure), the code just jumped to the ELSE statement ?

Sorry, but magic does not happen. If that if jumped to the else branch, it means that the value of your autolight was not "ON".

You need to add a Serial.print call before the if and output the value of autolight surrounded by some visible delimiter characters. Post the output here.

This is the String that I receive from my esp:

{"id":1,"lightstatus":"OFF","humgoal":80,"autolight":"ON"}

Actually, using the recvWithStartEndMarkers() function you have a null terminated character array. A c style string with small s.

Even though the value from autolight is "ON", after a while the Arduino code will jump to the ELSE statement.

For debug, I would printout this String which you test for "ON", byte by byte. There may be corrupted output from the ESP with non printing characters.

String autolight = root[String("autolight")];

Why are you parsing the received character array with the json parser instead of the standard c string tools.

@Montmorency

I’ve placed a few Serial.prinln’s in the function:

Serial.println("<<autolight status: >>"); 
         Serial.println(autolight); 
         if(autolight == "ON"){
              Serial.println("autolight is in IF statement");
              digitalWrite(LEDBLUE, HIGH);
              digitalWrite(LEDYELLOW, LOW);
                 
              if (lightlevel < 550){
                    digitalWrite(ledPin, HIGH);
        
                }
              else{
                    digitalWrite(ledPin, LOW);
   
                  }
          
          }
          else{   
                Serial.println("autolight is in ELSE statement");
                digitalWrite(LEDYELLOW, HIGH);
                digitalWrite(LEDBLUE, LOW); 
                
                  if(lightstatus == "ON"){    
                      digitalWrite(ledPin, HIGH);
      
                  }     
                  else{
                     digitalWrite(ledPin, LOW);

                  }
            
          }

This is the Serial output:

{"id":1,"lightstatus":"ON","humgoal":86,"autolight":"ON"}
<<autolight status: >>
ON
autolight is in ELSE statement

Super weird :confused:

Just a little after that, I got this output:

{"id":1,"lightstatus":"ON","humgoal":86,"autolight":"ON"}
<<autolight status: >>

autolight is in ELSE statement

Now you know "autoLight" contains the string "ON", but does NOT equal the string "ON"...

Montmorency: You need to add a Serial.print call before the if and output the value of autolight surrounded by some visible delimiter characters.

Regards, Ray L.

Update

Fixed it by replacing the IF with:

if(autolight.substring(0) == "ON"){

Thank you :slight_smile:

(The remarks below are based on this implementation: WString.h Wstring.cpp. There are other implementations, but they all behave the same in this respect.)

This gives us a good idea of what happened. Your autolight string somehow ended up with an embedded \0 character in it, not as a terminator but as part of is length(). E.g. autolight could stand for a string "ON\0" of length 3 or even for "ON\0garbage" of length 10.

This will Serial.print as proper ON, but in reality it is more than just ON.

Trying to use the == operator to compare it with C-string "ON" will generate a temporary String of length 2. But a String of length 3 (or 10) is immediately reported by == operator as not equal to a String of length 2. That is why your if is not entered.

Meanwhile, String::substring(0) is implemented to take a substring up to the first \0 character. So, that substring(0) call converts the original "ON\0garbage" into the proper "ON". The equality holds, the if is entered.

if this analysis is correct then another workaround for this issue might look as follows

// Instead of
// if (autolight == "ON")
// try
if (strcmp(autolight.c_str(), "ON") == 0)
   ...

The real question here is: Is the Arduino's String class designed to support embedded \0 characters as part of the string? For example, the standard C++ std::string is designed to support embedded \0. What about Arduino's String?

If it is, then implementation of String::substring is incorrect: it is not supposed to stop at fist \0.

If it is not supposed to support embedded \0, then how did the OP managed to obtain such String from Json parser? A bug in Json parser?

Wow. One Golam is enough !

tasmod: Wow. One Golam is enough !

Haha! :D

I actually enjoyed reading Montmorency's analysis (and his/her other posts). Golam tends to be circumlocutory and brings up irrelevant details like number bases when discussing something like serial communication.

Montmorency brought up some interesting questions. I, too, wonder whether Arduino's String class intends to preserve embedded null bytes. It doesn't appear to do so since it has no constructor that takes a char* and a length. It only has a constructor that takes a char*, which suggests that it's designed to hold only valid C strings.

To answer the last question:

Montmorency: If it is not supposed to support embedded \0, then how did the OP managed to obtain such String from Json parser? A bug in Json parser?

That could be done by using operator[] to insert a null byte inside the String, which likely is a bug in the Json parser--the author of that library might be trying to truncate a String by inserting a null byte (which doesn't quite work as we can see in this thread).

christop: I, too, wonder whether Arduino's String class intends to preserve embedded null bytes. It doesn't appear to do so since it has no constructor that takes a char* and a length. It only has a constructor that takes a char*, which suggests that it's designed to hold only valid C strings.

Yes, I came to the same conclusion. String is intended as a fairly thin wrapper over a malloc-ed C-string. It is a C-string with a stored length. Zero character fully retains its special meaning. Embedding \0 in the middle of String is not allowed.

christop: That could be done by using operator[] to insert a null byte inside the String, which likely is a bug in the Json parser--the author of that library might be trying to truncate a String by inserting a null byte (which doesn't quite work as we can see in this thread).

Good point. Indeed, it is possible to do directly with operator[]. Would be a bug in the Json parser.

To the OP:

If you do a hex dump of your string for indices in [0, length()) range, you'll see if there are any embedded zero values there. If there are, then there's the definitive answer to the original mystery.