[Solved]Pass local const char* data to global?

Hello,
I’m using ArduinoJson to get data and that works well. Problem is, how can I “properly” pass the received local const char* data to a global variable.

const char* localData = root["variable"];

I can’t seem to word the question in a way that google gives me some hits that lead me in the right direction.
I can pass the data if I declare the global variable as const char*, but this must be wrong, as I’m declaring the global variable as constant and then changing it’s contents.
I have also tried this conversion:

const char* localData = root["variable"];
char* convertedLocalData = const_cast<char*>(localData);
globalData = convertedLocalData;

Declaring the global variable as char* it passes.
In both cases I can Serial.print the proper data after the passing to local, but after receiving more JSON data not related to the passed data the global variable changes to some of the newly received unrelated data. It is the only global variable to change for no apparent(to me) reason.
I’m lost :confused:
Could someone please throw me a bone on this issue?
My sketch is large (around 30kb), not fully organized and not complete, but I will post if this is not a general programming problem and is needed.
It compiles and all works properly aside from this issue.
If it can’t be done, oh well, I can leave the affected part out of my project.

First, the statement:

const char* localData = root["variable"];

is not going to compile because an index for an array must be an integral value, not a string.

Second, a pointer variable like localData needs to have a valid memory address (lvalue) assigned to it, not the contents of root[]. Usually, one would expect to see something like:

const char* localData = root;           // Same as assigning the base of root[], or &root[0]

// or

const char* localData = &root[index];   // The memory address (lvalue) of the "index"-th value in the array

but not what you have. A valid pointer can only hold one of two values: 1) a memory address (i.e., an lvalue), or 2) null, which indicates a non-valid pointer.

Third, why are you making localData a constant?

Thanks for the reply, maybe this will add a bit more info to my problem.

econjack:
First, the statement:

const char* localData = root["variable"];

is not going to compile because an index for an array must be an integral value, not a string.

It does compile and works fine as I have multiple const char* declarations without problem.

Third, why are you making localData a constant?

Because if I remove the constant to just char* it throws this error:

conversion from ‘ArduinoJson::JsonVariant’ to ‘char*’ is ambiguous

This is the original code I based my project off of:

Weather-Display/ESP8266_Weather_Display.ino
@thisoldgeek thisoldgeek on Jan 2 add sketch


#include <ArduinoJson.h>
#include <SoftwareSerial.h>

#define SSID "<SSID>" // insert your SSID
#define PASS "<PASSWORD" // insert your password
#define LOCATIONID "<LOCATION>" // location id
#define DST_IP "23.222.152.140" //api.wunderground.com

#define ESP_RST 4


SoftwareSerial lcdSerial(2, 3); // RX, TX for debugging

const int buffer=300;

int passNum = 1;

// Array of desired weather conditions 
// These must be in the order received from wunderground!
//
// Also, watch out for repeating field names in returned json structures, 
//  and fields with embedded commas (used as delimiters)


char* conds[]={"\"city\":","\"weather\":","\"temp_f\":","\"relative_humidity\":","\"wind_dir\":","\"wind_mph\":","\"pressure_in\":"};
int num_elements = 7;  // number of conditions you are retrieving, count of elements in conds

char close_brace ='}';
char comma = ',';

