Доброго времени суток. прошу помощи. при написании ниже приложенного скетча все какое то время работало как надо, но ближе к завершению проекта ардуинка стала зависать сначала через 2-3 минуты, ну а в последнее время через пару секунд. попытавшись воспользоваться гуглом пришел к выводу, что проблема возникает в результате утечки памяти. при компиляции каких либо оранжевых предупреждений нет. все белым шрифтом. переменные занимают 69% (из них почти 60% занимает буфер дисплея) помогите найти причину зависания . что смущает, смущает передача данных по Serial . непонятно почему но данные льются потоком, а должны разово при входящем запросе. Понимаю это скорее всего esp косячит, хотя скетч чесно скопипащен из просторов...(телеграмм бот)
C++:
#include <EEPROM.h>
#include <microDS3231.h>
MicroDS3231 rtc;
#define OLED_SOFT_BUFFER_64 // Буфер на стороне МК
#include <GyverOLED.h> // Библиотека дисплея
#include <GyverBME280.h> // библиотека датчика BME280
#include <GyverButton.h> // библиотека кнопок
GyverOLED oled;
GyverBME280 bme;
GButton up(4, LOW_PULL); // Кнопки
GButton down(5, LOW_PULL);
GButton ok(3, LOW_PULL);
#define ITEMS 3 // Общее кол во пунктов (больше 8 - нужно несколько страниц)
#define Check220 6 // пин включения инвертора 220В
#define Health 7 // пин сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
const char week_1[] PROGMEM = "Пн"; const char week_2[] PROGMEM = "Вт"; const char week_3[] PROGMEM = "Ср"; const char week_4[] PROGMEM = "Чт"; const char week_5[] PROGMEM = "Пт"; const char week_6[] PROGMEM = "Сб"; const char week_0[] PROGMEM = "Вс";
const char* const week[] PROGMEM = {week_1, week_2, week_3, week_4, week_5, week_6, week_0};
const char mesyac_1[] PROGMEM = "января"; const char mesyac_2[] PROGMEM = "февраля"; const char mesyac_3[] PROGMEM = "марта"; const char mesyac_4[] PROGMEM = "апреля"; const char mesyac_5[] PROGMEM = "мая"; const char mesyac_6[] PROGMEM = "июня";
const char mesyac_7[] PROGMEM = "июля"; const char mesyac_8[] PROGMEM = "августа"; const char mesyac_9[] PROGMEM = "сентября"; const char mesyac_10[] PROGMEM = "октября"; const char mesyac_11[] PROGMEM = "ноября"; const char mesyac_12[] PROGMEM = "декабря";
const char* const mesyac[] PROGMEM = {mesyac_1, mesyac_2, mesyac_3, mesyac_4, mesyac_5, mesyac_6, mesyac_7, mesyac_8, mesyac_9, mesyac_10, mesyac_11, mesyac_12};
bool flagTimer[] = {0, 0, 0};
int8_t timerminuk[] = {0, 0, 0};
int8_t timerchask[] = {0, 0, 0};
int8_t timerchasn[] = {0, 0, 0};
int8_t timerminun[] = {0, 0, 0};
int addr = 0;
bool vkl = 0;
bool faccum = 1;
byte cont = 255; // начальная яркость экрана
bool contr = 0;
volatile int counter = 0; // счетчик оборотов анемометра
const float pi = 0.013 * 3.14159 / 60; // постоянная радиуса анемометра для определения скорости ветра
uint32_t ckor = millis();
float speed = 0; // переменная скорости ветра
void setup() {
Serial.begin(9600);
bme.begin();
oled.init(OLED128x64); // Инциализация дисплея
oled.setContrast(cont); // Макс. яркость
pinMode(6, OUTPUT); // подключается паралельно к кнопке инвертора для вкл/выкл его по таймеру
pinMode(7, OUTPUT); // подключается реле 220 аакума и сети по умолчанию открыт питание от аккума
attachInterrupt(0, anemometr, RISING); //вызов метода для подсчета оборотов анемометра по прерыванию
}
void loop() {
ok.tick();
if (ok.isHolded()) { //вход в меню при удержании кнопки ок
menu();
}
skorvetr(); //расчитывающий скорость ветра
printTime(); //вывод инфы на дисплей
provBudil(); //проверка исполнения таймеров
saveAccum(); //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
peredacha (); //отправка данных на esp
}
void printTime() { // вывод главное экрана с временем и значениями
DateTime now = rtc.getTime();
bool ftimer[3]; EEPROM.get(10, ftimer);
oled.clear(); // Очищаем буфер
oled.home();
oled.scale2X();
if (now.hour < 10) oled.print(F("0"));
oled.print(now.hour);
oled.print(F(":"));
if (now.minute < 10) oled.print(F("0"));
oled.print(now.minute);
oled.print(F(":"));
if (now.second < 10) oled.print(F("0"));
oled.print(now.second);
printFromPGM(&week[now.day - 1]); // oled.print(now.day);
oled.scale1X();
oled.print(F(" "));
oled.print(now.date);
oled.print(F(" "));
printFromPGM(&mesyac[now.month - 1]); //oled.print(now.month);
oled.print(F(" "));
oled.print(now.year);
oled.print(F("г."));
oled.setCursor(0, 4);
oled.print(F("Темп = "));
oled.print(bme.readTemperature(), 1);
oled.println(F("*C"));
oled.print(F("Влажность = "));
oled.print(round(bme.readHumidity()));
oled.println(F("%"));
oled.print(F("Давлен = "));
oled.print(int(pressureToMmHg((bme.readPressure()))));
oled.println(F("mmHg"));
oled.print(F("Напряжение: ")); //напряжение сети
oled.print(ceilf(analogRead(1) / 4.092) / 10, 1);
oled.print(F("V"));
if (contr) {
oled.setCursor(0, 3);
oled.print("Яркость: ");
oled.inverse(1);
oled.print(cont / 10);
oled.inverse(0);
}
for (byte i = 0; i < 3; i++) {
if (flagTimer[i]) {
oled.setCursor(17, 3); oled.inverse(1); oled.print(" T "); oled.inverse(0);
}
}
oled.update();
}
void printFromPGM(int charMap) { //чтение данных из массивов progmem и вывод на экран
char buffer[24]; // буфер для хранения строки
uint16_t ptr = pgm_read_word(charMap); // получаем адрес из таблицы ссылок
uint8_t i = 0; // переменная - индекс массива буфера
do {
buffer[i] = (char)(pgm_read_byte(ptr++)); // прочитать символ из PGM в ячейку буфера, подвинуть указатель
} while (buffer[i++] != NULL); // повторять пока прочитанный символ не нулевой, подвинуть индекс буфера
oled.print(buffer);
} // печатаем готовую строку
void menu() { //меню настроек устройства
while (1) { //меню настроек при удержании кнопки ок
static int8_t pointer = 0; // Переменная указатель
/* Кнопки */
up.tick(); // Опрос кнопок
down.tick();
ok.tick();
if (up.isClick() or up.isHold()) { // Если кнопку нажали или удерживают
pointer = constrain(pointer - 1, 0, ITEMS - 1); // Двигаем указатель в пределах дисплея
}
if (down.isClick() or down.isHold()) {
pointer = constrain(pointer + 1, 0, ITEMS - 1);
}
if (ok.isClick()) { // Нажатие на ОК - переход в пункт меню
switch (pointer) { // По номеру указателей располагаем вложенные функции (можно вложенные меню)
case 0: Budilnic(); break; // По нажатию на ОК при наведении на 0й пункт вызвать функцию
case 1: setVremya(); break;
case 2: Yarkoct(); break;
}
}
/* меню */
oled.clear(); // Очищаем буфер
oled.home(); // Курсор в левый верхний угол
oled.print // Вывод всех пунктов
(F(
" SET таймер 220В\n" // Не забываем про '\n' - символ переноса строки
" SET дата и время\n"
" Яркость экрана\n"));
printPointer(pointer); // Вывод указателя
oled.update(); // Выводим кадр на дисплей
provBudil(); //проверка исполнения таймеров
saveAccum(); //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
peredacha ();
skorvetr();
if (ok.isHolded()) return;
}
}
void printPointer(uint8_t pointer) { //отображеие курсора в меню
oled.setCursor(0, pointer);
oled.print(">");
}
void setVremya (void) { //раздел меню Set дата и время, установка текущей даты и времени
DateTime nov = rtc.getTime();
int8_t sec = nov.second; int8_t minu = nov.minute; int8_t chas = nov.hour; int8_t den = nov.date; int8_t mes = nov.month; int16_t god = nov.year;
int8_t fmesto = 0; bool save = 0;
while (1) {
up.tick(); // Опрос кнопок
down.tick();
ok.tick();
provBudil(); //проверка исполнения таймеров
saveAccum(); //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
peredacha ();
skorvetr();
if (ok.isHolded()) { //при удержании кнопки ок выход с сохранением если настройка времени в сохраняется или без сохранения
if (save) {
rtc.setTime(sec, minu, chas, den, mes, god);
return;
} else {
return;
}
}
if (ok.isClick()) { // при клике ок меняется что будет изменяться
fmesto++;
}
if (fmesto >= 6) { //при клике ок на последнем настраиваемом пункте возвращается на первый
fmesto = 0;
}
if (up.isClick() or up.isHold()) { // клик или удержание кнопки вверх увеличивает значение с проверкой предотвращающей неправильные значения
switch (fmesto) {
case 0: {
chas++;
if (chas >= 24) {
chas = 0;
};
break;
}
case 1: {
minu++;
if (minu >= 60) {
minu = 0;
};
break;
}
case 2: {
den++;
if (den >= provdate(mes, god) + 1) {
den = 1;
};
break;
}
case 3: {
mes++;
if (mes >= 13) {
mes = 1;
};
break;
}
case 4: {
god++;
if (god >= 2101) {
god = 2020;
};
break;
}
case 5: {
save = !save;
break;
}
}
}
if (down.isClick() or down.isHold()) { // клик или удержание кнопки вниз уменьшает значение значение с проверкой предотвращающей неправильные значения
switch (fmesto) {
case 0: {
if (chas == 0) {
chas = 23;
} else {
chas--;
} break;
}
case 1: {
if (minu == 0) {
minu = 59;
} else {
minu--;
} break;
}
case 2: {
if (den == 1) {
den = provdate(mes, god);
} else {
den--;
} break;
}
case 3: {
if (mes == 1) {
mes = 12;
} else {
mes--;
} break;
}
case 5: {
save = !save;
break;
}
}
}
oled.clear(); // далее до конца функции код вывода инфы на дисплей
oled.home();
oled.println(F(" установка времени"));
oled.setCursor (7, 2);
if (fmesto == 0) oled.inverse(1);
if (chas < 10) oled.print(F("0"));
oled.print(chas);
oled.inverse(0);
oled.print(F(":"));
if (fmesto == 1) oled.inverse(1);
if (minu < 10) oled.print(F("0"));
oled.print(minu);
oled.inverse(0);
oled.print(F(":0"));
oled.print(sec);
oled.setCursor (2, 4);
if (fmesto == 2) oled.inverse(1);
oled.print(den);
oled.inverse(0);
oled.print(F(" "));
if (fmesto == 3) oled.inverse(1);
printFromPGM(&mesyac[mes - 1]);
oled.inverse(0);
oled.print(F(" "));
if (fmesto == 4) oled.inverse(1);
oled.print(god);
oled.inverse(0);
oled.print(F("г."));
if (fmesto == 5) oled.inverse(1);
if (save) {
oled.setCursor(5, 6);
oled.print(F("Сохранить"));
} else {
oled.setCursor(4, 6);
oled.print(F("не сохранять"));
}
oled.inverse(0);
oled.update();
}
}
int provdate(int mes, int god) { //проверка в каком месяце сколько дней
byte den;
switch (mes) {
case 1: {
den = 31;
break;
}
case 2: {
if (god % 4 == 0) {
den = 29;
} else {
den = 28;
} break;
}
case 3: {
den = 31;
break;
}
case 4: {
den = 30;
break;
}
case 5: {
den = 31;
break;
}
case 6: {
den = 30;
break;
}
case 7: {
den = 31;
break;
}
case 8: {
den = 31;
break;
}
case 9: {
den = 30;
break;
}
case 10: {
den = 31;
break;
}
case 11: {
den = 30;
break;
}
case 12: {
den = 31;
break;
}
}
return den;
}
void Budilnic() { //раздел меню SET таймер 220В, установка таймеров на включение и выключение 220В
int8_t minu[] = {timerminun[0], timerminun[1], timerminun[2]}; int8_t chas[] = {timerchasn[0], timerchasn[1], timerchasn[2]}; //проставляем текущее время во время включения таймера
int8_t timinu[] = {timerminuk[0], timerminuk[1], timerminuk[2]}; int8_t tichas[] = {timerchask[0], timerchask[1], timerchask[2]}; //инициализируем временной интервал таймера (сколько работать)
int8_t fmesto = 0; bool save[] = {0, 0, 0}; int8_t timnomer = 0; bool fTimer[] = {flagTimer[0], flagTimer[1], flagTimer[2]};
while (1) {
up.tick(); // Опрос кнопок
down.tick();
ok.tick();
provBudil(); //проверка исполнения таймеров
saveAccum(); //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
peredacha ();
skorvetr();
if (ok.isHolded()) {
for (byte i = 0; i < 3; i++) { // сохранение и выход на меню вверх
flagTimer[i] = fTimer[i];
timerminuk[i] = timinu[i];
timerchask[i] = tichas[i];
timerchasn[i] = chas[i];
timerminun[i] = minu[i];
}
EEPROM.put(addr, timerminuk); addr += sizeof(timerminuk);
EEPROM.put(addr, timerchask); addr += sizeof(timerchask);
EEPROM.put(addr, timerchasn); addr += sizeof(timerchasn);
EEPROM.put(addr, timerminun); addr += sizeof(timerminun);
EEPROM.put(addr, flagTimer); addr = 0; return;
}
if (ok.isClick()) {
fmesto++;
}
if (fmesto >= 7) {
fmesto = 0;
}
if (up.isClick() or up.isHold()) {
switch (fmesto) {
case 0: {
timnomer++;
if (timnomer >= 3) {
timnomer = 0;
};
break;
}
case 1: {
fTimer[timnomer] = !fTimer[timnomer];
break;
}
case 2: {
chas[timnomer]++;
if (chas[timnomer] >= 24) {
chas[timnomer] = 0;
};
break;
}
case 3: {
minu[timnomer]++;
if (minu[timnomer] >= 60) {
minu[timnomer] = 0;
};
break;
}
case 4: {
tichas[timnomer]++;
if (tichas[timnomer] >= 24) {
tichas[timnomer] = 0;
};
break;
}
case 5: {
timinu[timnomer]++;
if (timinu[timnomer] >= 60) {
timinu[timnomer] = 0;
};
break;
}
case 6: {
save[timnomer] = !save[timnomer];
break;
}
}
}
if (down.isClick() or down.isHold()) {
switch (fmesto) {
case 0: {
if (timnomer == 0) {
timnomer = 2;
} else {
timnomer--;
} break;
}
case 1: {
fTimer[timnomer] = !fTimer[timnomer];
break;
}
case 2: {
if (chas[timnomer] == 0) {
chas[timnomer] = 23;
} else {
chas[timnomer]--;
} break;
}
case 3: {
if (minu[timnomer] == 0) {
minu[timnomer] = 59;
} else {
minu[timnomer]--;
} break;
}
case 4: {
if (tichas[timnomer] == 0) {
tichas[timnomer] = 23;
} else {
tichas[timnomer]--;
} break;
}
case 5: {
if (timinu[timnomer] == 0) {
timinu[timnomer] = 59;
} else {
timinu[timnomer]--;
} break;
}
case 6: {
save[timnomer] = !save[timnomer];
break;
}
}
}
oled.clear(); // очистить
oled.home();
oled.println(F(" установка"));
oled.println(F(" электротаймера"));
oled.print(F("таймер N. "));
if (fmesto == 0) oled.inverse(1);
oled.println(timnomer + 1);
oled.inverse(0);
oled.print(F("Таймер: "));
if (fmesto == 1) oled.inverse(1);
if (fTimer[timnomer]) {
oled.println("ВКЛ!");
}
else {
oled.println("ВЫКЛ");
}
oled.inverse(0);
oled.print(F("Вкл. в: "));
if (fmesto == 2) oled.inverse(1);
if (chas[timnomer] < 10) oled.print(F("0"));
oled.print(chas[timnomer]);
oled.inverse(0);
oled.print(F("ч."));
if (fmesto == 3) oled.inverse(1);
if (minu[timnomer] < 10) oled.print(F("0"));
oled.print(minu[timnomer]);
oled.inverse(0);
oled.println(F("м."));
oled.print(F("Выкл. в: "));
if (fmesto == 4) oled.inverse(1);
if (tichas[timnomer] < 10) oled.print(F("0"));
oled.print(tichas[timnomer]);
oled.inverse(0);
oled.print(F("ч."));
if (fmesto == 5) oled.inverse(1);
if (timinu[timnomer] < 10) oled.print(F("0"));
oled.print(timinu[timnomer]);
oled.inverse(0);
oled.println(F("м."));
if (fmesto == 6) oled.inverse(1);
if (save[timnomer]) {
oled.setCursor(5, 7);
oled.print(F("Сохранить"));
} else {
oled.setCursor(4, 7);
oled.print(F("не сохранять"));
}
oled.inverse(0);
oled.update();
}
}
void provBudil() { //проверка не наступило ли время включенных таймеров и исполнение их
DateTime bodun = rtc.getTime();
int8_t minu = bodun.minute; int8_t chas = bodun.hour;
EEPROM.get(addr, timerminuk); addr += sizeof(timerminuk);
EEPROM.get(addr, timerchask); addr += sizeof(timerchask);
EEPROM.get(addr, timerchasn); addr += sizeof(timerchasn);
EEPROM.get(addr, timerminun); addr += sizeof(timerminun);
EEPROM.get(addr, flagTimer); addr = 0;
for (byte i = 0; i < 3; i++) {
if (flagTimer[i] && chas == timerchasn[i] && minu == timerminun[i] && !vkl) {
digitalWrite(Check220, HIGH);
vkl = !vkl;
}
if (flagTimer[i] && chas == timerchask[i] && minu == timerminuk[i] && vkl) {
digitalWrite(Check220, LOW);
vkl = !vkl;
}
}
}
void saveAccum() { //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
if ((analogRead(1) / 4.092) / 10 < 11 && !faccum) {
digitalWrite(Health, HIGH);
faccum = !faccum;
}
if ((analogRead(1) / 4.092) / 10 > 13 && faccum) {
digitalWrite(Health, LOW);
faccum = !faccum;
}
}
void Yarkoct() { //изменение яркости экрана
contr = 1;
while (1) {
oled.setContrast(cont);
printTime();
up.tick(); // Опрос кнопок
down.tick();
ok.tick();
provBudil(); //проверка исполнения таймеров
saveAccum(); //проверка сохранения жизни аккумулятора панели и переключение при низком заряде на сеть 220В
peredacha ();
skorvetr();
if (ok.isHolded()) {
contr = 0;
return;
}
if (up.isClick() or up.isHold()) {
if (cont > 245) {
cont = 255;
}
else cont += 10;
}
if (down.isClick() or down.isHold()) {
if (cont < 10) {}
else cont -= 10;
}
}
}
void peredacha () { //передача данных в ESP8266
if (Serial.available() > 0) {
char incomingByte = Serial.read();
String dacha = "$" + String(int (bme.readTemperature() * 10)) + " " + String(int (bme.readHumidity())) + " " + String(int(pressureToMmHg((bme.readPressure())))) + " " + String(int(analogRead(1) / 4.092)) + " " + String(int(speed * 10)) + ";";
Serial.print(dacha);
}
}
void anemometr() { // метод считающий обороты анемометра по прерыванию
counter++;
}
void skorvetr() { // метод расчитывающий скорость ветра
if (millis() - ckor >= 60000) {
ckor = millis();
speed = pi * counter;
counter = 0;
}
}