How to store JSON Array in arduino program? - c++

I was working on IOT project for which I needed JSON parsing in NodeMCU.
I saw this sample code for arduino for parsing JSON with the help of ArduinoJson library which worked well and I was able to get and parse the data from url(say url_1) successfully. Now I want to store this data in an array so that when I get data from the other ur2(say url_2) I can compare them with each other and trigger an event repective to the result.
Data in url_1 and url_2 is of form,
["1","1","1","1","0","0","0","0"]
and assume that url_1 had same values as specified.
What I did was I declared an array Data[] in which I stored the parsed JSON values so that I could use them later on in if else statements in the code.
As you can see that the data I am retrieving in url have 1's and 0's only, so what I want to do is that "If get 1 do this", "else do that", which you can see in the code. But the problem is that once I end the connection to the url the Data[] array gives only garbage values which I checked by printing them on serial monitor as shown in the code.
What I believe is that "const char* Data[20];" stores the position of JSON data and when I end the connection, the data at those position is also lost that's why I'm getting the garbage values. Now, I could be wrong since I'm new to this stuff. That's why I wanted to know how to solve this problem which is that if what I said was right, then how could I store the parsed json data in an array so that it is not lost even if the connection to the url has been ended.
(I'm new to this platform so If I did something wrong and wish that you guys can guide me for the future and I also apologize for my broken english).
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
const char* Data[20];
const char* ssid = "SSID";
const char* password = "Password";
//Connecting to WiFi
void setup() {
WiFi.mode(WIFI_OFF);
delay(1000);
WiFi.mode(WIFI_STA);// Hides the viewing of ESP as wifi Hotspot
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to WiFi Successfully");
}
void loop()
{
if(WiFi.status()== WL_CONNECTED)
{
HTTPClient http;
//Starting connection to url_1
http.begin("url_1");
int httpCode = http.GET();
if(httpCode > 0)
{
String data = http.getString();
const size_t bufferSize = JSON_ARRAY_SIZE(8) + 20;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonArray& root = jsonBuffer.parseArray(data);
for(int i=0;i<8;i++){
Data[i] = root[i];
Serial.println("Printing whole Data");
Serial.println(Data[i]);
}
}
http.end();//ending the connetion
for(int i=0;i<8;i++)
{
if(strcmp(Data[i],"1")==0){
Serial.print("if satement, Data = ");
Serial.println(Data[i]);
}
else
{
Serial.print("else satement, Data = ");
Serial.println(Data[i]);
}
}
}
}

Data[] contains pointers to dynamically allocated strings. When you leave the code block with the JSON parser, its destructor is called and therefore the allocated memory can and has been overwritten by something else.
I would suggest to use instead
bool Data[...];
...
// true for "1", false for "0"
Data[i] = strcmp(root[i], "1") == 0;
...
EDIT if you need to store more "complicated" data, e.g. actual strings, you will need to make a copy of the string pointed to by root[i].

Related

BLE using ESP 32

I am trying to program a BLE client using an ESP32. I have used the attached code (which is the example code from the example section). My problem right now is, how do I get it to show the MAC address of the scanned device only, and how do I change this MAC address into a JSON format for a LoRa chip to transmit? Thank you so much for your time to read this post.
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 5; //In seconds
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
}
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
The result of your scan is foundDevices, which contains a list of all found devices. We can iterate through it to access the devices. The List contains BLEAdvertisedDevices which have a method called getAddress(). The address can be converted to a string using toString(). You can print the string or put it into your json container.
Your loop-code could look like this:
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
for (int i = 0; i < foundDevices.getCount(); ++i)
{
std::string address = foundDevices.getDevice(i).getAddress().toString();
int rssi = foundDevices.getDevice(i).getRSSI();
Serial.print(address.c_str());
Serial.print(rssi);
// TODO: Insert into JSON
}
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}

Arduino string comparison not working when using String str_out = String((char*)buf);

I am successfully receiving data over RF on a second Arduino. However I am trying to compare the incoming string so that I can call a method when there is a match. The "if" block is never executed. I am just trying to compare the strings that are incoming. It's printing the correct values to the serial monitor but the block is never executed. Maybe because this is using a pointer to a string (don't shoot me)? I am not that familiar with C or C++. I have tried several of the string comparison methods in the Arduino docs but no joy? Any reccomendations?
void loop()
{
// Set buffer to size of expected message
uint8_t buf[7];
uint8_t buflen = sizeof(buf);
// Check if received packet is correct size
if (rf_driver.recv(buf, &buflen))
{
// Message received with valid checksum
Serial.print("Message Received: ");
String str_out = String((char*)buf);
Serial.println(str_out); /
if (str_out == "plasma1") { // this is never executed wtf!!!
plasmaSequenceOne();
} else if (str_out == "plasma2") {
plasmaSequenceTwo();
} else if (str_out == "plasma3") {
plasmaSequenceThree();
} else if (str_out == "plasma4") {
plasmaSequenceFour();
}
}
}
Making Alex' self-response easier readable, and avoiding unnecessary String objects:
uint8_t buf[8];
uint8_t buflen = sizeof(buf)-1; // leave space for terminating 0
if (rf_driver.recv(buf, &buflen)) {
buf[buflen] = 0;
char* str_out = (char*)buf;
Serial.println (str_out);
if (strcmp(str_out, "plasma1")==0) plasmaSequenceOne();
...
}
This worked, thanks to #Remy Lebeau.

