deserializeJson() не удалось: NoMemory при отправке последовательного json с использованием ArduinoJson

Привет всем! У меня есть скетч, который занимает 99% места на моем диске, так как я использую Arduino Nano.

Хотя все в моем коде подходит как есть, похоже, есть проблема с отправкой строки json через Serial, так как она возвращается с сообщением "deserializeJson() failed: NoMemory". Я также уже использовал ArduinoJson Assistant.

Моя строка json выглядит так: {"hardd": {"warrDays": "1265","temp": "102","hddST": "18TB","hddSU": "4TB","hddPB": "70","fan": "4200","date": "Пт Мар 11TH 2024","time": "10:45pm"}}

Обратите внимание, что iconImages.h содержит только код, в котором изображения преобразуются в байт-код, подобный этому (всего их 12):

const unsigned char hdd [] PROGMEM = {
    // 'hdd (1), 24x24px
    0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x5f, 0xff, 0xfa, 0x7f, 0xff, 0xfe, 0x7f, 0x00, 0xfe, 0x7e, 
    0x7e, 0x7e, 0x79, 0xcf, 0x9e, 0x7b, 0x0f, 0xde, 0x76, 0x7f, 0xee, 0x64, 0xff, 0xe6, 0x6d, 0xe7, 
    0xf6, 0x69, 0x99, 0xf6, 0x69, 0xbd, 0xf6, 0x6b, 0xbd, 0xf6, 0x6f, 0x99, 0xf6, 0x6f, 0xc3, 0xf6, 
    0x67, 0xff, 0xe6, 0x77, 0xff, 0xee, 0x73, 0xf3, 0xce, 0x79, 0xc7, 0x9e, 0x1c, 0x0e, 0x3e, 0x0f, 
    0x00, 0xfe, 0x66, 0x07, 0xfe, 0x70, 0x3f, 0xfe, 0x72, 0x7f, 0xfe, 0x76, 0x0d, 0x5e, 0x70, 0xed, 
    0x1e, 0x5f, 0xed, 0x5a, 0x3f, 0xef, 0xfc, 0x00, 0x00, 0x00
};

Вот мой код. Если у кого-то есть предложения, как сократить объем дискового пространства/памяти, чтобы не возникало ошибок при отправке строки JSON, это было бы здорово!

Мой код:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_EMC2101.h>
#include <ArduinoJson.h>
#include "iconImages.h"

#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMono9pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>

#define hdd_SCREEN_ADDRESS 0x3C
#define apple_SCREEN_ADDRESS 0x3C
#define dell_SCREEN_ADDRESS 0x3C
#define TCAADDR 0x70

//                           W    H
Adafruit_SSD1306 hdd_display(128, 32, &Wire, -1);
Adafruit_SSD1306 apple_display(128, 32, &Wire, -1);
Adafruit_SSD1306 dell_display(128, 32, &Wire, -1);

Adafruit_EMC2101 emc2101;
char character;

//For timer=========================================
const long Minutes = 1;
const long _Interval = (Minutes * 60 * 1000);
unsigned long previousMillis = 0;
//==================================================

//For HDD Stats=====================================
int i = 0;
const char* hdd_warrDays  = "";
const char* hdd_temp      = "";
const char* hdd_hddST     = "";
const char* hdd_hddSU     = "";
const char* hdd_hddPB     = "";
const char* hdd_fan       = "";
//==================================================

//For Apple Stats===================================
const char* apple_os      = "";
const char* apple_hddST   = "";
const char* apple_hddSU   = "";
const char* apple_hddPB   = "";
const char* apple_ip      = "";
const char* apple_uptime  = "";
//==================================================

//For Dell Stats====================================
const char* dell_os     = "";
const char* dell_hddST  = "";
const char* dell_hddSU  = "";
const char* dell_hddPB  = "";
const char* dell_ip     = "";
const char* dell_uptime = "";
//==================================================

//For Date & Time ==================================
const char* _date = "";
const char* _time = "";
//==================================================

