bblanchon / ArduinoJson

📟 JSON library for Arduino and embedded C++. Simple and efficient.
https://arduinojson.org
MIT License
6.69k stars 1.12k forks source link

Associating JsonArray entries to different variables #1315

Closed Joaomarques1994 closed 4 years ago

Joaomarques1994 commented 4 years ago

Hi, Firstly I would like to thank bblanchon for the work that he is done regarding ArduinoJson Secondly, I am having issues retrieving the data from the JsonArray. With my current code (shown below) I can see in the Serial Monitor however I cannot associate each array entry to a specific variable. Bare in mind that I serialize and deserialize in the same code only to test those concepts since the main goal is to promote the communication between an esp 8266 and an arduino mega.

Thank you in advance for your time

Code:

#include <ArduinoJson.h>

#include <DHT.h>;

//Constants
#define DHTPIN 7 // what pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302)

DHT dht (DHTPIN, DHTTYPE);
int sensorValue_1, sensorValue_2;
int data1, data2, data3, data4;

void setup() {
  // put your setup code here, to run once:

Serial.begin(9600);
  dht.begin();
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
}

//StaticJsonDocument<5000> doc;
 const size_t CAPACITY =JSON_ARRAY_SIZE(4);
     StaticJsonDocument<CAPACITY> doc;
JsonArray array =doc.to<JsonArray>();

void loop() {
  // put your main code here, to run repeatedly:
float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  sensorValue_1= analogRead(A1);
  sensorValue_2=analogRead(A2);
 Serial.print(h);
  Serial.print(",");
   Serial.print(t);
    Serial.print(",");
    Serial.print(sensorValue_1);
     Serial.print(",");
     Serial.println(sensorValue_2);

    array.add(t);
    array.add(h);
    array.add(sensorValue_1);
    array.add(sensorValue_2);

    serializeJson(doc, Serial);
// doc["temp"] = t;
//  doc["hum"] = h;
// doc["acel1"]=sensorValue_1;
// doc["acel2"]=sensorValue_2;

//  if(Serial.available()>0)
//  {
//    serializeJson(doc, Serial);
//  }
  delay(3000); // 30 detik
//StaticJsonDocument<5000> doc;

//deserializeJson(doc,Serial);
const size_t CAPACITY =JSON_ARRAY_SIZE(4);
     StaticJsonDocument<CAPACITY> doc;
deserializeJson(doc, Serial);
//
JsonArray json_list =doc.as<JsonArray>();
for(JsonVariant v:array){
  Serial.println(v.as<float>());

}
delay(1000);
**MY PROBLEM IS HERE**
data1= json_list[0];
data2= json_list[1];
data3= json_list[2];
data4= json_list[3];

//data1=doc["temp"];
//data2=doc["hum"];
//data3=doc["acel1"];
//data4=doc["acel2"];
//
// Serial.println(data1);
//Serial.println(data2);
//Serial.println(data3);
//Serial.println(data4);

delay(3000);
}
dyarkovoy commented 4 years ago

try to move the line

JsonArray array =doc.to<JsonArray>();

inside the loop() {}

You are adding values to the array in the loop, and each iteration tries to add new set of values, but the old ones aren't being removed. Given that you created the document only for 4 array entries, it adds values only on the first iteration of the loop() and then just runs out of memory. So you need to clear your document before adding new set of values. doc.to<JsonArray>(); should do that for you.

Joaomarques1994 commented 4 years ago

Hi dyarkovoy,

Thank you for your contribution. Now my array add new data. However, I am unable to associate my variables (data1, data2, data3, data4) with the entries of the array. I need to associate this to send data to a database

dyarkovoy commented 4 years ago

Hi, @Joaomarques1994, deserializeJson() returns status, check what it is. Your input may not be parseable.

Joaomarques1994 commented 4 years ago

Thank you once more dyarkovoy. However, I don't think that would be the case since in the serial monitor I manage to see the deserialized data as shown below. The zeros only appear when I try to associate the different data to my data variables (data1, data2, data3, data4). The zeros only appear when I "serial.print" those variables.

64.10,26.00,409,425 [26,64.1,409,425]26.00 64.10 409.00 425.00 0 0 0 0

My issue is to write the following part of the code in a way that I could associate the entries of the array with my variables (data1, data2, data 3, data4)

