Arduino часто зависает
Я использую Arduino Nano для программирования автоматического дезинфицирующего средства для рук с термометром. Но после запуска в течение некоторого времени плата зависает, и только перезапуск может решить проблему. При проверке последовательного монитора он внезапно перестает работать.
Программа включает в себя считывание расстояния с ультразвукового датчика и температуры с ИК-термометра (расстояние и температура окружающей среды считываются непрерывно, и последовательный монитор будет постоянно отображать значения). Случайным образом, когда температура считывается и отображается на OLED-экране, плата зависает (плата зависает только в это время), и перезапуск может заставить ее работать еще 1 раз, а затем снова зависнуть.
Вопрос 1 Это как-то связано с кодом или это аппаратная проблема?
Вопрос 2 Вызывает ли клонированная версия NANO такую проблему? (Я использую версию клона)
Это мой код:
#include <Wire.h>
#include <Adafruit_MLX90614.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
Adafruit_SSD1306 display(-1);
//Adafruit_SSD1306 дисплей (128, 64 и провод, -1);
#define trigPin 12 // Триггерный штифт
#define echoPin 11 // echo pin
float roomTemp; // температура
float objectTemp, stemp; // температура объекта
int readcount = 0;
float threshold= 3.5 ;
int maximumRange = 15; // Необходимый максимальный диапазон
int minimumRange = 3; // Необходимый минимальный диапазон
long duration, distance; // Длительность используется для вычисления расстояния
int dtime;
unsigned long rememTime;
// кодирование двигателя
int set_time;
float distance_cm;
unsigned long ultra_time;
int set_cm = 20;
int motor = 2; // Выход для привода двигателя
int flag = 0;
void setup() {
Serial.begin(9600);// инициализировать последовательную связь со скоростью 9600 бит в секунду:
pinMode (trigPin, OUTPUT);
pinMode (echoPin, INPUT);
pinMode(motor, OUTPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C) ;
delay(200);
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop() {
// кодирование датчика температуры
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
//Вычислить расстояние (в см) по скорости звука.
distance= duration * 0.034/2;
// считывание объекта и температуры окружающей среды
objectTemp = threshold + mlx.readObjectTempC() ;
roomTemp = mlx.readAmbientTempC() ;
// печать на последовательный порт
Serial.println("Object:" + String(objectTemp) + ", Ambient:" + String(roomTemp));
Serial.println(distance);
// дисплей на OLED
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 25);
display.print("Dis:" + String(distance) + "cm");
display.setCursor(65, 25);
display.print("Room:" + String(roomTemp).substring(0, 4) + "C");
display.display();
display.setTextSize(2);
display.setCursor(0, 0);
if (distance < minimumRange) {
display.print("TOO CLOSE!");
}
if ((distance >= minimumRange) && (distance <= maximumRange)) {
if (readcount == 5) { // после чтения 5 раз подряд
disptemp();
} else {
display.print("HOLD ON"); // находясь в зоне действия, попросите пользователя удерживать позицию
stemp = stemp + objectTemp;
readcount++; // до прибл. 5 х 200 мс = 1 сек .
}
} else { // если пользователь находится вне зоны действия, сбросить расчет
dtime = 100;
readcount = 0;
stemp = 0;
}
display.display();
delay(dtime);
Serial.println("count :"+String(readcount));
}
void disptemp() {
objectTemp = stemp / 5; // получить среднее значение temp
display.setTextSize(1);
display.print("YOUR TEMP:");
display.setTextSize(2);
display.setCursor(60,5);
display.print(String(objectTemp).substring(0, 4) + "C");
display.display();
readcount = 0;
stemp = 0;
if (objectTemp >= 38) {
play_alert();
} else {
play_ok();
}
ultrasonicRead();
dtime = 5000; // подождите 5 секунд.
}
void play_ok() { // воспроизведение трех последовательных нот при температуре объекта ниже 37,5 °C
tone(3, 600, 1000); // контакт, частота, длительность
delay(200);
tone(3, 750, 500);
delay(100);
tone(3, 1000, 500);
delay(200);
noTone(3);
}
void play_alert() { // звуковой сигнал 3x при температуре объекта> = 37.5C
tone(3, 2000, 1000);
delay(1000);
tone(3, 3000, 1000);
delay(1000);
tone(3, 4000, 1000);
delay(1000);
noTone(3);
}
void ultrasonicRead() {
if (distance < set_cm) {
digitalWrite(motor, HIGH);
Serial.println("Motor On");
delay(500);
digitalWrite(motor, LOW);
Serial.println("motor off");
}
}
@Roshan Mathews, 👍1
Обсуждение2 ответа
Любое выделение / удаление или перераспределение памяти [1] во время выполнения приведет к тому, что куча (пул памяти, из которого производятся эти выделения) будет расширяться до тех пор, пока куча и стек не вырастут друг к другу настолько, чтобы столкнуться, и в этот момент один перезапишет часть другого непредсказуемые результаты.
Строки выделяются из кучи. Строки, которые строятся понемногу, как в случае:
Serial.println("Object:" + String(objectTemp) + ", Ambient:" + String(roomTemp));
причина выделения несколько распределений / отмен.
В качестве простого в реализации теста закомментируйте операторы, использующие объект String, и просто распечатайте необработанные данные. Ваша программа должна работать без сбоев.
Чтобы воссоздать то, что вы сейчас делаете со строками, я бы предложил использовать snprintf()
для создания выходного буфера фиксированного размера, который может быть локальным символьным массивом (выделенным в стеке и освобожденным при выходе содержащей функции). Это должно быть так же стабильно, как и сокращенный эксперимент, который я предложил выше.
[1] * Если только выделенная память не будет выделена самой последней-первой. И даже это предполагает предвидение используемого алгоритма распределения памяти, а значит, непереносимо).
Один из способов обойти это - написать собственный распределитель / деаллокатор памяти, который имеет предварительно назначенный пул блоков фиксированной длины, один из которых он предоставляет для любого запроса памяти любого размера, если:
- есть свободный блок для выделения; и
- запрошенный размер может поместиться в (пространстве данных) предварительно назначенного блока.
почему бы не использовать Serial.print()?, @Juraj
Серия Serial.print() вполне приемлема; я предложил snprintf() как семантически более близкую к конкатенации строк, как это было написано в OP., @JRobert
В дополнение к некоторым из вышеперечисленных комментариев;
Ваш Nano может не поддерживать сторожевой пес. Пожалуйста, проверьте этот пост на форуме Arduino
Если вы ищете "Есть много маленьких программ ..." Ниже я просто опубликую функции, которые я использую для получения информации о свободной памяти, куче и стеке.
int freeRam() { extern int __heap_start, *__brkval; int v; return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval); } uint8_t * stack() { uint8_t * ptr; ptr = (uint8_t *)malloc(4); free(ptr); return ((uint8_t *)(SP)); } uint8_t * heap() { uint8_t * ptr; ptr = (uint8_t *)malloc(4); free(ptr); return(ptr); }
"может не поддерживать сторожевого пса" - пожалуй, не самый лучший способ выразить это. [Optiboot очищается](https://github.com/arduino/ArduinoCore-avr/blob/1.8.3/bootloaders/optiboot/optiboot.c#L289 ) "MCUSR" и поэтому "WDRF", то есть вы не можете сказать, что сторожевой таймер был * причиной* сброса, просто проверив "MCUSR". В качестве механизма сброса он в остальном работает совершенно нормально. Есть несколько способов в некоторой степени решить проблему очистки "MCUSR", помимо просто исправления загрузчика., @timemage
- Sim800L Проблемы с загрузкой, HTTPACTION возвращает 0, 302, 701
- Я получаю сообщение об ошибке?
- avrdude ser_open() can't set com-state
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Какое максимальное энергопотребление Arduino Nano 3.0?
- Как навсегда изменить скорость передачи данных ESP8266 (12e)?
- Питание светодиодной ленты - Сколько ампер?
- Arduino nano как клавиатура HID
Удалите все строковые объекты из вашего кода., @Majenko
Возможно, вы захотите использовать сторожевой таймер, чтобы заставить плату автоматически сбрасываться для такого рода вещей. Чтобы было ясно, я ** не ** предлагаю это как * решение *; вы все равно должны решить реальную проблему. Но сторожевой пес часто используется для добавления уровня защиты от замерзания при развертывании., @timemage
Это должен быть веселый проект. Так как вы не дали нам схему (не вьющуюся вещь) Я могу только догадываться. Мне кажется, вы используете интерфейс I2C и, возможно, зависите от подтягивающих резисторов. Кроме того, ранняя библиотека 1 wire зависала, если устройство пропускало ACK. Есть много небольших программ, которые отображают объем свободной памяти, включают его в свой код и смотрят, является ли он стабильным или медленно утекает память., @Gil
Вы можете поместить следующие две строки в верхней части скетча:
void got_to_line(int n) {Serial.print(F("[Line:")); Serial.print(n); Serial.println(']'); Serial.flush();}
и#define GOT_HERE() got_to_line(__LINE__)
Затем вы можете засорить свой кодGOT_HERE();
перемещая их, пока не найдете точную строку (или строки), где происходит замораживание. Если вы это сделаете, дайте нам знать точную линию (или линии), на которой он замерзает., @timemage