Serial.println() нарушает работу программы, и еще одна неясность

Тема в разделе "ESP8266, ESP32", создана пользователем Fireforce, 26 ноя 2017.

  1. Fireforce

    Fireforce Нуб

    В доки пока сильно не углублялся, пока только со структурой кода более менее разбираться начал. Но все же приму к сведению, спасибо за информацию.
     
  2. Fireforce

    Fireforce Нуб

    Пересобрал код, добавил в проект датчик BME280.
    Код (C++):
    // Библиотеки ESP и Blynk
    //#define BLYNK_PRINT Serial //печатает логотип и инфо по подключению
    //#define BLYNK_DEBUG
    #include <ESP8266WiFi.h>
    #include <BlynkSimpleEsp8266.h>
    // Библиотеки для прошивки ESP по воздуху
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    #include <ESP8266mDNS.h>
    // Библиотеки для часов реального времени, таймера, счетчика и др.
    #include <Wire.h>
    #include <PZEM004T.h>
    #include <SimpleTimer.h>
    #include <iarduino_RTC.h>
    #include "SparkFunBME280.h"
    #include <plcStandardLib_1.h>
    //#include <SoftwareSerial.h>

    //ВНИМАНИЕ! Перед загрузкой отключи вывод "D3" от оптопары сигнализации "достигнут лимит",
    //иначе если он останется подключен, после прошивки устройство не запустится.
    //после запуска вывод можно подключить обратно.

    int mmH; //атм.давление
    int limit = 75; //значение при котором срабатывает сигнализация "достигнут лимит" и оно же участвует в условии "сброс".
    bool taboo = false; //если она TRUE, это запрет работы ресурсоемких функций.
    // переменные для мигалки      //встроенный светодиод инвертирован и при высоком уровне сигнала светодиод гаснет
    //const int ledPin = 2;           // номер выхода, подключенного к светодиоду. Встроенный на плате светодиод сидит на D4(GPIO2)
    //bool ledState = HIGH;             // этой переменной устанавливаем состояние светодиода

    //unsigned long previousMillis = 0;        // храним время последнего переключения светодиода
    //unsigned long interval = 1000;           // интервал между включение/выключением светодиода (1 секунда)

    //Конфигурация выходов
    const int pzem_reset = 16;        //D0 - IO16 - сброс счетчика PZEM004
    const int power_max = 0;          //D3 - IO0 - сигнал - "достигнут лимит"
    const int power_min = 14;        //D5 - IO14 - сигнал - "новый период"

    //Таймеры и триггеры
    TP TP1(5000); //Инициализация единичного импульса, 5с. сигнал - "достигнут лимит"
    TP TP2(3000); //Инициализация единичного импульса, 3с. сигнал - "новый период"
    R_TRIG R_TRIG1;
    R_TRIG R_TRIG2;
    R_TRIG R_TRIG3;
    R_TRIG R_TRIG4;
    F_TRIG F_TRIG1;
    F_TRIG F_TRIG2;
    F_TRIG F_TRIG3;

    //аутентификационные данные
    char auth[] = "ffffffffffffffffffffff"; //код авторизации в проекте Blynk
    char ssid[] = "vvvvvvvv";   // Название WiFi сети.
    char pass[] = "zzzzzzzz";   // Пароль WiFi сети.
    bool isFirstConnect = true; // переменная для хранения информации первый ли раз приконектились (после полного отключения питания)
    int wifisignal;

    //Переменные для счетчика
    float v;
    float i;
    float p;
    float e;
    int vv;
    int pp;
    int ee;

    //Исполнительные устройства//
    //Управление сигнализацией
    bool out_tp1;
    bool out_tp2;
    //для функции "Сброс"
    bool on_off;
    unsigned long preMillis = 0;
    int interval1; //интервал для нахождения светодиода во включенном состоянии
    int interval2; //в выключенном состоянии
    int interval3; //во включенном состоянии
    bool run_pzem_res;
    bool in_r_tr1;
    bool in_r_tr2;
    bool in_r_tr3;
    bool in_r_tr4;
    bool out_r_tr2;
    bool out_r_tr3;
    bool out_f_tr1;
    bool out_f_tr2;
    bool out_f_tr3;

    BME280 multiSensor;
    PZEM004T pzem(&Serial);
    IPAddress ip(192, 168, 1, 1);
    SimpleTimer timer_Connect;
    SimpleTimer timer_ledflash;
    SimpleTimer timer_sendWifi;
    SimpleTimer timer_send_time;
    SimpleTimer timer_pzm;
    WidgetLCD lcd(V1);         //в свойствах виджета включи режим ADVANCED
    WidgetTerminal terminal(V9);
    WidgetLED on_off_virt(V7);
    WidgetLED res_virt(V8);
    iarduino_RTC time_(RTC_DS3231);  // Объявляем объект time_ для работы с RTC модулем на базе чипа DS3231, используется аппаратная шина I2C[/SPOILER]
     
  3. Fireforce

    Fireforce Нуб

    Код (C++):
    void setup()
    {

      //Конфигурация датчика BME280
      multiSensor.settings.commInterface = I2C_MODE;
      multiSensor.settings.I2CAddress = 0x76; //Адрес датчика
      multiSensor.settings.runMode = 3; //В примере предлагают использовать Forced mode, но при обновлении раз в секунду достаточно Normal mode
      multiSensor.settings.tStandby = 5; //Очевидно чаще не нужно
      multiSensor.settings.filter = 0;
      multiSensor.settings.tempOverSample = 1;
      multiSensor.settings.pressOverSample = 1;
      multiSensor.settings.humidOverSample = 1;
      multiSensor.begin();


      int on_off_virt = 255; //устанавливаю максимальную яркость свечения виртуального светодиода
      on_off = false;
      run_pzem_res = false;
      interval1 = 5000;
      interval2 = 5500;
      interval3 = 5900;

      //Конфигурируем выхода
      pinMode(ledPin, OUTPUT);
      pinMode(pzem_reset, OUTPUT);
      pinMode(power_max, OUTPUT);
      pinMode(power_min, OUTPUT);

      //конфигурация циклических интервалов для процессов, выполняющихся по таймеру
      ////timer_ledflash.setInterval(50L, ledflash);     // каждые 50 милисекунд, проверяем состояние функции Ledflash
      timer_Connect.setInterval(30000L, reconnectBlynk); // проверяем каждые 30 секунд, если все еще подключен к серверу
      timer_send_time.setInterval(5000L, RTC_virt);      // передаем время с DS3231 и показания с BME280: давление, влажность и температуру
      timer_sendWifi.setInterval(10000L, sendWifi);      // Wi-Fi singal
      timer_pzm.setInterval(7000L, pzm);                 //периодичность отправки данных (не делать меньше секунды, система зависнет!)

      //Serial.begin(9600); // запуск коммуникационного порта
      //Serial.println("\Starting");

      //Blynk.begin(auth, ssid, pass); // Обычно мы пользуемся конструкцией сверху,
      //но из-за нее зависает ESP8266 при попытке установить WiFi соединение
      Blynk.config(auth); // поэтому конфигурируем соединение
      Blynk.disconnect(); //разорвать соединение
      Blynk.connect();  // а потом пробуем уже конектиться
      pzem.setAddress(ip);
      time_.begin();  // Инициируем работу с модулем RTC - DS3231
      lcd.clear();   //Очистка LCD Widget
      ArduinoOTA.setHostname("ESP8266-01"); //OTA Задаем имя сетевого порта
      ArduinoOTA.begin(); //OTA Инициализируем OTA
    }

    BLYNK_CONNECTED() {
      if (isFirstConnect) {     // Если приконектились первый раз то
       Blynk.syncAll();                          // синхронизируемся
       Blynk.notify("Your device is online.");  // выводим PushUp сообщение
       isFirstConnect = false; // Соответственно уже первый раз подключились, переводим флаг в отрицательное состояние
       terminal.print("Blynk v"); //вывод информации в терминал
       terminal.println(BLYNK_VERSION);
       terminal.println("Device started");
       terminal.flush();
      }
    }

    void reconnectBlynk() {
      if (!Blynk.connected()) { //если соединения нет то
      if (Blynk.connect()) {  //Для подключения к Blynk server используются функции Blynk.connect(). Она возвращает результат в булевом типе.
        terminal.print("Reconnected");  //вывожу сообщение об успешном переподключении к серверу Blynk.
        terminal.flush();
        //BLYNK_LOG("Reconnected"); //выводит информацию в терминал Arduino IDE
      }
        //else
        //{ BLYNK_LOG("Not reconnected");  
        //}
      }
    }


    void pzm() //обработка значений PZEM004 и передача их в LСD Widget
    {           //данные обновляются с переодичностью 7 сек.

      v = pzem.voltage(ip);
      if (v < 0.0) v = 0.0;

      i = pzem.current(ip);
      if (i < 0.0) i = 0.0;

      p = pzem.power(ip);
      if (p < 0.0) p = 0.0;

      e = pzem.energy(ip);
      if (e < 0.0) e = 0.0;

      //вывод значений в LСD Widget Blynk
      lcd.clear();
      lcd.print(0, 0, "U=");
      vv = (int)v;
      lcd.print(2, 0, vv);
      lcd.print(5, 0, "V");
      lcd.print(8, 0, "I=");
      lcd.print(10, 0, i);
      lcd.print(14, 0, " A");
      lcd.print(0, 1, "P=");
      pp = (int)p;
      lcd.print(2, 1, pp);
      lcd.print(6, 1, "W");
      lcd.print(8, 1, "E=");
      ee = int(e)/1000;  //преобразовываю из float в беззнаковое число int. 25450.400/1000 = 25,4504. Переменная "ee" будет = 25 кВт
      lcd.print(10, 1, e / 1000);
      lcd.print(13, 1, "kWh");

    }
     
     
  4. Fireforce

    Fireforce Нуб

    Код (C++):

    //====================//Исполнительные устройства//

    //"сигнал - достигнут лимит"
    void fn_power_max() { //сначала преобразовываем десятичное число в Булево, для использовании в управлении райзер-триггером R_TRIG1
      if (ee >= limit) {in_r_tr1 = true; taboo = true;} //устанавливаю запрет работы ресурсоемкой функции pzm()
      digitalWrite(power_max, out_tp1 = TP1.Run(R_TRIG1.Run(in_r_tr1)));  //power_max - номер выхода(pin)
      out_f_tr1 = F_TRIG1.Run(out_tp1); //по окончании работы таймера TP1, триггер даст единичный импульс, длительностью одну интерацию. Т.е при изменении состояния переменной "out_tp1" с TRUE в-> FALSE.
      if (out_f_tr1 == true) {      //эта конструкция нужна, чтобы при многократных циклах во время работы таймера TP1, повторно не отправлять сообщения.
      Blynk.notify(String(ee)+" kWh, limit.");
      in_r_tr1 = false; //разблокирую вход триггера R_TRIG1
      taboo = false;
      }
    }

    //Сброс счетчика
    //Перед загрузкой кода отсоедини вывод "Сброс", чтобы при инициализации выводов не обнулить накопленные показания.
    //Функция формирует временные интервалы для управления светодиодом оптопары сброса PZEM004.
    //функции разрешено работать 1-го числа с 9 по 23 час.,
    void pzem_reset_on() { //in_r_tr1 заведена в условие на случай если по стечении обстоятельств fn_power_max()и pzem_reset_on() вместе захотят включится
    if (ee >= limit && time_.day == 1 && time_.Hours >= 19 && time_.Hours <= 23 && time_.minutes == 1 && taboo == false) {
         taboo = true;
         in_r_tr2 = true;
      }
    out_r_tr2 = R_TRIG2.Run(in_r_tr2);  //при срабатывании R_TRIG появляется единичный импульс, длительностью в одну интерацию                                
    if (out_r_tr2 == true) { //при последующих циклах работы функции вход триггера заблокирован до тех пор пока in_r_tr2 не станет равно FALE
       Blynk.notify(String(ee)+" kWh, seychas srabotaet Sbros."); //отпраляю push-уведомление, что через 1 минуту сработает "Сброс"
    }
    if (ee >= limit && time_.day == 1 && time_.Hours >= 19 && time_.Hours <= 23 && time_.minutes == 2 && taboo == false) {in_r_tr3 = true;}
       out_r_tr3 = R_TRIG3.Run(in_r_tr3);
    if (out_r_tr3 == true) {
       Blynk.email("my-mail@inbox.com", String(ee)+" kWh.");
       preMillis = millis();
       run_pzem_res = true;
    }      

    if (millis() - preMillis < interval1 && run_pzem_res == true) {on_off = true;}
    if (millis() - preMillis >= interval1 && millis() - preMillis <= interval2 && run_pzem_res == true) {on_off = false;}
    if (millis() - preMillis >= interval2 && millis() - preMillis <= interval3 && run_pzem_res == true) {on_off = true;}
    if (millis() - preMillis > interval3 && run_pzem_res == true) {on_off = false; run_pzem_res = false;}

      out_f_tr2 = F_TRIG2.Run(run_pzem_res); {//триггер сработает по изменению состояния переменной "run_pzem_res" с TRUE в-> FALSE
    if (out_f_tr2 == true)
      Blynk.notify("Sbros - OK.");
      in_r_tr2 = false; //разблокирую входы райзер-триггеров R_TRIG2, R_TRIG3
      in_r_tr3 = false; //по условиу ee >= limit функцию уже не зациклит, т.к. показания уже сброшены и условие не верно.
      taboo = false; //разрешаю работу pzm()
      }
    }


    //сигнал - "новый период" //сигнал прозвучит через 1 минуту после отработки фн."Сброс счетчика", так же система отправит push-уведомление.
    void fn_power_min() {
    if (time_.day == 1 && time_.Hours >= 19 && time_.Hours <= 23 && time_.minutes == 3 && taboo == false) { //день и промежуток времени когда работает функция
        taboo = true;
        in_r_tr4 = true;
      }
      digitalWrite(power_min, out_tp2 = TP2.Run(R_TRIG4.Run(in_r_tr4))); //power_min - номер выхода(pin)
      out_f_tr3 = F_TRIG3.Run(out_tp2);
      if (out_f_tr3 == true) {
      Blynk.notify(String(ee)+" kWh, Nachat noviy period.");
      taboo = false;
      }
    }


    void pzem_reset_out() //Включение светодиода оптопары сброса и визуализация выполнения функции "Сброс" для счетчика PZEM004, через приложение Blynk.
    {
      if (on_off == true)
       on_off_virt.on(); //отображение состояния виртуального светодиода в приложении Blynk
      else on_off_virt.off();
       digitalWrite(pzem_reset, on_off);
    }


    void var_res_visu() {//визуализация состояния переменной "taboo", в приложении Blynk.

      if (taboo == true) res_virt.on();  //включить виртуальный светодиод отображения состояния переменной "taboo"
      else res_virt.off();
    }


    void sendWifi() { //индикатор Wi-Fi
      wifisignal = map(WiFi.RSSI(), -105, -40, 0, 100);
      terminal.println(String(wifisignal)+" dBm"); //передаю в терминал Blynk уровень
      terminal.flush();     //принимаемого сигнала Wi-fi сигнала от роутера
    }


    void RTC_virt()  // передает в виджет время и показания с датчика BME280: давление, влажность и температуру
    {
      Blynk.virtualWrite(V6, String(mmH)+"mmH "+ "   H=" + (uint8_t)multiSensor.readFloatHumidity()+" %");
      Blynk.virtualWrite(V5, String (time_.gettime("H:i:s"))+"  t="+multiSensor.readTempC());
      //Blynk.virtualWrite(V4, time_.gettime("d-m-Y")); // день, месяц, год
    }


    void loop()
    {
      if (taboo == false) { //сбор данных со счетчика разрешен только если taboo == false
      timer_pzm.run(); //интервальный таймер для активации функции сбора данных - pzm()
      }
      mmH = multiSensor.readFloatPressure()/133; //обработка данных - "атм.давление"
      ledState = HIGH; //выключаю встроенный на плате светодиод
      digitalWrite(ledPin, ledState); //Интегрированный на плату ESP8266 светодиод инвертирован,
                      //при логическом (HIGH, "1" или TRUE) он гаснет.

      ArduinoOTA.handle(); // OTA Всегда готовы к прошивке без проводов
      timer_Connect.run(); // проверяем каждые 30 секунд, если все еще подключен к серверу
      //timer_ledflash.run(); //Мигалка что бы видеть есть ли зависания
                             // во время попытки установить связь с WiFi

      fn_power_max(); //функция сигнал - "достигнут лимит"
      fn_power_min(); //функция сигнал - "новый период"
      pzem_reset_on(); //Формирование временных интервалов для оптопары сброса счетчика PZEM004.
      pzem_reset_out(); //Включение светодиода оптопары сброса и визуализация выполнения функции "Сброс" для счетчика PZEM004, через приложение Blynk.


      if (Blynk.connected()) {  //проверяем присоединились ли к серверу Blynk,
       Blynk.run();    //если присоединились то запускаем в работу Blynk.run()
       timer_sendWifi.run(); //уровень сигнала Wi-fi
       timer_send_time.run(); //работа с DS3231 и BME280
       var_res_visu(); //визуализация состояния переменной "taboo"
      }
    }

    /*
    // Просто мигалка что бы видеть есть ли зависания
    // во время попытки установить связь с WiFi
    void ledflash()
    {
    // Просто мигалка
    unsigned long currentMillis = millis();

      //проверяем не прошел ли нужный интервал, если прошел то
      if(currentMillis - previousMillis > interval) {
        // сохраняем время последнего переключения
        previousMillis = currentMillis;
        // если светодиод не горит, то зажигаем, и наоборот
        if (ledState == LOW) {
        ledState = HIGH;
        Serial.println(currentMillis);
        }
         else
        {  ledState = LOW;
        Serial.println(currentMillis);
        }
          digitalWrite(ledPin, ledState);
      }
    }
    */

    Развертка правда видна при прорисовке LCD-виджета, который отображает показания со счетчика. Медленно так слева на право отрисовывает, заполняя виджет символами раз в 7 секунд. Но это уже не критично, даже винтажности добавляет:)

    Замечание по библиотеке для часов реального времени - iarduino_RTC .
    В примере работы с этой библиотекой применяется зарезервированное слово "time". Сам по себе пример работает, но как только из него берешь к примеру такие строки:

    iarduino_RTC time(RTC_DS3231);
    ...time.gettime("d-m-Y, H:i:s, D");

    и встраиваешь в другой проект, возникает ошибка компиляции.
    Чтобы устранить это, "time" нужно заменить на time_t, time_ или что-то типа того.

    В общем код сейчас работает как нужно.

    Всех с наступающим Новым Годом! Спасибо за участие в обсуждении.