void setup() {
  Serial.begin(9600);

  if (!hdd_display.begin(SSD1306_SWITCHCAPVCC, hdd_SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }

  hdd_display.clearDisplay();
  hdd_display.display();
  hdd_display.setTextColor(WHITE);
  /*
    if(!apple_display.begin(SSD1306_SWITCHCAPVCC, apple_SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;);
    }

    apple_display.clearDisplay();
    apple_display.display();
    apple_display.setTextColor(WHITE);

    if(!dell_display.begin(SSD1306_SWITCHCAPVCC, dell_SCREEN_ADDRESS)) {
      Serial.println(F("SSD1306 allocation failed"));
      for(;;);
    }

    dell_display.clearDisplay();
    dell_display.display();
    dell_display.setTextColor(WHITE);
  */
}

void loop() {
  unsigned long currentMillis = millis();

  while (Serial.available()) {
    String input_content = Serial.readString();

    if (input_content.length() > 2) {
      Serial.println(input_content);
      readAndParseJson(input_content);
      input_content = "";
    }
  }

  if (currentMillis - previousMillis >= _Interval) {
    previousMillis = currentMillis;

    Serial.println("previousMillis");

    if (i == 0 || i == 1 || i == 2 || i == 3) {
      i++;
    } else if (i == 4) {
      i = 0;
    }
  }

  doHDD(i);
  //doApple(i);
  //doDell(i);

  delay(100);
}

String getSplitValues(String data, char separator, int index) {
  int found = 0;
  int strIndex[] = { 0, -1 };
  int maxIndex = data.length() - 1;

  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
    }
  }

  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

void scanI2C() {
  for (uint8_t t = 0; t < 8; t++) {
    Wire.beginTransmission(TCAADDR);
    Wire.write(1 << t);
    Wire.endTransmission();
    Serial.print("TCA Port #");
    Serial.println(t);

    for (uint8_t addr = 0; addr <= 127; addr++) {
      if (addr == TCAADDR) continue;

      Wire.beginTransmission(addr);

      if (!Wire.endTransmission()) {
        Serial.print("Found I2C 0x");
        Serial.println(addr, HEX);
      }
    }
  }
}

void readAndParseJson(String json) {
  //https://arduinojson.org/v7/assistant
  StaticJsonDocument<512> doc;
  DeserializationError error = deserializeJson(doc, json);
  json = "";

  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());

    return;
  }

  if (doc.containsKey("hardd")) {
    Serial.println("hdd");
    JsonObject hdd = doc[F("hardd")];
    hdd_warrDays = hdd[F("warrDays")];
    hdd_temp = hdd[F("temp")];
    hdd_hddST = hdd[F("hddST")];
    hdd_hddSU = hdd[F("hddSU")];
    hdd_hddPB = hdd[F("hddPB")];
    hdd_fan = hdd[F("fan")];
    _date = hdd[F("date")];
    _time = hdd[F("time")];
  } else if (doc.containsKey("apple")) {
    Serial.println("apple");
    JsonObject apple = doc[F("apple")];
    apple_os = apple[F("OS")];
    apple_hddST = apple[F("hddST")];
    apple_hddSU = apple[F("hddSU")];
    apple_hddPB = apple[F("hddPB")];
    apple_ip = apple[F("ip")];
    apple_uptime = apple[F("uptime")];
  } else if (doc.containsKey("dell")) {
    Serial.println("dell");
    JsonObject dell = doc[F("dell")];
    dell_os = dell[F("OS")];
    dell_hddST = dell[F("hddST")];
    dell_hddSU = dell[F("hddSU")];
    dell_hddPB = dell[F("hddPB")];
    dell_ip = dell[F("ip")];
    dell_uptime = dell[F("uptime")];
  }
}

