ARDUINO зависание ардуино нано

vivatgm

✩✩✩✩✩✩✩
26 Янв 2021
23
0
Доброго времени суток. прошу помощи. при написании ниже приложенного скетча все какое то время работало как надо, но ближе к завершению проекта ардуинка стала зависать сначала через 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;
  }
}
 

bort707

★★★★★★✩
21 Сен 2020
3,066
915
Я бы обратил внимание на функцию
printFromPGM (строка 117)
Вы читаете какую-то строчку из ПРОГМЕМ в буфер в памяти, при этом никакой проверки, что строчка поместилась в буфер - в коде нет.Если вы где-то ошибклись и в конце строки почему-то не будет терминатора - распашка памяти обеспечена.
Проверку на переполнение буфера нужно делать ВСЕГДА, даже если вы уверены. что его, переполнения. быть не может.
 
  • Лойс +1
Реакции: vivatgm

vivatgm

✩✩✩✩✩✩✩
26 Янв 2021
23
0
спасибо попробую, о результатах отпишусь
 

vivatgm

✩✩✩✩✩✩✩
26 Янв 2021
23
0
Докладываю. Bort707, после вашего ответа проанализировал ситуацию и понял, что проблема возникла на новой ардуинке, а в моем скетче запись происходит раньше чтения, поэтому в той части и возникало исключение. Добавил проверку как вы сказали и еще добавил вначале скетча проверку на валидность данных в прогмем и....за сутки ни единого разрыва. Но выяснилось другое. При подключении есп ардуинка сразу зависает, при этом есп продолжает работать и отвечать на вайфай запросы. Но это уже совсем другая история. Спасибо