JsonArray json_list =doc.as(); for(JsonVariant v:array){ Serial.println(v.as());

dyarkovoy commented 4 years ago

Are you writing to and reading from the same document? In your code, there are 2 doc variables defined, one is global and one is inside the loop(){}, which seems confusing. If you intend to use 2 different documents, maybe name them differently and define in the same place? Then, you retrieve JsonArray json_list from the inner doc, but when printing your data you iterate over array, which belongs to the outer doc. I'm not exactly sure if this is your intention, but seems confusing to me. Maybe clean up your code a little.

Joaomarques1994 commented 4 years ago

Thank you once more. My intention is to create a single doc and past the different array entries to my variables

dyarkovoy commented 4 years ago

Then you shoud create a single doc. Right now you create 2 different ones.

Joaomarques1994 commented 4 years ago

Hi again. Thank you once again for helping me with my problem. However, I was not able to correct my issue. I will put my code here and the serial monitor response to show that my problem is not fixed yet. I apologize in advance if the problem occurred due to my stupidity.

Code

include

include ;

//Constants

define DHTPIN 7 // what pin we're connected to

define DHTTYPE DHT22 // DHT 22 (AM2302)

DHT dht (DHTPIN, DHTTYPE); int sensorValue_1, sensorValue_2; int data1, data2, data3, data4;

void setup() { // put your setup code here, to run once:

Serial.begin(9600); dht.begin(); pinMode(A1, INPUT); pinMode(A2,INPUT); }

//StaticJsonDocument<5000> doc;

// const size_t CAPACITY =JSON_ARRAY_SIZE(4); // StaticJsonDocument doc;

//JsonArray array =doc.to();

void loop() { // put your main code here, to run repeatedly: float h = dht.readHumidity(); // Read temperature as Celsius (the default) float t = dht.readTemperature(); sensorValue_1= analogRead(A1); sensorValue_2=analogRead(A2); Serial.print(h); Serial.print(","); Serial.print(t); Serial.print(","); Serial.print(sensorValue_1); Serial.print(","); Serial.println(sensorValue_2);

  const size_t CAPACITY =JSON_ARRAY_SIZE(4);
 StaticJsonDocument<CAPACITY> doc;

JsonArray array =doc.to(); array.add(t); array.add(h); array.add(sensorValue_1); array.add(sensorValue_2);

serializeJson(doc, Serial);

// doc["temp"] = t; // doc["hum"] = h; // doc["acel1"]=sensorValue_1; // doc["acel2"]=sensorValue_2;

// if(Serial.available()>0) // { // serializeJson(doc, Serial); // } delay(3000); // 30 detik //StaticJsonDocument<5000> doc;

//deserializeJson(doc,Serial); //const size_t CAPACITY =JSON_ARRAY_SIZE(4); // StaticJsonDocument doc; deserializeJson(doc, Serial); // JsonArray json_list =doc.as(); for(JsonVariant v:array){ Serial.println(v.as());

} delay(1000);

data1= json_list[0]; data2= json_list[1]; data3= json_list[2]; data4= json_list[3];

//data1=doc["temp"]; //data2=doc["hum"]; //data3=doc["acel1"]; //data4=doc["acel2"]; // Serial.println(data1); Serial.println(data2); Serial.println(data3); Serial.println(data4);

delay(3000); }

Serial Monitor 63.20,25.80,405,421 [25.8,63.2,405,421]25.80 63.20 405.00 421.00 0 0 0 0

dyarkovoy commented 4 years ago

If I understand your logic correctly, you serialize JSON to Serial port and then want to deserialize it by reading from the same Serial port on the same device that wrote it? This isn't possible, Serial is not a buffer where you can send data and retrieve later. Serial is a means of communication between TWO enpoints. Endpoint A writes to serial port and Endpoint B receives it at the same moment. Endpoint B writes to serial port and Endpoint A receives it at the same moment. But it is not possible that Endpoint A writes some data to Serial, and some moments later Endpoint A reads what it wrote, it doesn't work that way. To make your code work the way you want to, you need to connect Serial port on your Arduino (or whatever hardware you use), let's call it Endpoint A, not to the PC with the serial monitor, but to another Arduino ( let's call it Endpoint B). Now Endpoint A should do serializeJson(doc, Serial); with intervals in the loop, and Endpoint B should check in the loop if there's data available on the Serial ( if(Serial.available()>0) ... ) and if it is, call deserializeJson(doc, Serial);. This is only a rough draft, not the exact recipe, because there are other pitfalls. For example, Endpoint B needs to understand where is the start if the data, because you may turn it on at the moment when Endpoint A is in the middle of writing, so what should Endpoint B do? It should somehow understand that what it just read is only a part of the packet, and the other part was lost because it started reading few moments after Endpoint A started writing, so it should discard this partial packet and wait for the new one. Usually this problem is solved by writing some special character or sequence of characters that would indicate the start of the packet, and ending the packet with another special character(s). There are other things to consider. If you want to go this way, read about Point-To-Point protocol (PPP), it is designed for these kind of tasks.