void doHDD(int num) {
  hdd_display.clearDisplay();
  hdd_display.drawBitmap(0, 0, hdd, 24, 30, WHITE);

  if (num == 0) {
    //Show tempature
    hdd_display.drawBitmap(25, 0, thermometer, 24, 30, WHITE);
    hdd_display.setFont(&FreeSans9pt7b);
    hdd_display.setCursor(45, 15);
    hdd_display.print(hdd_temp);
    //hdd_display.print("100");
    hdd_display.setTextSize(1);
    hdd_display.setCursor(45, 29);
    hdd_display.print("tempature");
    hdd_display.drawBitmap(75, 0, degreesf, 24, 30, WHITE);
  } else if (num == 1) {
    //Show warranty
    hdd_display.setFont();
    hdd_display.drawBitmap(30, 1, warranty, 24, 30, WHITE);
    hdd_display.setTextSize(1);
    hdd_display.setCursor(60, 3);
    //hdd_display.print(341);
    hdd_display.print(hdd_warrDays);
    hdd_display.print(" day's");
    hdd_display.setCursor(60, 13);
    hdd_display.print("left of");
    hdd_display.setCursor(60, 23);
    hdd_display.print("warranty");
  } else if (num == 2) {
    //Show Hdd stats
    hdd_display.setFont();
    hdd_display.drawBitmap(30, 1, hddSpace, 24, 30, WHITE);
    hdd_display.setTextSize(1);
    hdd_display.setCursor(60, 3);
    //hdd_display.print("5.6TB free");
    hdd_display.print(hdd_hddST);
    hdd_display.print(" free");
    hdd_display.setCursor(60, 13);
    //hdd_display.print("of 18TB");
    hdd_display.print("of ");
    hdd_display.print(hdd_hddSU);

    //drawProgressbar(60, 25, 60, 7, atoi(hdd_hddPB));
    int progress = atoi(hdd_hddPB) > 100 ? 100 : atoi(hdd_hddPB);
    progress = progress < 0 ? 0 : progress;
    float bar = ((float)(60 - 1) / 100) * progress;
    hdd_display.drawRect(60, 25, 60, 7, WHITE);
    hdd_display.fillRect(60 + 2, 25 + 2, bar, 7 - 4, WHITE);
  } else if (num == 3) {
    //Show fan rpms
    hdd_display.setFont();
    hdd_display.drawBitmap(30, 1, fanSpeed, 24, 30, WHITE);
    hdd_display.setTextSize(1);
    hdd_display.setCursor(60, 3);
    hdd_display.print("Fan running");
    hdd_display.setCursor(60, 13);
    hdd_display.print("@ ");
    //hdd_display.print("4,102");
    hdd_display.print(hdd_fan);
    hdd_display.print(" rpm");
  } else if (num == 4) {
    //Show date/time
    hdd_display.setFont();
    hdd_display.drawBitmap(30, 1, timeAndDate, 24, 30, WHITE);
    hdd_display.setTextSize(1);
    hdd_display.setCursor(60, 3);
    //hdd_display.print("10:34pm on");
    hdd_display.print(_time);
    hdd_display.print(" on ");
    hdd_display.setCursor(60, 13);
    //hdd_display.print("Fri Sep");
    hdd_display.print(getSplitValues(_date, ' ', 0));
    hdd_display.print(" ");
    hdd_display.print(getSplitValues(_date, ' ', 1));
    hdd_display.setCursor(60, 23);
    //hdd_display.print("22nd 2024");
    hdd_display.print(getSplitValues(_date, ' ', 2));
    hdd_display.print(" ");
    hdd_display.print(getSplitValues(_date, ' ', 3));
  }

  hdd_display.display();
}