Strange characters after JSON

I have an application that gets the Arduino Nano information and sends it to ESP-01 via UART. The ESP-01 send this to MQTT.
NANO CODE:
#include "DHTesp.h"
#include <ArduinoJson.h>
#include <SoftwareSerial.h>
#define gasSensor A1
#define dhtPin 5
#define rain A2
#define soil A3
#define ldr A4
DHTesp dht;
void setup() {
Serial.begin(115200);
pinMode(gasSensor, INPUT);
pinMode(rain, INPUT);
pinMode(soil, INPUT);
pinMode(ldr, INPUT);
digitalWrite(dhtPin, LOW);
dht.setup(dhtPin, DHTesp::DHT11);
}
void loop() {
delay(dht.getMinimumSamplingPeriod());
float humidity = dht.getHumidity();
float temperature = dht.getTemperature();
DynamicJsonBuffer jBuffer;
JsonObject& measure = jBuffer.createObject();
JsonObject& data = jBuffer.createObject();
measure["gas"] = analogRead(gasSensor);
measure["humidity"] = humidity;
measure["temperature"] = temperature;
measure["heatindex"] = dht.computeHeatIndex(temperature, humidity, false);
measure["rain"] = analogRead(rain);
measure["soil"] = analogRead(soil);
measure["ldr"] = analogRead(ldr);
data["measure"] = measure;
data.printTo(Serial);
}
ESP-01 CODE:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
const char* mqtt_server = "0.0.0.0";
WiFiClient espClient;
PubSubClient client(espClient);
char mystr[100];
void setup() {
Serial.begin(115200);
WiFi.begin("", "");
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println();
Serial.print("Connected to IP: ");
Serial.println(WiFi.localIP());
client.setServer(mqtt_server, 1883);
}
void loop() {
if (client.connect("ESP")) {
Serial.println("STATUS MQTT-ESP: OK");
while (true) {
Serial.readBytes(mystr, 108);
client.publish("esp", mystr);
delay(1000);
}
} else {
Serial.println("STATUS MQTT-ESP: OFF");
}
}
But, I'm getting strange characters into MQTT:
And, at the connection between NANO <-uart-> Computer, the JSON is normal:
Someone could help me?
Thanks.
I am basing this answer on a bit of assumptions as I don't have an Arduino at hand.
You send your data like this:
data.printTo(Serial);
This sends the string holding the formatted JSON data. This does not include the terminating 0 byte.
Then you receive it like this:
char mystr[100];
...
while(true){
Serial.readBytes(mystr, 108);
client.publish("esp", mystr);
delay(1000);
}
This has multiple errors:
You do not care if you got any bytes at all. The method returns the number of bytes but you do not handle the return value at all. If you got 0 bytes within the timeout value, you will just send the previous message again instead of waiting for valid data.
You cannot put 108 bytes into memory location of 100 bytes.
You put an array of char into the publish method that does not contain any termination. How should that method know how many characters are really part of the string?
Try this:
while(true){
size_t num = Serial.readBytes(mystr, sizeof(mystr)-1);
mystr[num] = 0;
client.publish("esp", mystr);
delay(1000);
}
You do not have any protocol that allows you to detect when a message starts or ends. UART communiation is a byte stream without any boundaries for datagrams. You must ensure that you know what belongs to a message and what does not. This also means that you can never know if you have a complete message in your receive bufffer or an incomplete or even more than one message. It's up to you to detect and split messages. Fixing this is a bit more complex. I can only give some hints what you need to do.
a) Detect message boundaries:
You might send some terminator like \n, 0 or similar after each JSON string and scan for this extra byte on receiver side.
Or you can send the length of the string before you send the string.
You could also just check on receiver side when you have a matching pair of {}. This would not require any change on sender side.
b) Collect messages:
Call your read function as long as it takes until you detect the end of a string.
You might need to use intermediate buffer to collect multiple read buffers.
c) Forward messages:
As soon as you detected the end of a message, forward it via publish function.
Then move the remaining content of your collection buffer to the start of that buffer.
If you immediately find the end of another message, repeat that step.
If you don't find any complete message, continue to collect more data until the next message is complete.

Load config parameters on Arduino ESP8266