void setup()
{  pinMode(ESP_RST,OUTPUT);
  reset();

  Serial.begin(9600);
  Serial.setTimeout(5000);
  lcdSerial.begin(9600); // Print to LCD (or similar display)
  
}
void loop()
{
   reset();
   delay(5000);  //ESP8266 takes a while to restart
 
 // try to connect to wifi
  boolean connected=false;
  for(int i=0;i<5;i++){
    if(connectWiFi()){
      connected = true;
      break;
    }
  }
   
  Serial.println("AT+CIPMUX=0"); // set to single connection mode
  String cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += DST_IP;
  cmd += "\",80";
  Serial.println(cmd);
  //lcdSerial.println(cmd);
  if(Serial.find("Error")) return;
  
  cmd = "GET /api/<YOURKEY>/conditions/q/";
  cmd += LOCATIONID;
  cmd +=".json";
  cmd += " HTTP/1.1\r\nHost: api.wunderground.com\r\n\r\n";
  Serial.print(F("AT+CIPSEND="));
  Serial.println(cmd.length());
  if(Serial.find(">")){
    //lcdSerial.print(">");
  }else{
    Serial.println(F("AT+CIPCLOSE"));
    lcdSerial.println(F("?fConnection timeout"));
    delay(1000);
    return;
  }
  Serial.print(cmd);
  
  unsigned int i = 0; //timeout counter
  char json[buffer]="{"; // array for Json parsing
  int n = 1;          // character counter for json  
  
  for (int j=0;j<num_elements;j++){
    while (!Serial.find(conds[j])){} // find the part we are interested in.
  
    String Str1 = conds[j];
  
    for (int l=0; l<(Str1.length());l++)
        {json[n] = Str1[l];
         n++;}   
    while (i<60000) {
      if(Serial.available()) {
        char c = Serial.read();
         
          if(c==',') break;
          json[n]=c;
          n++;
          i=0;
        }
        i++;
      }
     if (j==num_elements-1)
        {json[n]=close_brace;}
     else   
        {json[n]=comma;}
     n++;   
  }

parseJSON(json);

// Done with processing for now - close connection
Serial.println(F("AT+CIPCLOSE"));

  delay(5000);

 // Only check weather every 15 minutes
 // So you don't go over quota on wunderground (for free api license)
  delay(900000);
}


void parseJSON(char json[300])
{
  StaticJsonBuffer<buffer> jsonBuffer;
 JsonObject& root = jsonBuffer.parseObject(json);
 
 if (!root.success())
{
  lcdSerial.print("?fparseObject() failed");
  //return;
}

 
 const char* city = root["city"];
 const char* weather = root["weather"];
 double temp_f = root["temp_f"];
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
 const char* humidity = root["relative_humidity"]; //***THIS IS WHAT I WANT TO PASS*****
////////////////////////////////////////////////////////////////////////////////////////////////////////////
 const char* wind_dir = root["wind_dir"];
 double wind_mph = root["wind_mph"];
 const char* pressure_in = root["pressure_in"];
 
 
 // location of conditions
 lcdSerial.print("?f    ");
 lcdSerial.print(city);
 
 // Conditions: Sunny, Cloudy, Fog, Rain, etc. 
 lcdSerial.print(F("?n"));
 lcdSerial.print(weather);
  
 // Temp
 lcdSerial.print(F("?n"));
 lcdSerial.print((int)temp_f);
 lcdSerial.print(F("F|"));

// Humidity 
 lcdSerial.print(humidity);
 lcdSerial.print(F("|"));
 
 // Wind Direction
 lcdSerial.print(wind_dir);
 lcdSerial.print(F(" "));
 
  // Wind Speed
 lcdSerial.print((int)wind_mph);
 lcdSerial.print(F(" "));
 lcdSerial.print(F("MPH"));
 
 
 // Barometric Pressure
 lcdSerial.print(F("?nBarometer: "));
 lcdSerial.print(pressure_in);
  
}


void reset()
{
  digitalWrite(ESP_RST,LOW);
  delay(100);
  digitalWrite(ESP_RST,HIGH);
 
}


boolean connectWiFi()
{
  Serial.println(F("AT+CWMODE=1"));
  String cmd="AT+CWJAP=\"";
  cmd+=SSID;
  cmd+="\",\"";
  cmd+=PASS;
  cmd+="\"";
  Serial.println(cmd);
  delay(2000);
  if(Serial.find("OK")){
    lcdSerial.println(F("?fConnected to WiFi..."));
    delay(500);     // Allow display to print
    return true;
  }else{
    lcdSerial.println(F("?fCan't Connect..."));
    delay(500);
    return false;
  }
}

In that code I commented the line with the local humidity value I’m trying to pass to a global variable.

econjack: First, the statement:

const char* localData = root["variable"];

is not going to compile because an index for an array must be an integral value, not a string.

Unless the [] operators are overloaded, which this lib does apparently, yummy templates too :D (puke):

const JsonVariant &operator[](key_type key) const { return at(key); }

In both cases I can Serial.print the proper data after the passing to local, but after receiving more JSON data not related to the passed data the global variable changes to some of the newly received unrelated data. It is the only global variable to change for no apparent(to me) reason.

You have a global pointer pointing to the root, root changes, therefore what your global points to changes. Allocate some memory and copy it in, ie:

char g_global_root[64];
// make sure 'variable' key points to a char type! Also make sure your array is large enough ...
strcpy( g_global_root, (char*)root["variable"]);