void doApple(int num) {
  String _apple_tmp[3];
  apple_display.clearDisplay();
  apple_display.drawBitmap(0, 0, apple, 24, 30, WHITE);

  if (num == 0) {
    //Show OS
    apple_display.setFont();
    apple_display.drawBitmap(30, 1, runningOS, 24, 30, WHITE);
    apple_display.setTextSize(1);
    apple_display.setCursor(60, 3);
    //apple_display.print("macOS 12.9");
    apple_display.print(getSplitValues(apple_os, ' ', 0));
    apple_display.setCursor(60, 13);
    //apple_display.print("Monterey");
    apple_display.print(getSplitValues(apple_os, ' ', 1));
    apple_display.setCursor(60, 23);
    apple_display.print(getSplitValues(apple_os, ' ', 2));
  } else if (num == 1) {
    //Show Hdd stats
    apple_display.setFont();
    apple_display.drawBitmap(30, 1, hddSpace, 24, 30, WHITE);
    apple_display.setTextSize(1);
    apple_display.setCursor(60, 3);
    //apple_display.print("5.6TB free");
    apple_display.print(apple_hddST);
    apple_display.print(" free");
    apple_display.setCursor(60, 13);
    apple_display.print("of ");
    //apple_display.print("of 18TB");
    apple_display.print(apple_hddSU);

    //drawProgressbar(60, 25, 60, 7, atoi(apple_hddPB));
    int progress = atoi(apple_hddPB) > 100 ? 100 : atoi(apple_hddPB);
    progress = progress < 0 ? 0 : progress;
    float bar = ((float)(60 - 1) / 100) * progress;
    apple_display.drawRect(60, 25, 60, 7, WHITE);
    apple_display.fillRect(60 + 2, 25 + 2, bar, 7 - 4, WHITE);
  } else if (num == 2) {
    //Show IP address
    apple_display.setFont();
    apple_display.drawBitmap(30, 1, ethernet, 24, 30, WHITE);
    apple_display.setTextSize(1);
    apple_display.setCursor(60, 3);
    //apple_display.print("192.168.1.123");
    apple_display.print("IP Address");
    apple_display.setCursor(60, 13);
    apple_display.print("...");
    apple_display.print(getSplitValues(apple_ip, '.', 2));
    apple_display.print(".");
    apple_display.print(getSplitValues(apple_ip, '.', 3));
  } else if (num == 3) {
    //Show uptime
    apple_display.setFont();
    apple_display.drawBitmap(30, 1, uptime, 24, 30, WHITE);
    apple_display.setTextSize(1);
    apple_display.setCursor(60, 3);
    //apple_display.print("35 days 17:14");
    apple_display.print("Uptime");
    apple_display.setCursor(60, 13);
    apple_display.print(getSplitValues(apple_uptime, ' ', 0));
    apple_display.print(" ");
    apple_display.print(getSplitValues(apple_uptime, ' ', 1));
    apple_display.setCursor(60, 23);
    apple_display.print(getSplitValues(apple_uptime, ' ', 2));
  } else if (num == 4) {
    //Show date/time
    apple_display.setFont();
    apple_display.drawBitmap(30, 1, timeAndDate, 24, 30, WHITE);
    apple_display.setTextSize(1);
    apple_display.setCursor(60, 3);
    //apple_display.print("10:34pm on");
    apple_display.print(_time);
    apple_display.print(" on ");
    apple_display.setCursor(60, 13);
    //apple_display.print("Fri Sep");
    apple_display.print(getSplitValues(_date, ' ', 0));
    apple_display.print(" ");
    apple_display.print(getSplitValues(_date, ' ', 1));
    apple_display.setCursor(60, 23);
    //apple_display.print("22nd 2024");
    apple_display.print(getSplitValues(_date, ' ', 2));
    apple_display.print(" ");
    apple_display.print(getSplitValues(_date, ' ', 3));
  }

  apple_display.display();
}

