Как сделать так, чтобы эта программа занимала меньше памяти?

Я измеряю время разрядки аккумулятора. Arduino Nano управляет OLED-дисплеем SSD1036, платой с двумя реле и платой измерения напряжения/тока INA3221.

Батарея подключена к INA3221, а затем к резистору. Реле в этой схеме позволяет отключать батарею.

Я протестировал каждую часть настройки по отдельности, и всё работает. Когда я объединяю код в одну программу, функция getTime() перестаёт работать. Она должна возвращать время в виде форматированной строки (12:34:56), но вместо этого возвращает пустую строку.

Я переписал эту функцию, чтобы использовать меньше памяти, но этого оказалось недостаточно: когда я запускаю полную программу с функцией времени, изменённой, как указано в связанном вопросе, вызов getTime() по-прежнему возвращает пустую строку.

При компиляции я получаю такой отчет:

Sketch использует 23088 байт (75%) памяти программы. Максимум — 30720 байт. Глобальные переменные используют 806 байт (39%) динамической памяти, оставляя 1242 байта для локальных переменных. Максимальный объём — 2048 байт.

Поскольку небольшие фрагменты этой программы работают, я подозреваю, что мне не хватает памяти. Как мне уместить эту программу в память Nano?

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TimeLib.h>
#include <INA3221.h>
#include <Bounce2.h>

//D4 is relay 1, D5 is relay 2
#define Rel1 4
#define Rel2 5

//buttons
const int inputPin1 = 2;//D2 -stop measurement
const int inputPin2 = 6;//D6 - start measurement
Bounce bouncer1 = Bounce(); 
Bounce bouncer2 = Bounce(); 
int buttonState = LOW;
int stopMeting = 0;
int stopMeting1 = 0;
int stopMeting2 = 0;
float stopVoltage = 1.30;
String stopTijd1;
String stopTijd2;


//SSD1036 OLED
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//timer
unsigned long previousMillis = 0;
const long interval = 1000; //in ms
char buffer[20] = {0};

//INA3221
#define SERIAL_SPEED 115200 
#define PRINT_DEC_POINTS 3 

INA3221 ina_0(INA3221_ADDR42_SDA);

float voltage1 = 0.0;
float current1 = 0.0;
float voltage2 = 0.0;
float current2 = 0.0;
float power1 = 0.0;
float power2 = 0.0;
float energy1 = 0.0;
float energy2 = 0.0;
float dEnergy1 = 0.0;
float dEnergy2 = 0.0;

void setup() {
  Serial.begin(115200);
  Serial.println("Start program");
  //buttons
  pinMode(inputPin1,INPUT_PULLUP);
  bouncer1.attach(inputPin1);
  pinMode(inputPin2,INPUT_PULLUP);
  bouncer2.attach(inputPin2);
  //relay
  pinMode(Rel1,OUTPUT);
  digitalWrite(Rel1,LOW);
  pinMode(Rel2,OUTPUT);
  digitalWrite(Rel2,LOW);

  //oled
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);
  display.display();
  delay(2000);

  //time
  setTime(00,00,00,01,01,2024);

  //INA
  current_measure_init();
  delay(2000);

}

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

  bouncer1.update();
  int debouncedInput1 = bouncer1.read();
  bouncer2.update();
  int debouncedInput2 = bouncer2.read();
  
  if ( bouncer2.changed() ) {
    int debouncedInput2 = bouncer2.read();
    if ( debouncedInput2 == LOW ) {
      startMeting();
    }
  }


  if ( bouncer1.changed() and stopMeting == 0) {
    int debouncedInput1 = bouncer1.read();
    if ( debouncedInput1 == LOW ) {
      eindMeting();
      stopMeting = 1;
    }
  }

  String Tm = getTime();
  voltage1 = ina_0.getVoltage(INA3221_CH1);
  voltage2 = ina_0.getVoltage(INA3221_CH2);

  if (voltage1 < stopVoltage and stopMeting1 == 0) {
    stopMeting1 = 1;
    digitalWrite(Rel1,LOW);
    stopTijd1 = Tm;
  }
    if (voltage2 < stopVoltage and stopMeting2 == 0) {
    stopMeting2 = 1;
    digitalWrite(Rel1,LOW);
    stopTijd2 = Tm;
  }

  if (stopMeting1 == 1 and stopMeting2 == 1) {
    stopMeting = 1;
    eindMeting();
  }

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (stopMeting == 0) {
      updatedisplay(currentMillis);
      }
    }  
  
}
void startMeting(void) {
  digitalWrite(Rel1,HIGH);
  digitalWrite(Rel2,HIGH);
}

void eindMeting(void) {
  digitalWrite(Rel1,LOW);
  digitalWrite(Rel2,LOW);
  setupdisplay();
  display.println("Eind meting");
  String Tm = getTime();
  Serial.println("Eind meting ");
  Serial.print(Tm);Serial.print(";");
  Serial.print(current1);Serial.print(";");
  Serial.print(voltage1);Serial.print(";");
  Serial.print(current2);Serial.print(";");
  Serial.print(voltage2);Serial.print(";");
  Serial.print(dEnergy1);Serial.print(";");
  Serial.print(dEnergy2);Serial.println(";");
  display.print("time "); display.println(Tm);
  display.print("E1 ");display.println(dEnergy1);
  display.print("E2 ");display.println(dEnergy2);
  //display.println(stopTijd1);display.println(stopTijd2);
  display.display();
}