Joaomarques1994 commented 4 years ago

Thank you so much for the explanation. Firstly I would like to apologize since my main goal is to have two endpoints (one is an arduino Mega and the other one is an esp 8266) (it is an arduino mega wifi). I tested the two endpoints but only zeros appeared in my database. With this in mind, I only tested in the same endpoint because I did not want to fill my database with nonsense data (only zeros). So I thought that I could test the serialize concept on one endpoint. However, with your explanation, I know now that it is not possible. I will try to use both at the same time. Sorry once if I made you waste time with me regarding this question. Once more thank you for the explanation and patience that you had. If I manage to do it right, I will let you know.

Joaomarques1994 commented 4 years ago

One quick question, if I only put deserialize (doc, Serial) in the other code it will give me the error that doc was not declared in the scope. I would like to ask how can I declare doc without creating a different doc. Thank you in advance

dyarkovoy commented 4 years ago

You should read about scope rules in C, and also about declaring and defining variables. The simple answer is that you need to do StaticJsonDocument<5000> doc; only once, and not many times. If your code complains that doc was not declared, and you already have that line in your code, it may be misplaced.

Joaomarques1994 commented 4 years ago

Hi, once more thank you for answering my question. I understand that I only can write StaticJsonDocument<5000> doc;. However, in the other endpoint I need to declare doc again. Do you know should declare doc? thanks in advance

dyarkovoy commented 4 years ago

To be honest, I don't understand your question. You just write StaticJsonDocument<5000> doc; in the software for Endpoint A. Then, you write StaticJsonDocument<5000> doc; in the software for Endpoint B. And now you have doc declared and defined both in your software for for Endpoint A and in your software for Endpoint B. Are you worried about double definition? There is none, because you have 2 separate endpoints and 2 separate softwares. So you declare one doc in the software for Endpoint A and one doc in the software for Endpoint B. When I said before that you should use a single definition, it means single definition per scope, not single definition per all the software you ever write.

Joaomarques1994 commented 4 years ago

Thank you again and so sorry for my stupidity. I will try and let you know. Once again thank you

Joaomarques1994 commented 4 years ago

Hi once again Firstly I would like to apologize for taking so time to solve my problem. I tried to perform as you said but only manage to receive 0 at my database. I am using an arduino mega wifi. I have to different codes one for the mega and other for the esp8266 I will put here both codes. The communication to my database is working since I put some random values in my esp 8266 and they appeared at my database. Thank you in adavance

Mega

include

include ;

//Constants

define DHTPIN 7 // what pin we're connected to

define DHTTYPE DHT22 // DHT 22 (AM2302)

DHT dht (DHTPIN, DHTTYPE); int sensorValue_1, sensorValue_2; int data1, data2, data3, data4;

void setup() { // put your setup code here, to run once:

Serial.begin(9600); dht.begin(); pinMode(A1, INPUT); pinMode(A2,INPUT); }