I am using Arduino ESP8266 to store and load configuration settings on SPIFSS. I used this ConfigFile.ino as a reference example.
https://github.com/esp8266/Arduino/blob/master/libraries/esp8266/examples/ConfigFile/ConfigFile.ino
This function loads the configuration settings onto variables serverName and accessToken.
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
// We don't use String here because ArduinoJson library requires the input
// buffer to be mutable. If you don't use ArduinoJson, you may as well
// use configFile.readString instead.
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
if (!json.success()) {
Serial.println("Failed to parse config file");
return false;
}
const char* serverName = json["serverName"];
const char* accessToken = json["accessToken"];
// Real world application would store these values in some variables for
// later use.
Serial.print("Loaded serverName: ");
Serial.println(serverName);
Serial.print("Loaded accessToken: ");
Serial.println(accessToken);
return true;
}
I made some modifications to this function to load the configuration settings into a struct.
struct ConfigSettingsStruct
{
String ssid;
String password;
};
ConfigSettingsStruct ConfigSettings;
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
// We don't use String here because ArduinoJson library requires the input
// buffer to be mutable. If you don't use ArduinoJson, you may as well
// use configFile.readString instead.
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
if (!json.success()) {
Serial.println("Failed to parse config file");
return false;
}
//const char* serverName = json["serverName"];
//const char* accessToken = json["accessToken"];
char ssid_[30];
strcpy(ssid_, json["ssid"]);
ConfigSettings.ssid = String(ssid_);
char password_[30];
strcpy(password_, json["password"]);
ConfigSettings.password = String(password_);
// Real world application would store these values in some variables for
// later use.
Serial.print("Loaded ssid: ");
Serial.println(ConfigSettings.ssid);
Serial.print("Loaded password: ");
Serial.println(ConfigSettings.password);
return true;
}
After I download the code and run ESP8266, the WiFi chip resets with some stack error. What is wrong with my code? How can the config settings be properly loaded onto ConfigSettings?
There is nothing wrong with your code in the question. It should work. I strongly suspect that the cause of the stack error lies elsewhere. Please check your code carefully again.
This does not count as an answer but can be helpful as a reminder to look elsewhere. You may be looking at the wrong place.
Please notice that; you have a possible memory leak after
std::unique_ptr<char[]> buf(new char[size]);
I suggest you to use to allocate some memory via malloc (which is not stylish but classic) and free it after all. You need also close file before returns.
Also your ssid and passphrase buffer lengths are not enough. Max ssid length must be 32. Assuming that you got a psk based encryption, you need to increase pass buffer length to 64.
Tiny but; maybe you can think to add a typedef before struct define despite C++ threads them definable within the namespace.

Losing characters in TCP Telnet transmission

I'm using Winsock to send commands through Telnet ; but for some reason when I try to send a string, a few characters get dropped occasionally. I use send:
int SendData(const string & text)
{
send(hSocket,text.c_str(),static_cast<int>(text.size()),0);
Sleep(100);
send(hSocket,"\r",1,0);
Sleep(100);
return 0;
}
Any suggestions?
Update:
I checked and the error still occurs even if all the characters are sent. So I decided to change the Send function so that it sends individual characters and checks if they have been sent:
void SafeSend(const string &text)
{
char char_text[1];
for(size_t i = 0; i <text.size(); ++i)
{
char_text[0] = text[i];
while(send(hSocket,char_text,1,0) != 1);
}
}
Also, it drops characters in a peculiar way ; i.e. in the middle of the sentence. E.g.
set variable [fp]exit_flag = true
is sent as
ariable [fp]exit_flag = true
Or
set variable [fp]app_flag = true
is sent as
setrable [fp]app_flag = true
As mentioned in the comments you absolutely need to check the return value of send as it can return after sending only a part of your buffer.
You nearly always want to call send in a loop similar to the following (not tested as I don't have a Windows development environment available at the moment):
bool SendString(const std::string& text) {
int remaining = text.length();
const char* buf = text.data();
while (remaining > 0) {
int sent = send(hSocket, buf, remaining, 0);
if (sent == SOCKET_ERROR) {
/* Error occurred check WSAGetLastError() */
return false;
}
remaining -= sent;
buf += sent;
}
return true;
}
Update:
This is not relevant for the OP, but calls to recv should also structured in the same way as above.
To debug the problem further, Wireshark (or equivalent software) is excellent in tracking down the source of the problem.
Filter the packets you want to look at (it has lots of options) and check if they include what you think they include.
Also note that telnet is a protocol with numerous RFCs. Most of the time you can get away with just sending raw text, but it's not really guaranteed to work.
You mention that the windows telnet client sends different bytes from you, capture a minimal sequence from both clients and compare them. Use the RFCs to figure out what the other client does different and why. You can use "View -> Packet Bytes" to bring up the data of the packet and can easily inspect and copy/paste the hex dump.