void setupdisplay(void) {
  display.clearDisplay();
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
}

void updatedisplay(unsigned long currentMillis) {
  //INA
  current1 = ina_0.getCurrent(INA3221_CH1) * 1000; 
  voltage1 = ina_0.getVoltage(INA3221_CH1);
  current2 = ina_0.getCurrent(INA3221_CH2) * 1000;
  voltage2 = ina_0.getVoltage(INA3221_CH2);
  power1= current1 * voltage1;
  power2= current2 * voltage2;
  if (stopMeting1 == 0) {
    energy1= energy1 + power1; //in mWs
  }
    if (stopMeting2 == 0) {
    energy2= energy2 + power2; //in mWs
  }
  dEnergy1 = (energy1/3600); //in mWh
  dEnergy2 = (energy2/3600); //in mWh
  String Tm = getTime();
  Serial.print(Tm);Serial.print(";");
  Serial.print(current1);Serial.print(";");
  Serial.print(voltage1);Serial.print(";");
  Serial.print(current2);Serial.print(";");
  Serial.print(voltage2);Serial.print(";");
  Serial.print(dEnergy1);Serial.print(";");
  Serial.print(dEnergy2);Serial.println(";");
  setupdisplay();
  display.print("time ");display.println(Tm);
  display.print(hour());display.print(minute());display.println(second());
  display.print("I1 ");display.println(current1);
  display.print("V1 ");display.println(voltage1);
  display.print("I2 ");display.println(current2);
  display.print("V2 ");display.println(voltage2);
  display.print("E1 ");display.println(dEnergy1);
  display.print("E2 ");display.println(dEnergy2);
  display.display();
}

String getTime(void) {
  snprintf(buffer, sizeof(buffer)-1,"%02d:%02d:%02d", hour(), minute(), second());
  return buffer;
}

void current_measure_init() {
    ina_0.begin(&Wire);
    ina_0.reset();
    ina_0.setShuntRes(100, 100, 100);
}

, 👍-1

Обсуждение

Этот вопрос похож на вопрос: [Как сделать, чтобы эта функция занимала меньше памяти?](https://arduinoprosto.ru/q/96623/how-can-i-make-this-function-take-up-less-memory). Если вы считаете, что это отличается, пожалуйста, отредактируйте вопрос, чётко указав, в чём именно отличие, и/или почему ответы на этот вопрос не помогают решить вашу проблему., @jsotola

Как указано в другом вопросе, изменение вызова getTIme, как предложено в ответе, пока не сделало программу достаточно маленькой., @Hobbes

не повторяйте код, @jsotola

Если вы подозреваете, что проблема связана с памятью, удалите все экземпляры класса String из вашей программы. Вместо этого используйте буферы char и соответствующие функции C. Если и после этого проблема не исчезнет, значит, проблема в чём-то другом., @chrisl

Не похоже на проблему с памятью. Осталось много динамической памяти. Подозреваю, что проблема в функции, которая пытается вернуть массив символов в виде строки, но не уверен, является ли это проблемой на самом деле., @Delta_G

В дополнение к комментарию @Delta_G, buffer уже является глобальным массивом символов, к которому могут получить доступ все функции, нет необходимости возвращать его как строку., @hcheung


2 ответа


2

Ваши начальные объёмы памяти довольно скромны. Использование памяти программой не увеличится во время выполнения, и я не вижу никаких динамических выделений из оперативной памяти, кроме стека вызовов, конечно же, который ведёт себя корректно — до тех пор, пока не появится достаточно вложенных вызовов функций, чтобы перезаписать некоторые статически выделенные области (обычно глобальные). Без этого у вас вряд ли закончится память.

С чего начать: 1) В стандартной библиотеке Arduino printf() и подобные функции не реализуют вывод чисел с плавающей точкой. Не знаю, насколько хорошо они себя ведут, если вы попытаетесь. 2) Возможно, вы перезаписываете какую-то переменную — например, адрес или указатель?

Вы говорите, что протестировали части, но вся программа даёт сбой. Я бы попробовал добавлять по одной части за раз и тестировать столько, сколько нужно, пока что-то не даст сбой. Затем повторите то же самое, добавляя части в другом порядке. Сбой происходит на той же добавленной части или на другой?

Другой подход заключается в замене каждой функции по одной заглушкой — функцией с тем же именем и параметрами, которая не делает ничего, кроме возврата допустимого значения (если таковое требуется). Или возвращает «false» или недопустимое значение, если вы подозреваете, что именно это и вызывает ошибку. Заменяйте одну функцию заглушкой, пока программа не «заработает» (разумеется, без того, что эта функция должна делать) с заглушкой, но не завершится с полным кодом функции. Теперь можно начинать добавлять заглушки к основным разделам кода этой функции, пока программа снова не заработает. Снова сужайте область заглушки и т.д., пока не найдёте проблемные операторы.

,

0
String getTime(void) {
  snprintf(buffer, sizeof(buffer)-1,"%02d:%02d:%02d", hour(), minute(), second());
  return buffer;
}

Если результат уже находится в буфере, зачем возвращать его как строку? Это вполне может привести к фрагментации кучи.

Я настоятельно рекомендую вам переписать весь код без использования типа String.

,