Hi elac

Looking at the example program for the JSON library, it looks like root holds a reference to a JSON object, and the [] has been overloaded to extract the parsed values.

How about declaring a global char array of appropriate size. Pass a pointer to this array into your function. Within the function, strncpy() the data you want from the JSON object into the global array. I've seen this technique used in an MQTT messaging library to read out the data content of a received message before the next message overwrites it.

Regards

Ray

Thank you very much everyone!! A round of +1 karma for all!! I am able to go forward now :grin: TammyTam your example was just what I needed to see. Here is what worked:

// Set global variable
char outHumid[4];
//Used this in parseJSON function
 const char* humidity = root["relative_humidity"];
 strcpy( outHumid, (const char*)root["relative_humidity"]);
//Used this to display outHumid on TFT
 tft.print(outHumid);
// Debug to see if variable held is constantly correct
 Serial.println(outHumid);

I had to change:

strcpy( outHumid, (char*)root["relative_humidity"]);

to

strcpy( outHumid, (const char*)root["relative_humidity"]);

or I received this error:

invalid cast from type 'ArduinoJson::JsonVariant' to type 'char*'

Now this all works great and I am extremely grateful... but I also would like to know why not just how. Could I be pointed to some easy to understand reading material about whats going on here? Best would be with some examples. strncpy or strcpy, which to use when, why? As of now it's looking pretty Greek to me. Like TammyTam said:

Unless the [] operators are overloaded, which this lib does apparently, yummy templates too :D (puke):

and Hackscribble said:

Looking at the example program for the JSON library, it looks like root holds a reference to a JSON object, and the [] has been overloaded to extract the parsed values.

What is the significance of [] operators are overloaded those statements suggest something? But what importance it has I don't know.

For context, a C string is an array of char where one of the elements is set to the null character '\0' to indicate the end of the valid contents. The function strcpy copies one C string to another. It looks for the null to know when to stop.

For example:

char buffer[20];
strcpy(buffer, "ABC");

After this, the first four elements of buffer will be 'A', 'B', 'C' and '\0'.

Sometimes, you need to copy bytes from an array that is not a C string (i.e. does not have a terminating null) into a C string. You will have to know how many bytes to copy. This is where strncpy comes in:

strncpy(buffer, source, numBytes);  // copy numBytes bytes from source to buffer
// if you need buffer to be a C-string, put in the null ...
buffer[numBytes] = '\0';

You need to check in your code that numBytes is not going to cause an overrun of either the source or destination.

What is the significance of [] operators are overloaded those statements suggest something? But what importance it has I don't know.

As you know, the usual use of square brackets is to access an element of an array. As @tammytam discovered, the library has overloaded or redefined what the square brackets operator means when used with its object class.

The library parses JSON and creates a list of keys and values. To access this list, you provide a key inside the square brackets and the object returns the matching value.

Thanks again Hackscribble for the easy to understand explanations. I will look into the ArduinoJson library to better understand your explanation of the [] operators are overloaded.

strncpy(buffer, source, numBytes);  // copy numBytes bytes from source to buffer

Is just what I needed to pass the global outHumid data to outHighHumid outLowHumid variables for a temp/humidity high-low-current function. Pic of what was accomplished: |500x375

Quick update, to compare/change the high low values to the char outHumid[5] value I first had to convert the outHumid value like so:

int convertedOutHumid = atoi(outHumidBuffer);

Renaming outHumid to outHumidBuffer to better fit it’s purpose.
Now the humidity comparison code works properly. :slight_smile:

if (outHighHumid < convertedOutHumid)
    {
      outHighHumid = convertedOutHumid;
    }
if (outLowHumid > convertedOutHumid)
    {
      outLowHumid = convertedOutHumid;
    }

Final humidity part of comparison function:

//Global variables
char outHumidBuffer[5];
int outHighHumid;
int outLowHumid;

// Humidity part of High/Low/Current function
const char* humidity = root["relative_humidity"];
strcpy(outHumidBuffer, (const char*)humidity);
int convertedOutHumid = atoi(outHumidBuffer);
if (outHighHumid < convertedOutHumid)
    {
      outHighHumid = convertedOutHumid;
    } 
if (outLowHumid > convertedOutHumid)
    {
      outLowHumid = convertedOutHumid;
    }