Как сделать так, чтобы эта программа занимала меньше памяти?
Я измеряю время разрядки аккумулятора. 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);
}
@Hobbes, 👍-1
Обсуждение2 ответа
Ваши начальные объёмы памяти довольно скромны. Использование памяти программой не увеличится во время выполнения, и я не вижу никаких динамических выделений из оперативной памяти, кроме стека вызовов, конечно же, который ведёт себя корректно — до тех пор, пока не появится достаточно вложенных вызовов функций, чтобы перезаписать некоторые статически выделенные области (обычно глобальные). Без этого у вас вряд ли закончится память.
С чего начать: 1) В стандартной библиотеке Arduino printf() и подобные функции не реализуют вывод чисел с плавающей точкой. Не знаю, насколько хорошо они себя ведут, если вы попытаетесь. 2) Возможно, вы перезаписываете какую-то переменную — например, адрес или указатель?
Вы говорите, что протестировали части, но вся программа даёт сбой. Я бы попробовал добавлять по одной части за раз и тестировать столько, сколько нужно, пока что-то не даст сбой. Затем повторите то же самое, добавляя части в другом порядке. Сбой происходит на той же добавленной части или на другой?
Другой подход заключается в замене каждой функции по одной заглушкой — функцией с тем же именем и параметрами, которая не делает ничего, кроме возврата допустимого значения (если таковое требуется). Или возвращает «false» или недопустимое значение, если вы подозреваете, что именно это и вызывает ошибку. Заменяйте одну функцию заглушкой, пока программа не «заработает» (разумеется, без того, что эта функция должна делать) с заглушкой, но не завершится с полным кодом функции. Теперь можно начинать добавлять заглушки к основным разделам кода этой функции, пока программа снова не заработает. Снова сужайте область заглушки и т.д., пока не найдёте проблемные операторы.
String getTime(void) {
snprintf(buffer, sizeof(buffer)-1,"%02d:%02d:%02d", hour(), minute(), second());
return buffer;
}
Если результат уже находится в буфере, зачем возвращать его как строку? Это вполне может привести к фрагментации кучи.
Я настоятельно рекомендую вам переписать весь код без использования типа String.
- Нужна помощь с библиотекой U8GLIB
- Serial.println использует слишком много памяти (не строки)
- Помогите уменьшить размер скетча!
- Проблемы с памятью? dtostr() и strcat()
- Стирание 1 байта внешней Flash памяти (winbond)
- Почему Serial.print(1) требует на 228 байт больше программной памяти по сравнению с Serial.print((char)(48+1))?
- deserializeJson() не удалось: NoMemory при отправке последовательного json с использованием ArduinoJson
- Отладка максимальной емкости
Этот вопрос похож на вопрос: [Как сделать, чтобы эта функция занимала меньше памяти?](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