void loop() { // put your main code here, to run repeatedly: float h = dht.readHumidity(); // Read temperature as Celsius (the default) float t = dht.readTemperature(); sensorValue_1= analogRead(A1); sensorValue_2=analogRead(A2); Serial.print(h); Serial.print(","); Serial.print(t); Serial.print(","); Serial.print(sensorValue_1); Serial.print(","); Serial.println(sensorValue_2);

  const size_t CAPACITY =JSON_ARRAY_SIZE(4);
 StaticJsonDocument<CAPACITY> doc;

doc["temp"] = t; doc["hum"] = h; doc["acel1"]=sensorValue_1; doc["acel2"]=sensorValue_2;

serializeJson(doc, Serial);

delay(3000); // 30 detik }

Esp 8266

include

include

include

include

include

// DECLARAÇÃO DE VARIÁVEIS PARA WIFI const char ssid = ; // name of your wifi network!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! const char password = ; // wifi pasword

// DECLARAÇÃO DE VARIÁVEIS PARA MySQL IPAddress server_addr(); // IP of the MySQL server here char user = ; // MySQL user login username char password_sql = ; // MySQL user login password

char INSERT_SQL[] = ; char query[128]; WiFiClient client; MySQL_Connection conn((Client *)&client);

// DECLARAÇÃO DE FUNÇÕES //void conectaWifi(); //void enviaDados();

int data1, data2, data3, data4; void setup() { // put your setup code here, to run once: Serial.begin(9600); conectaWifi();

while (!conn.connect(server_addr, 3306, user, password_sql)) { Serial.println("Conexão SQL falhou."); conn.close(); delay(1000); Serial.println("Conectando SQL novamente.");

if (conn.connect(server_addr, 3306, user, password_sql)){ Serial.println("Conectado ao servidor SQL."); } } } void loop() { // put your main code here, to run repeatedly:

const size_t CAPACITY =JSON_ARRAY_SIZE(4); StaticJsonDocument doc; deserializeJson(doc, Serial);

data1=doc["temp"]; data2=doc["hum"]; data3=doc["acel1"]; data4=doc["acel2"];

//

//data1=30; //data2=50; //data3=400; //data4=400; Serial.println(data1); Serial.println(data2); Serial.println(data3); Serial.println(data4);

enviaDados(); }

void conectaWifi(){ WiFi.mode(WIFI_STA); WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) { delay(500); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void enviaDados(){ sprintf(query, INSERT_SQL, data1, data2, data3, data4); // Initiate the query class instance MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); // Execute the query cur_mem->execute(query); // Note: since there are no results, we do not need to read any data // Deleting the cursor also frees up memory used delete cur_mem; Serial.println("Informações Enviadas"); delay(3000); }

dyarkovoy commented 4 years ago
  1. You should not print raw data to Serial on the sending endpoint. When you do this, you confuse the receiving endpoint, because it will not be able understand where the raw data is, and where the serialized data is. You need to remove or comment out this part of the code:
    Serial.print(h);
    Serial.print(",");
    Serial.print(t);
    Serial.print(",");
    Serial.print(sensorValue_1);
    Serial.print(",");
    Serial.println(sensorValue_2);
  2. You allocate your doc for the array, but use it as an object. This is wrong, you should decide whether you want array of values, i.e. [1,2,3,4], or an object, i.e {"temp":1,"hum":2,"acel1":3,"acel2":4}. In the former case, you should compute capacity of the document as const size_t CAPACITY = JSON_ARRAY_SIZE(4);, in the latter case you should compute capacity of the document as const size_t CAPACITY = JSON_OBJECT_SIZE(4);.
  3. You should do synchronization on the receiving endpoint as I explained in this post. The easiest way for your case, if you serialize as an array, your data packet will always start with '[' and end with ']'. If you decide to serialize as an object, your data packet will always start with '{' and end with '}'. So first you need to decide whether you want to use array or object. Next, on the receiving endpoint, instead of doing deserializeJson(doc, Serial);, you should define a String input and read one character from Serial in the loop until you hit the starting character of your data packet, either '[' or '{'. Once you get that starting character, you continue reading in the loop character-by-character and append them to the input until you hit ending character (either ']' or '}'). Once that happens, you are ready to call deserializeJson(doc, input); and now you should have valid data in your doc. Another approach would be to serialize to string on the sending endpoint and use Serial.println(serialized string), then on the receiving endpoint do Serial.readStringUntil('\n') and then deserialize your document from the string. Both approaches suffer from deficiencies, but they have an advantage of being simple. If you want ultimate and reliable solution to transfer packets between endpoints via serial interface, you should look into Point-To-Point protocol (PPP)

Finally, this discussion is moving further and further away from ArduinoJson and into the very basics of C. You may want to find forum/thread for general C questions and ask your questions there.

bblanchon commented 4 years ago

Finally, this discussion is moving further and further away from ArduinoJson and into the very basics of C. You may want to find forum/thread for general C questions and ask your questions there.

Agreed! This is not the right place for this type of discussion. @dyarkovoy was kind enough to answers all your lengthy questions for the last ten days. Please stop abusing his (her) generosity.

@Joaomarques1994, I don't think you realize how much time it takes to write detailed answers as he (she) did. Maybe if you spent more time working on your questions, you'd find the answer yourself, or at least, give all the information to the people who are trying to help you.