void doDell(int num) {
  dell_display.clearDisplay();
  dell_display.drawBitmap(0, 0, dell, 24, 30, WHITE);

  if (num == 0) {
    //Show OS
    dell_display.setFont();
    dell_display.drawBitmap(30, 1, runningOS, 24, 30, WHITE);
    dell_display.setTextSize(1);
    dell_display.setCursor(60, 3);
    //apple_display.print("Windows 11");
    dell_display.print(getSplitValues(dell_os, ' ', 0));
    dell_display.setCursor(60, 13);
    //apple_display.print("Monterey");
    dell_display.print("V. ");
    dell_display.print(getSplitValues(dell_os, ' ', 1));
    dell_display.print(dell_os);
  } else if (num == 1) {
    //Show Hdd stats
    dell_display.setFont();
    dell_display.drawBitmap(30, 1, hddSpace, 24, 30, WHITE);
    dell_display.setTextSize(1);
    dell_display.setCursor(60, 3);
    //dell_display.print("5.6TB free");
    dell_display.print(dell_hddST);
    dell_display.print(" free");
    dell_display.setCursor(60, 13);
    //dell_display.print("of 18TB");
    dell_display.print("of ");
    dell_display.print(dell_hddSU);

    //drawProgressbar(60, 25, 60, 7, atoi(dell_hddPB));
    int progress = atoi(dell_hddPB) > 100 ? 100 : atoi(dell_hddPB);
    progress = progress < 0 ? 0 : progress;
    float bar = ((float)(60 - 1) / 100) * progress;
    dell_display.drawRect(60, 25, 60, 7, WHITE);
    dell_display.fillRect(60 + 2, 25 + 2, bar, 7 - 4, WHITE);
  } else if (num == 2) {
    //Show IP address
    dell_display.setFont();
    dell_display.drawBitmap(30, 1, ethernet, 24, 30, WHITE);
    dell_display.setTextSize(1);
    dell_display.setCursor(60, 3);
    //dell_display.print("192.168.1.123");
    dell_display.print("IP Address");
    dell_display.setCursor(60, 13);
    dell_display.print("...");
    dell_display.print(getSplitValues(apple_ip, '.', 2));
    dell_display.print(".");
    dell_display.print(getSplitValues(apple_ip, '.', 3));
  } else if (num == 3) {
    //Show uptime
    dell_display.setFont();
    dell_display.drawBitmap(30, 1, uptime, 24, 30, WHITE);
    dell_display.setTextSize(1);
    dell_display.setCursor(60, 3);
    //dell_display.print("35 days 17:14");
    dell_display.print("Uptime");
    dell_display.setCursor(60, 13);
    dell_display.print(getSplitValues(apple_uptime, ' ', 0));
    dell_display.print(" ");
    dell_display.print(getSplitValues(apple_uptime, ' ', 1));
    dell_display.setCursor(60, 23);
    dell_display.print(getSplitValues(apple_uptime, ' ', 2));
  } else if (num == 4) {
    //Show date/time
    dell_display.setFont();
    dell_display.drawBitmap(30, 1, timeAndDate, 24, 30, WHITE);
    dell_display.setTextSize(1);
    dell_display.setCursor(60, 3);
    //dell_display.print("10:34pm on");
    dell_display.print(_time);
    dell_display.print(" on ");
    dell_display.setCursor(60, 13);
    //dell_display.print("Fri Sep");
    dell_display.print(getSplitValues(_date, ' ', 0));
    dell_display.print(" ");
    dell_display.print(getSplitValues(_date, ' ', 1));
    dell_display.setCursor(60, 23);
    //dell_display.print("22nd 2024");
    dell_display.print(getSplitValues(_date, ' ', 2));
    dell_display.print(" ");
    dell_display.print(getSplitValues(_date, ' ', 3));
  }

  dell_display.display();
}

Было бы здорово, если бы кто-нибудь посмотрел код и предложил области, где я мог бы сэкономить немного памяти, чтобы json работал.

, 👍0

Обсуждение

Эта строка, должно быть, неверна: 'Serial.println("previousMillis");' Убрав кавычки, вы сэкономите несколько байт. Также я бы использовал макрос F() для большего количества ваших строк. Есть даже случай заменить использование строк на массивы символов. Но это требует много работы. И еще один момент: я обнаружил, что всякий раз, когда я работаю близко к ограничениям памяти с Arduino IDE, код начинает вести себя непредсказуемо. Я видел это до достижения 99% доступности., @Lee-xp


1 ответ


Лучший ответ:

3

Я почти уверен, что вы используете ArduinoJson 7, потому что ArduinoJson 6 не вернет NoMemory в таких условиях.

На 8-битных микроконтроллерах (таких как Arduino Nano) я рекомендую использовать версию 6, поскольку ее фиксированная стратегия выделения памяти позволяет коду быть намного меньше и может работать без выделения кучи благодаря StaticJsonDocument.

Используйте ArduinoJson Assistant для вычисления правильного размера вашего StaticJsonDocument. В вашем случае он предлагает размер 192 байта.

Вы можете сэкономить много оперативной памяти, передавая поток напрямую в deserializeJson() вместо загрузки его в String.

Избегание containsKey() может уменьшить размер вашей программы (и немного ускорить ее). Вместо этого извлеките JsonObject и проверьте, является ли он пустым.

Остерегайтесь, макрос F() не дублирует строки. По этой причине вам, вероятно, следует использовать обычные строки RAM при извлечении из JsonDocument.

Другие приемы экономии памяти можно найти в статье Как уменьшить использование памяти?.

Наконец, вы можете значительно сократить размер программы, удалив ведение журнала и заменив повторяющиеся шаблоны циклами.

,

Спасибо за предложения, Бенуа. Я попробую сделать что-нибудь из этого и посмотреть, получится ли., @StealthRT