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

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

  1. Fireforce

    Fireforce Нуб

    Доброго времени суток!
    Делаю проект под ESP8266 и виджет Blynk под Android. Но с функцией pzem_reset_on() для сброса показаний счетчика PZEM004 что-то не выходит. Сброс происходит по событию minute_time==1 от часов реального времени DS3231. Логика функции: Светодиод включился на 5 сек., отключился на время 400 мс, снова включился на 500мс. и выключился.
    Код (C++):

    #include <SoftwareSerial.h> // Arduino IDE <1.6.6
    #include <Wire.h> //I2C library
    #include <RtcDS3231.h>  //RTC library
    #include <plcStandardLib_1.h>

    int pzem_reset=16; //D0 - IO16 - сброс счетчика PZEM004
    int interval1=5000; //интервал для нахождения светодиода во включенном состоянии
    int interval2=5400; //интервал для нахождения светодиода в выключенном состоянии
    int interval3=5800; //интервал для нахождения светодиода во включенном состоянии
    unsigned long previousMillis;
    int minute_time;
    R_TRIG R_TRIG1;
    boolean out_r_tr;
    bool on_off=false;
    bool run_pzem_res=false;
    RtcDS3231<TwoWire> rtcObject(Wire); //Uncomment for version 2.0.0 of the rtc library

    void setup()//Конфигурация//////////////////////////////////////////
    {
    Serial.begin(9600);// запуск коммуникационного порта
    rtcObject.Begin();// запустить Wire  Wire.begin(DS3231_SCL, DS3231_SDA);
    pinMode(pzem_reset, OUTPUT);//D0 сброс счетчика PZEM004
    RtcDateTime currentTime = RtcDateTime(17, 10, 24, 10, 00, 55); //Для установки времени - раскоментируй - define date and time object
    rtcObject.SetDateTime(currentTime); //Для установки времени - раскоментируй
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    void loop()//Цикл
    {
    RTC();// функция часов реального времени
    pzem_reset_on(); //Формирует одиночный импульс и временные интервалы для оптопары сброса счетчика PZEM004.
    }

    void RTC()
    {
    RtcDateTime currentTime = rtcObject.GetDateTime();    //get the time from the RTC
    char str[15];   //declare a string as an array of chars
    sprintf(str, "%d/%d/%d %d:%d:%d",     //%d allows to print an integer to the string
              currentTime.Year(),   //get year method
              currentTime.Month(),  //get month method
              currentTime.Day(),    //get day method
              currentTime.Hour(),   //get hour method
              minute_time = currentTime.Minute(), //get minute method
              currentTime.Second()  //get second method
             );
    //Serial.println(str);
    //delay(1000); //1 second delay
    }

    void pzem_reset_on()
    {
      out_r_tr = R_TRIG1.Run(minute_time==1);
      if (out_r_tr==true){run_pzem_res=true; previousMillis = millis();}
    if (millis() - previousMillis < interval1 & run_pzem_res==true) {on_off=true;}
    if (millis() - previousMillis == interval1 & run_pzem_res==true) {on_off=false;}
    if (millis() - previousMillis == interval2 & run_pzem_res==true) {on_off=true;}
    if (millis() - previousMillis == interval3 & run_pzem_res==true) {on_off=false; run_pzem_res=false;}
    digitalWrite(pzem_reset, on_off);
    }
    Программа работает, но почему то при добавлении в нее функции Serial.println(str), зависает, т.е. светодиод включается и все.
     
    Последнее редактирование: 26 ноя 2017
  2. Fireforce

    Fireforce Нуб

    Если же эту функцию я встраиваю в основной проект, она совсем перестает работать.
    Код (C++):
    #define BLYNK_PRINT Serial
    #include <ESP8266WiFi.h>
    #include <ESP8266mDNS.h>
    #include <WiFiUdp.h>
    #include <BlynkSimpleEsp8266.h>
    #include <SimpleTimer.h>
    #include <SoftwareSerial.h> // Arduino IDE <1.6.6
    #include <PZEM004T.h>
    #include <plcStandardLib_1.h>
    #include <Wire.h> //I2C library
    #include <RtcDS3231.h>  //RTC library

    boolean sw_on;
    boolean sw_on_r_tr;
    boolean out_r_tr;
    boolean out_f_tr;
    R_TRIG R_TRIG1; // Инициализация триггера фронта для кнопки
    F_TRIG F_TRIG1;
    int pzem_reset=16; //D0 - IO16 - сброс счетчика PZEM004
    int power_max=0; //D3 - IO0 - Сигнализация "Высокая мощность"
    int power_min=2; //D4 - IO2 - Сигнализация "Низкая мощность"
    bool preOn;
    unsigned long led1OnTime;
    bool led1On;
    unsigned long led2OnTime;
    bool led2On;
    unsigned long preOnTime;
    int minute_time;
    unsigned long previousMillis;
    bool on_off=false;
    int interval1=5000; //интервал для нахождения светодиода во включенном состоянии
    int interval2=5400; //интервал для нахождения светодиода в выключенном состоянии
    int interval3=5800; //интервал для нахождения светодиода во включенном состоянии
    bool run_pzem_res=false;

    PZEM004T pzem(&Serial);
    IPAddress ip(192, 168, 1, 1);
    SimpleTimer timer;
    RtcDS3231<TwoWire> rtcObject(Wire); //Uncomment for version 2.0.0 of the rtc library
    WidgetLCD lcd(V10);
    //////////////////////////////////////////////SimpleTimer timer;/////////////////////////////////////////////////
    // This function sends Arduino's up time every second to Virtual Pin (5).
    // In the app, Widget's reading frequency should be set to PUSH. This means
    // that you define how often to send data to Blynk App.
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



    //=================================================================
                               //Конфигурация//
    void setup()
    {
    //=======Данные аутентификации
    char auth[] = "dfdfdfdfjhhhhhjhkjkkjkjkjkkj";
    char ssid[] = "M365";
    char pass[] = "12345678";
    Blynk.begin(auth, ssid, pass);
    pzem.setAddress(ip);
    rtcObject.Begin();// запустить Wire  Wire.begin(DS3231_SCL, DS3231_SDA);
    Serial.begin(9600);// запуск коммуникационного порта
    pinMode(pzem_reset, OUTPUT);//D0 сброс счетчика PZEM004
    pinMode(power_max, OUTPUT); //D3 работает на "выход" - Сигнализация "Высокая мощность"
    pinMode(power_min, OUTPUT);//D4 работает на "выход" - Сигнализация "Низкая мощность"
    led1On = false;
    led2On = false;
    //lcd.clear(); //Use it to clear the LCD Widget
    // Setup a function to be called every second
    timer.setInterval(5000L, pzm);

    //Установка времени в модуль DS3231//
    RtcDateTime currentTime = RtcDateTime(17, 10, 24, 23, 00, 55); //define date and time object
    rtcObject.SetDateTime(currentTime); //Для установки времени - раскоментируй*/

    }

    //=====================================================================
                                //Цикл//
    void loop()
    {
    Blynk.run();
    pzm();
    timer.run(); // Initiates SimpleTimer
    RTC(); //функция часов реального времени
    pzem_reset_on(); //Формирует одиночный импульс и временные интервалы для оптопары сброса счетчика PZEM004.
    sw_on_p(); //Сигнализация "Высокая мощность"
    sw_off_p(); //Сигнализация "Низкая мощность"
    }

    //=====================================================================
                             //Функция часов реального времени//
    void RTC()
    {
    RtcDateTime currentTime = rtcObject.GetDateTime();    //get the time from the RTC
    char str[15];   //declare a string as an array of chars
    sprintf(str, "%d/%d/%d %d:%d:%d",     //%d allows to print an integer to the string
              currentTime.Year(),   //get year method
              currentTime.Month(),  //get month method
              currentTime.Day(),    //get day method
              currentTime.Hour(),   //get hour method
              minute_time = currentTime.Minute(), //get minute method
              currentTime.Second()  //get second method
             );
    //Serial.println(str);        
    }

    //====================================================================
                              //Функция обработки для PZEM004//
    void pzm()
    {
      float v = pzem.voltage(ip);
      if (v < 0.0) v = 0.0;
      Blynk.virtualWrite(V1, v);
      Serial.print(v); Serial.print("V; ");
      float i = pzem.current(ip);
      if (i >= 0.0) {
        Blynk.virtualWrite(V2, i);
        Serial.print(i); Serial.print("A; ");
      }

      float p = pzem.power(ip);
      if (p >= 0.0) {
        Blynk.virtualWrite(V3, p);
        Serial.print(p); Serial.print("W; ");
        if ( p >= 800.00) sw_on_r_tr = true; //Формирует управляющий сигнал
        if ( p < 800.00) sw_on_r_tr = false; //для детектора фронта - R_TRIG.
      }

      float e = pzem.energy(ip);
      if (e >= 0.0) {
        Blynk.virtualWrite(V4, e);
        Serial.print(e); Serial.print("Wh; ");
      }

      Serial.println();
      lcd.clear(); //Use it to clear the LCD Widget
      lcd.print(0, 0, "U="); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      int vv = (int)v;
      lcd.print(2, 0, vv); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(5, 0, "V"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(8, 0, "I="); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(10, 0, i); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(14, 0, " A"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(0, 1, "P="); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      int pp = (int)p;
      lcd.print(2, 1, pp); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(6, 1, "W"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(8, 1, "E="); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      //int ee=(int)e/1000;
      lcd.print(10, 1, e / 1000); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
      lcd.print(13, 1, "kWh"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
    }

    //============================================
                                //Исполнительные устройства//
    //====================                                                            
    //Сигнализация "Высокая мощность"//

    void sw_on_p()
    {
      //out_r_tr = R_TRIG1.Run(sw_on_r_tr);
      if (sw_on_r_tr == true) {
        preOnTime = millis();
        if (millis() - preOnTime > 10) preOn = true;
        digitalWrite(brelok_on, preOn);
        led1On = true;
        led1OnTime = millis(); //Записываю время включения светодиода
      }

      if (millis() - led1OnTime > 1000) preOn = false;
    {
        digitalWrite(brelok_on, LOW);
        led1On = false;
      }
    }

    //====================
    //Сигнализация "Низкая мощность"//

    void sw_off_p()
    {
      out_f_tr = F_TRIG1.Run(sw_on_r_tr);
      if (out_f_tr == true) {
        digitalWrite(brelok_off, out_f_tr);
        led2On = true;
        led2OnTime = millis(); //Записываю время включения светодиода
      }

      if (millis() - led2OnTime > 1000) {
        digitalWrite(brelok_off, LOW);
        led2On = false;
      }
    }

    //====================================
    //сброс счетчика //

    void pzem_reset_on()
    {
    out_r_tr = R_TRIG1.Run(minute_time==1);
    if (out_r_tr==true){run_pzem_res=true; previousMillis = millis();}
    if (millis() - previousMillis < interval1 & run_pzem_res==true) {on_off=true;}
    if (millis() - previousMillis == interval1 & run_pzem_res==true) {on_off=false;}
    if (millis() - previousMillis == interval2 & run_pzem_res==true) {on_off=true;}
    if (millis() - previousMillis == interval3 & run_pzem_res==true)
    {on_off=false; run_pzem_res=false;}
    digitalWrite(pzem_reset, on_off);
    }
    Подскажите пожалуйста как исправить
     
    Последнее редактирование: 26 ноя 2017
  3. rkit

    rkit Гуру

    А вы уверены, что вам 14 символов хватит?
     
  4. ИгорьК

    ИгорьК Гуру

    plcStandardLib_1.h - это что и для чего?

    Код (C++):
    //delay(1000); //1 second delay
    А это в таком проекте категорически запрещено.
     
  5. Fireforce

    Fireforce Нуб

    Это аналог библиотеки из среды программирования CodeSys. В ней присутствуют одновибраторы R_TRIG и F_TRIG. То есть при возникновении импульса данные блоки отслеживают фронт. Если просто своими словами..Например нажали кнопку, в привязанной к ней переменной "in" появилось TRUE. "in" соединена со входом одновибратора R_TRIG, на его выходе проходит короткий импульс. Следующий такой импульс мы можем получить только если отпустим кнопку и снова ее нажмем. т.е импульс в этом одновибраторе всегда формируется при смене с FALSE в TRUE. Причем кнопку можно коротко нажать или продолжать удерживать, от этого длина импульса не изменится. F_TRIG работает наоборот. Импульс на его выходе формируется по перепаду с TRUE в FALSE. Т.е. по отпусканию кнопки.
    Так же эти блоки присутствуют в среде SMLogix от Сигнетикс, но с другими названиями - Raise(Поднимать) и Fall(Упасть)

    Да я в курсе про delay, - на время выполнения этой функции блокируется остальная логика программы. Serial.println(str); delay(1000); применяю только для отладки, в основном чтобы увидеть состояние входов/выходов, удостовериться что провода надежно сидят на пинах. И то состояние некоторых переменных не посмотришь, - нарушается работа программы. Даже просто Serial.println() без delay(). После исключаю этот код из программы.
     
  6. Fireforce

    Fireforce Нуб

    Да, сомнения были, но строка нормально выводится в монитор в таком формате 17/11/27/ 10:00:55 , ничего не теряется. Хотя действительно получается что 18 символов, если считать двоеточия и пробел.
    Код брал с Гитхаба, поэтому не придал значения. Думаю рас он отлажен, значит так надо, Наверно я какой-то тонкости еще не знаю.
    В основном программирую PLC-контроллеры на паскалеподобном языке.
    Ардуино осваивал на ходу, поэтому половина кода скетча под исполнительные устройства писал сам, и еще половина копипаст с корректировкой.
     
    Последнее редактирование: 28 ноя 2017
  7. rkit

    rkit Гуру

    Нет никакого основания полагать, что на гитхабе хороший код. Любой код надо читать и разбирать.
     
  8. Fireforce

    Fireforce Нуб

    Собрал я отладчик на базе LCD-экрана и I2C-адаптера. Вывел на него переменные, состояние которых необходимо контролировать.
    Правда для удобства эмулировал часы реального времени. Выяснил что программа зависает на 2-м шаге (stat=2).
    Т.е. это выполняется:
    if (seconds == 5) {run_pzem_res=true; previousMillis = millis(); stat=1;}
    else
    {if (millis() - previousMillis < interval1 & run_pzem_res==true) {on_off=true; stat=2;}

    А дальше я предположил что таймер millis() проскакивает заданные в условиях значения.
    Как я понимаю esp8266 как и ардуино имеет несколько уровней абстракции программного кода. 1-й железо, кварцевый генератор - millis() берет данные из этого "железного" таймера, 2-й - библиотеки и 3-й сам код пользователя. Вопрос можно ли быть уверенным что все действия связаны друг с другом? Если я задал условие то оно точно выполнится?
    Не может ли получиться так что millis() уже насчитал 5000, но контроллер только начал новый цикл и находится в самом начале считывания пользовательской программы.
    И когда он доходит до строчки if (millis() - previousMillis == interval1 & run_pzem_res==true) {on_off=false; stat=3;},
    millis()
    на этот момент имеет уже значение 5200, а interval1=5000, следовательно условие не выполняется.
    Первое условие похоже выполняется только из-за того что стоит знак <. Все значения millis() что меньше 5000 зажигают светодиод, а другие просто не попадают в равенство из-за рассинхрона.

    Пробовал исключать библиотеки, в том числе и plcStandardLib_1.h, запускал на другой платформе Nano. Программа одинаково себя ведет.

    Может часть кода отвечающую за сброс счетчика нужно делать через прерывания?

    Код (C++):
    #include <Wire.h> //I2C library
    #include <LiquidCrystal_I2C_OLED.h>

    LiquidCrystal_I2C OLED1(0x27,16,2);  // Устанавливаем дисплей

    int stat = 0; //отслеживание состояния программы
    int pzem_reset=2; //D4 - GPIO2 - сброс счетчика PZEM004
    int interval1=5000; //интервал для нахождения светодиода во включенном состоянии
    int interval2=5400; //интервал для нахождения светодиода во включенном состоянии
    int interval3=5800;
    unsigned long previousMillis=0;
    bool on_off=false;
    bool run_pzem_res=false;
    ////////////Soft_RTC settings
    unsigned long timeNow = 0;
    unsigned long timeLast = 0;
    //Time start Settings:
    int seconds = 0;
    int minutes = 0;
    ///////////////////////////////
    void setup()
    {
    OLED1.init();                
    OLED1.clear();
    pinMode(pzem_reset, OUTPUT);  //сброс счетчика PZEM004
    }

    void loop()
    {
    OLED1.setCursor(0, 0);  //начало верхней строки
    OLED1.print(seconds);
    OLED1.setCursor(0, 1);  //начало нижней строки
    OLED1.print(stat);
    Soft_RTC();  // функция часов реального времени
    pzem_reset_on(); //Формирует временные интервалы для оптопары сброса счетчика PZEM004.
    pzem_reset_out();
    }

    void Soft_RTC()
    {
    timeNow = millis()/1000;
    seconds = timeNow - timeLast;
    if (seconds == 60) {
    timeLast = timeNow;
    minutes = minutes + 1; }
    }

    void pzem_reset_on()
    {
    if (seconds == 5) {run_pzem_res=true; previousMillis = millis(); stat=1;}
    else{
    if (millis() - previousMillis < interval1 & run_pzem_res==true) {on_off=true; stat=2;}
    else{
    if (millis() - previousMillis == interval1 & run_pzem_res==true) {on_off=false; stat=3;}
    else{
    if (millis() - previousMillis == interval2 & run_pzem_res==true) {on_off=true; stat=4;}
    else{
    if (millis() - previousMillis == interval3 & run_pzem_res==true) {on_off=false; run_pzem_res=false; stat=5;}
    }}}}
    }

    void pzem_reset_out()
    {
    digitalWrite(pzem_reset, on_off);
    }
     
    Последнее редактирование: 30 ноя 2017
  9. Fireforce

    Fireforce Нуб

    Через рандомные промежутки времени программа все же выполняется по нужному алгоритму, но потом снова продолжает висеть, доходя только до 2-го шага. Где-то раз в полчаса отрабатывает правильно. Один раз получилось что тольлько до 4-го шага дошла.
     
  10. b707

    b707 Гуру

    у вас функция Soft_RTC написана абсолютно неправильно, секунды меняются в хаотическом порядке в зависимости от "медленности" всей программы. Не удивлюсь, если время на этих "часах" никогда не достигает даже 1 минуты. Зачем они, кстати, нужны? Отработка 5 секундных интервалов пишется на миллис, RTC тут совершенно лишний.

    Рассуждения вверху о "уровнях абстракции" и "не успевающих таймерах" примерно настолько же близки к реальности, как теория влияния НЛО на творчество Пушкина...
     
    Последнее редактирование: 4 дек 2017
  11. b707

    b707 Гуру

    И еще дам действительно добрый и полезный совет:

    НИКОГДА не давайте себе поблажки, пытаясь обьяснить глюки своей программы "кривыми библиотеками", "недоработками авторов Ардуино" или "глючной китайской платой".


    Практика показывает, что проблемы новичков на 100% обьясняются их собственной неумелостью. Даже если вы просидели над кодом неделю, а он не работает - продолжайте искать свои ошибки.
    Допустить, что причина, может быть, не в вашем коде - разрешается только тогда, когда ваш уровень вырастет настолько, что вы сможете читать код библиотек как свой собственнй и действительно начнете находить ошибки в чужих библиотеках.
     
    IvanUA и Fireforce нравится это.
  12. Fireforce

    Fireforce Нуб

    Soft_RTC нужна только для отладки, чтобы на 5-й секунде активировать работу pzem_reset_on(), дальше уже неважно до скольки будет считать. Просто чтобы после загрузки скетча не сразу запускалась эта функция. Хотя можно было просто сделать кнопкой с дискретного входа, но это неудобно каждый раз нажимать.
    Модуль DS3231 у меня один и на время отладки этого проекта, чтобы не тыкать его из одного ус-ва в другое, я заменил его куском кода из полноценной софт-реализации DS3231, лиж бы он считал до пяти..
    Вообще в реальности pzem_reset_on() должна будет запуститься 1-го числа каждого месяца по команде от модуля часов реального времени DS3231, и сбросить накопленные за месяц показания хардверного счетчика PZEM004T.

    По идее на плате DS3231 присутствуют два настраиваемых будильника и они уже точно не зависят от времени цикла и тормозов программы в ESP.
    Но за месяц разбора с этой программой мне уже просто интересно что же я делаю не так.

    Я пробовал применял millis() через
    Код (C++):

    //====================================
    //сброс счетчика //

    // запуск pzem_reset_on()//
    void start_for_pzem_reset_on()
    {
      out_r_tr = R_TRIG1.Run(minute_time=1);
      if (out_r_tr==true)
      {var_comutator=1;}
    }

    void pzem_reset_on()
    {
    switch (var_comutator)
    {
    case 1:
      previousMillis = millis();
      interval=5000; //интервал для нахождения светодиода во включенном состоянии
      digitalWrite(pzem_reset, HIGH);
      while (millis() - previousMillis < interval){  //вошли в цикл, проверяем не прошел ли нужный интервал.
      if (millis() - previousMillis == interval) break;}
      var_comutator=2;
      digitalWrite(pzem_reset, LOW);
      previousMillis = millis(); //сохраняю время последнего переключения светодиода
      interval=300; //интервал для нахождения светодиода в выключенном состоянии

    case 2:
      while (millis() - previousMillis < interval){ //вошли в цикл, выход из него - когда выражение станет ложно.
      if (millis() - previousMillis == interval) break;}
      var_comutator=3;
      digitalWrite(pzem_reset, HIGH);
      previousMillis = millis();
      interval=400;  //интервал для нахождения светодиода во включенном состоянии

    case 3:
      while (millis() - previousMillis < interval){
      if (millis() - previousMillis == interval) break;}
      var_comutator=0;
      digitalWrite(pzem_reset, LOW);
       //цикл работы закончен.
    }}

    через FOR работает, но счет идет только до 1366 и дальше заново.
    Код (C++):
    void pzem_reset_on()
    {
    for (int count = 0; count < 2000; count ++)
    {if (count<700) digitalWrite(pzem_reset, HIGH);
    {if (count>700) digitalWrite(pzem_reset, LOW);
    {if (count>750) digitalWrite(pzem_reset, HIGH);
    {if (count>850) digitalWrite(pzem_reset, LOW);
    Serial.println(count);
    }}}}}

    В той среде что я работаю есть такие уставки "фиксированное время цикла" и "свободное выполнение программы", возможно ли такое на Ардуино?
     
    Последнее редактирование: 6 дек 2017
  13. Fireforce

    Fireforce Нуб

    В общем то с синтаксисом разобрался, похоже действительно нужно копнуть поглубже. Попробую теперь разобраться в коде библиотек.
     
  14. b707

    b707 Гуру

    для этого Soft_RTC или просто RTC совершенно не нужна.
    Вот такой простенький код запустит вам функцию через 5сек после старта программы:
    Код (C++):
    void setup() {
    prev_millis = millis();
    }
    void loop {
    if (millis() - prev_millis > 5000)  pzem_reset_on();
    }
     
    Посмотрел ваш код - практически все не так.
    Вот, к примеру, из вашего кода... Это прямо иллюстрация из книжки "Как не надо отмерять время функцией миллис".
    Код (C++):

    while (millis() - previousMillis < interval){  //вошли в цикл, проверяем не прошел ли нужный интервал.
      if (millis() - previousMillis == interval) break;}
     
    Первая строка - полный аналог функции delay().Она полностью завешивает программу, во время нее никакие действия в скетче невозможны. Если вам надо просто остановить программу на заданное время - так и пользуйтесь delay. миллис совсем не для этого. Вторая строка - это вообще блеск. Обьясните мне, для чего она нужна, если условие при входе в цикл уже гарантирует выход при окончании интервала?

    В коде на основе циклов FOR - во первых, совершенно непонятно, причем здесь время? - Во вторых, синтаксис ужасен, зачем вокруг всех условий пять фигурных скобок? - все , кроме первой - бесполезны...
    В ардуино такого нет. Более того, не советую рассчитывать на более-менее постоянное время даже простых циклов. Да оно и совершенно не нужно - программы для ардуино пишутся совсем не так.
     
  15. b707

    b707 Гуру

    Библиотеки здесь не причем. Вам туда рано. Судя по вашему коду -вам надо взять какую-то книжку по ардуино для начинающих и решать из нее примеры с самого начала, начиная с мигания диодом.
     
  16. Fireforce

    Fireforce Нуб

    Для проверки функции отсчета времени
    Код (C++):
    #include <Wire.h>
    #include <RtcDS3231.h>
    #include <plcStandardLib_1.h>
    #include <LiquidCrystal_I2C.h>

    LiquidCrystal_I2C lcd(0x27, 16, 2);


    int stat = 0; //отслеживание состояния программы
    int pzem_reset=16; //D0 - IO16 - сброс счетчика PZEM004
    unsigned long interval1=5000;  //интервал для нахождения светодиода во включенном состоянии
    unsigned long interval2=5400;  //интервал для нахождения светодиода в выключенном состоянии
    unsigned long interval3=5800;  //интервал для нахождения светодиода во включенном состоянии
    unsigned long previousMillis=0;
    R_TRIG R_TRIG1;
    bool out_r_tr;
    boolean on_off=false;
    boolean run_pzem_res=false;
    int minute_time;
    char str[15];   //declare a string as an array of chars
    bool a;
    ////////////Soft_RTC settings
    unsigned long timeNow = 0;
    unsigned long timeLast = 0;
    //Time start Settings:
    int seconds = 0;
    int minutes = 0;

    RtcDS3231<TwoWire> rtcObject(Wire); //Uncomment for version 2.0.0 of the rtc library

    //Конфигурация//
    void setup()
    {
      timeLast=millis()/1000;
    lcd.begin();                
    pinMode(pzem_reset, OUTPUT);//D0 сброс счетчика PZEM004
    }

    //Цикл//
    void loop()
    {
    RTC();
    lcd.setCursor(0, 0);
    lcd.print(str);
    lcd.setCursor(0, 1);
    lcd.print(seconds);
    lcd.setCursor(7, 1);
    lcd.print(stat);
    Soft_RTC();
    pzem_reset_on(); //Формирует одиночный импульс и временные интервалы для оптопары сброса счетчика PZEM004.
    pzem_reset_out();
    }

    void RTC()
    {
    RtcDateTime currentTime = rtcObject.GetDateTime();    //get the time from the RTC
    sprintf(str, "%d/%d/%d %d:%d:%d",     //%d allows to print an integer to the string
              currentTime.Year(),   //get year method
              currentTime.Month(),  //get month method
              currentTime.Day(),    //get day method
              currentTime.Hour(),   //get hour method
              minute_time = currentTime.Minute(), //get minute method
              currentTime.Second()  //get second method
             );
    }

    void Soft_RTC()
    {
    timeNow = millis()/1000;
    seconds = timeNow - timeLast;
    if (seconds == 60) {
    timeLast = timeNow;
    minutes = minutes + 1; }
    }

    void pzem_reset_on()
    {
      if (seconds == 5) a=true; //сначала преобразовываем десятичное число в Булево, чтобы можно было использовать в управлении R_TRIG.
    out_r_tr = R_TRIG1.Run(a == true);
      if (out_r_tr==true){previousMillis = millis(); run_pzem_res=true;}

    if (millis() - previousMillis < interval1 && run_pzem_res==true) {on_off=true; stat=1;}                  
    if (millis() - previousMillis >= interval1 && millis() - previousMillis <= interval2 && run_pzem_res==true) {on_off=false; stat=2;}
    if (millis() - previousMillis >= interval2 && millis() - previousMillis <= interval3 && run_pzem_res==true) {on_off=true; stat=3;}
    if (millis() - previousMillis > interval3 && run_pzem_res==true) {on_off=false; run_pzem_res=false; a=false; stat=4;}


    /*
    if (millis() - previousMillis < interval1 && run_pzem_res==true) {on_off=true; stat=2;}
    if (millis() - previousMillis == interval1 && run_pzem_res==true) {on_off=false; stat=3;}
    if (millis() - previousMillis == interval2 && run_pzem_res==true) {on_off=true; stat=4;}
    if (millis() - previousMillis == interval3 && run_pzem_res==true) {on_off=false; run_pzem_res=false; stat=5;}
    */

    }

    void pzem_reset_out()
    {
    digitalWrite(pzem_reset, on_off);
    }

    Код с множеством условий в функции pzem_reset_on(), что закоментирован , - не работает.
    Светодиод просто засвечивается и больше не выключается.
    Я переписал эту функцию, и заменил знаки == на "нечеткую логику" >= <=.
    Все заработало как нужно. Причем работает хоть с присутствующим в программе Serial.println(), хоть без него. При этом нормально функционирует и экран и RTC.
    Похоже влияет немного плавающее время цикла. И при начале следующей интерации в millis() может уже присутствовать нужное для срабатывания условия значения, но при этом до условия еще не дошло сканирование строк. Поэтому хоть и с небольшой погрешностью но условие все таки срабатывает при "нечеткой логике".

    Прописываю эту функцию в свой основной проект. Там она тоже работает, но не так как нужно. Интервалы не регулируются. Только примерно 10 секунд получаются. На скрине init.PNG инициализация Wi-Fi, сбор и выдача измеренных значений со счетчика(был не подключен, поэтому только нули).
    В монитор через Serial.println() вывел секунды, там они видны кратно идут 10, 20...
    А вот между 10 и 20 идет сбор данных и моя функция похоже не видит millis()
    Просмотрел библиотеки. В одной - BlynkSimpleEsp8266.h как раз в той что занимается инициализацией Wi-Fi соединения, есть такая строчка:
    Код (C++):
    while (WiFi.status() != WL_CONNECTED) {
                ::delay(500);
    т.е ждать пока не соединится. И еще пока сбор данных идет тоже millis() не видна - пробовал через Serial.println() мониторить. В остальных бибках все через millis() сделано.
     
    Последнее редактирование: 8 дек 2017
  17. DIYMan

    DIYMan Guest

    Прочитайте про функцию yield, и тогда поймёте, как можно обойти delay, который в библиотеках понатыкан.
     
    Fireforce нравится это.
  18. Fireforce

    Fireforce Нуб

    Заработала функция по нужному мне алгоритму.
    Я добавил условия прерывания для pzm(), тем самым отдав приоритет на выполнение pzem_reset_on().
    Теперь соберу в кучу железки и протестирую в реальных условиях. Как еще сервер Blynk такие изгаления с выдачей ему данных воспримет.
    Код (C++):
    void interrupt_pzm() //функция прерывания сбора и выдачи данных
    {
    if (a == false) pzm(); //сбор параметров со счетчика и их выдача на сервер разрешен только если не активна функция pzem_reset_on()
    }

    void pzem_reset_on()
    {
    if (rtc_day == 1 && res == false) a = true; //сначала преобразовываем десятичное число в Булево, чтобы можно было использовать в управлении R_TRIG.
    out_r_tr = R_TRIG1.Run(a == true);
    if (out_r_tr == true){
         previousMillis = millis(); run_pzem_res = true; res = true; //res устанавливаю в true, чтобы 2-й раз в этот день не сработало,а по наступлении 2-го дня переменую "res" возвращаю в исходное состояние.
         }
    if (millis() - previousMillis < interval1 && run_pzem_res == true){
         on_off=true;
         }
    if (millis() - previousMillis >= interval1 && millis() - previousMillis <= interval2 && run_pzem_res == true){
         on_off = false;
         }
    if (millis() - previousMillis >= interval2 && millis() - previousMillis <= interval3 && run_pzem_res == true){
         on_off = true;
         }
    if (millis() - previousMillis > interval3 && run_pzem_res==true){
         on_off = false; run_pzem_res = false; a = false;
         }
    }

    void logic_res()
    {
    if (rtc_day == 2) res = false; //"res" возвращаю в исходное состояние, подготавливая к срабатыванию pzem_reset_on() в начале следующего месяца
    }

    void pzem_reset_out()
    {
    digitalWrite(pzem_reset, on_off);
    }
     
  19. Fireforce

    Fireforce Нуб

    Спасибо за подсказку, почитаю . Нашел ваши посты в теме про многопоточность в Ардуино.
     
  20. brokly

    brokly Гик

    В случае с ESPшкой на этой функции уже висит обработка WiFi, кроме того в рекомендациях (которые вы наверное читали) написано, что вы обязаны через некие промежутки времени (на память не помню как часто) сами вызывать yield() или delay(...) иначе упадет вайвай.
     
    Fireforce нравится это.