1

Before someone flags this as duplicate I have found these two links and neither totally apply although I have implemented at least a little of both.

Buffer gets overwritten

Arduino reading json from EEPROM / converting uint8_t to char

My problem is this. I am trying to read and write a JSON string to an Arduinos EEPROM using ArduinoJson library (https://github.com/bblanchon/ArduinoJson). In the below code I generate a JsonObject from a hard coded JSON string (this works). I then write it to EEPROM (this works). It then gets read back from EEPROM (this works) but when I try and parse the second string using ArduinoJSON i get a parse failure.

For the purpose of testing I also clear the EEPROM each time just in case although eventually this will be removed.

The code compiles with no errors. I am hoping that someone more knowledgable in C++ than myself will spot something really obvious. I am compiling this onto a NodeMCU (esp8266).

#include <ArduinoJson.h>
#include <EEPROM.h>

StaticJsonBuffer<400> jsonBuffer;
JsonObject *jsonObject;
JsonObject *config;

String dummyJson = "{\"name\":\"RGB LED 1\",\"io\":[\"pwm1\",\"pwm2\",\"pwm3\"],\"io_type\":\"output\",\"device\":\"pwm_output\",\"uuid\":\"5a81f424aaf8d1e951ae78d270668337\",\"ip\":\"255.255.255.255\"}";

void setup()
{
  Serial.begin(9600);
  while (!Serial)
  {
    continue;
  }
  EEPROM.begin(512);

  Serial.println("\n\n");
  clearEEPROM();
  createDummyJsonObject();
  writeJsonToEEPROM();
  readJsonFromEEPROM();
}

void createDummyJsonObject()
{
  jsonObject = &jsonBuffer.parseObject(dummyJson);
  if (!jsonObject->success())
  {
    Serial.println("jsonBuffer.parseObject() failed");
    return;
  }
  else
  {
    Serial.println("JSON object generated from dummy string");
    jsonObject->prettyPrintTo(Serial);
    Serial.println("\n\n");
  }
}

void loop()
{
  // not used
}

void clearEEPROM()
{
  for (int i = 0; i < 512 + 1; i++)
  {
    EEPROM.write(i, 0);
  }
  EEPROM.commit();
}

void writeJsonToEEPROM()
{
  String jsonStr;
  jsonObject->printTo(jsonStr);

  for (int i = 0; i < jsonStr.length(); ++i)
  {
    EEPROM.write(i, jsonStr[i]);
  }

  EEPROM.write(jsonStr.length(), byte(0));
  EEPROM.commit();
}

void readJsonFromEEPROM()
{
  String jsonStr;

  for (int i = 0; i < 512; ++i)
  {
    char c = char(EEPROM.read(i));
    if (c != 0)
    {
      jsonStr += c;
      delay(1);
    }
    else
    {
      break;
    }
  }
  Serial.println(jsonStr);

  char charBuf[jsonStr.length()];
  jsonStr.toCharArray(charBuf, jsonStr.length());
  config = &jsonBuffer.parseObject(charBuf);

  if (!config->success())
  {
    Serial.println("jsonObject.parseObject() failed ");
    return;
  }
  else
  {
    // Never reaches this point! 
    Serial.println("\nJSON object generated from EEPROM data");
    config->prettyPrintTo(Serial);
    Serial.println("\n\n");
  }
}
Noel Drew
  • 51
  • 1
  • 7

2 Answers2

1

The size allocated for charBuf should be jsonStr.length() + 1 because you need space for a string terminator. Therefore you should also add something like charBuf[jsonStr.length()] = '\0'; to provide that string terminator:

int const jsonStringLengh = jsonStr.length();
char charBuf[jsonStringLengh + 1];
jsonStr.toCharArray(charBuf, jsonStringLengh);
charBuf[jsonStringLengh] = '\0';
Adrian W
  • 4,563
  • 11
  • 38
  • 52
  • Ahh ok thanks for that. I've added that but still no luck. Have I made the correct changes? `char charBuf[jsonStr.length() + 1];` `jsonStr.toCharArray(charBuf, jsonStr.length() + 1);` `charBuf[jsonStr.length()] = '\0';` – Noel Drew Jul 11 '18 at 12:22
  • The buffer needs one byte more which must be set to `'\0'`. The number of chars you feed into `toCharArray()` does not change. I have updated the response to make this more clear. And indeed, it is not possible to format comments to display code nicely. – Adrian W Jul 11 '18 at 13:08
  • Ahh ok I see. Thanks. I think I was getting confused with number of chars to copy and the array index at which to write to. I actually solved it by creating another StaticJsonBuffer (I can only assume they get cleared after use) but I've learnt something about strings and char arrays so thanks – Noel Drew Jul 11 '18 at 14:17
0

Ok so this solved it. All I had to do was create a new StaticJsonBuffer for the second string parse. For anyone who is having a similar issue here's the working code.

    #include <ArduinoJson.h>
#include <EEPROM.h>

StaticJsonBuffer<512> jsonBuffer;
JsonObject *jsonObject;
JsonObject *config;

String dummyJson = "{\"name\":\"RGB LED 1\",\"io\":[\"pwm1\",\"pwm2\",\"pwm3\"],\"io_type\":\"output\",\"device\":\"pwm_output\",\"uuid\":\"5a81f424aaf8d1e951ae78d270668337\",\"ip\":\"255.255.255.255\"}";

void setup()
{
  Serial.begin(9600);
  while (!Serial)
  {
    continue;
  }
  EEPROM.begin(512);

  clearEEPROM();
  createDummyJsonObject();
  writeJsonToEEPROM();
  readJsonFromEEPROM();
}

void createDummyJsonObject()
{
  jsonObject = &jsonBuffer.parseObject(dummyJson);
  if (!jsonObject->success())
  {
    Serial.println("jsonBuffer.parseObject() failed");
    return;
  }
  else
  {
    Serial.println("JSON object generated from dummy string");
  }
}

void loop()
{
  // not used
}

void clearEEPROM()
{
  for (int i = 0; i < 512 + 1; i++)
  {
    EEPROM.write(i, 0);
  }
  EEPROM.commit();
}

void writeJsonToEEPROM()
{
  String jsonStr;
  jsonObject->printTo(jsonStr);
  for (int i = 0; i < jsonStr.length(); ++i)
  {
    EEPROM.write(i, jsonStr[i]);
  }
  EEPROM.write(jsonStr.length(), byte(0));
  EEPROM.commit();
}

void readJsonFromEEPROM()
{
  String jsonStr;
  for (int i = 0; i < 512; ++i)
  {
    char c = char(EEPROM.read(i));
    if (c != 0)
    {
      jsonStr += c;
      delay(1);
    }
    else
    {
      break;
    }
  }

  StaticJsonBuffer<512> jsonBuffer2;
  config = &jsonBuffer2.parseObject(jsonStr);
  if (!config->success())
  {
    Serial.println("jsonObject.parseObject() failed ");
    return;
  }
  else
  {
    Serial.println("\nJSON object generated from EEPROM data");
    config->prettyPrintTo(Serial);
    Serial.println("\n\n");
  }
}
Noel Drew
  • 51
  • 1
  • 7