Помогите избавиться от delay

Тема в разделе "Arduino & Shields", создана пользователем Securbond, 8 ноя 2018.

Метки:
  1. Securbond

    Securbond Гуру

    Здравствуйте гуру arduino и тд и тп ))
    Около года назад прилепил на балконе светодиодную матрицу, на которую в тёмное время суток выводится время и температура с погодной станции. Теперь мучаюсь над идеей выводить на неё текст из топика mqtt.
    Проблема в том, что в функции вывода текста на матрицу присутствует задержка в виде delay, которая не даёт основному циклу вовремя получить информацию из топика. Если кому то будет не сложно то помогите избавиться от delay. Спасибо !

    Код (C++):
    //*****Подключаем необходимые библиотеки и файлы*****//
    #include <Adafruit_GFX.h>
    #include <Adafruit_NeoMatrix.h>
    #include <Adafruit_NeoPixel.h> //В данном скетче можно не использовать
    #include <Wire.h>
    #include "RtcDS3231.h"
    #include <ESP8266WiFi.h>
    #include <WiFiUdp.h>
    #include <PubSubClient.h>
    #define PIN 2 //Пин на который подключена матрица
    uint8_t brightness = 250; //Яркость матрицы
    uint8_t matrixspeed = 115; //Скорость бегущей строки
    boolean WIFI_connected = false; //Переменная для запоминания статуса WiFi (в данном скетче никак не используется)
    bool RTC_OK; //Переменная для проверки состояния обновления времени в RTC модуле
    bool tFlag; //флаг для конструкции "не чаще 1 раза в секунду"
    bool stat;
    bool messflag;
    uint8_t Hour_old; //хранение текущего часа (для проверки на изменение)
    uint8_t RTC_Minute; //хранение минут модуля RTC
    uint8_t RTC_Second; //хранение секунд модуля RTC

    char TimeStr[12]; //Переменная для хранения строки времени, для вывода на матрицу
    char TempStr[10]; //Переменная для хранения строки температуры, для вывода на матрицу
    unsigned long timeout2;//Переменная для таймера

    String txtInfo = "";
    int txtInfoLen;
    String txtOld = "";
    String txtInfo2 = "";
    int txtInfoLen2;
    int txtInfoLen3;

    const char* ssid = "Setka"; // your network SSID (name)
    const char* password = "good_password_123"; // your network password
                   //**Данные для сервера MQTT**//
    const char* mqtt_server = "192.168.0.106";//Адрес сервера MQTT
    unsigned int mqtt_port = 1883;
    const char* mqtt_name = "Matrix";
    const char* mqtt_user = "xxxxxxxx";
    const char* mqtt_pass = "xxxxxxxx";
    const char* mqtt_sub_inform = "/pogoda/sensors/tempDS";//Топик в котором находится температура на улице
    const char* mqtt_sub_mess = "/matrix/mess";//Сообщение на матрицу
    const char* mqtt_pub_inform = "/D1_Client/OutMes";
    WiFiClient ESPclient;
    PubSubClient MQTTclient(ESPclient);

                     //** Данные для NTP сервера **//
    unsigned int localPort = 2390; // local port to listen for UDP packet
    IPAddress timeServerIP;        // time.nist.gov NTP server address
    const char* ntpServerName = "pool.ntp.org";
    const int NTP_PACKET_SIZE = 48;// NTP time stamp is in the first 48 bytes ofme
    const int timeZone = 3;        // GMT+3 for Moscow Time
    const long timeZoneOffset = timeZone * 3600;
    byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

    WiFiUDP udp;
    RtcDS3231 rtc;
                //** Данные для инициализации матрицы светодиодов **//
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(128, PIN, NEO_GRB + NEO_KHZ800); //В данном скетче можно не использовать
    Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 8, PIN,
      NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT +  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
      NEO_GRB           + NEO_KHZ800);
    int x    = matrix.width();

    void setup() {
      RTC_OK = 0;
      tFlag = false;
      messflag = false;
      timeout2 = millis();
      pinMode(BUILTIN_LED, OUTPUT);
      Serial.begin(115200);
      Serial.println();
      //***Выводим информацию о модуле ESP*////
      /*
      Serial.println();
      Serial.print("ESP chip ID: ");
      Serial.println(ESP.getChipId());
      Serial.print("ESP FlashChip ID: ");
      Serial.println(ESP.getFlashChipId());
      Serial.print("ESP size of the current sketch: ");
      Serial.println(ESP.getSketchSize());
      Serial.print("ESP flash chip size/real size: ");
      Serial.print(ESP.getFlashChipSize());
      Serial.print(" / ");
      Serial.println(ESP.getFlashChipRealSize());

      */

      setup_wifi(); //Подключаемся к WiFi
      delay(20);
      //*****************************
      Serial.println("Starting UDP");
      udp.begin(localPort);
      Serial.print("Local port: ");
      Serial.println(udp.localPort());
      Wire.begin(SDA, SCL); // "Эти Константы определены в файле платы ESP которую Вы выбрали в настройках
      rtc.begin();
      delay(200);
    // *********** MQTT client *********************
      MQTTclient.setServer(mqtt_server, mqtt_port);
      MQTTclient.setCallback(callback);
      MQTTclient.connect(mqtt_name);
      MQTTclient.subscribe(mqtt_sub_inform);
      MQTTclient.subscribe(mqtt_sub_mess);
    }

    void ntp_update(){ //функция обновления NTP времени

      WiFi.hostByName(ntpServerName, timeServerIP);
      sendNTPpacket(timeServerIP); // send an NTP packet to a time server

      unsigned long timeout = millis() + 1000;
      while ((udp.available() < NTP_PACKET_SIZE) && (millis() < timeout)) {}
      int cb = udp.parsePacket();
      if (! cb) {
        Serial.println("no packet yet");
      }
       else  {
        Serial.print("packet received, length=");
        Serial.println(cb);
        // We've received a packet, read the data from it
        udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
        // combine the four bytes (two words) into a long integer
        // this is NTP time (seconds since Jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;
        Serial.print("Seconds since Jan 1 1900 = " );
        Serial.println(secsSince1900);

        // now convert NTP time into everyday time:
        Serial.print("Unix time = ");
        // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
        const unsigned long seventyYears = 2208988800UL;
        // subtract seventy years:
        unsigned long epoch = secsSince1900 - seventyYears;
        // print Unix time:
        Serial.println(epoch);    
        uint32_t rtcEpoch = rtc.getEpoch();
        Serial.print("RTC epoch = ");
        Serial.println(rtcEpoch);
        rtc.setEpoch(epoch + timeZoneOffset);
       if (abs(rtcEpoch - timeZoneOffset - epoch) > 1) {
          Serial.print("Updating RTC (epoch difference is ");
          Serial.print(abs(rtcEpoch - timeZoneOffset - epoch));
          Serial.println(')');    
          delay (300);  
        } else {
          RTC_OK = 1;
          Hour_old = rtc.getHour();  
          Serial.println("RTC date and time are synchronized.");
          }
        }
    }

    unsigned long sendNTPpacket(IPAddress& address) { //Пакет NTP
      Serial.println("sending NTP packet...");
      // set all bytes in the buffer to 0
      memset(packetBuffer, 0, NTP_PACKET_SIZE);
      // Initialize values needed to form NTP request
      // (see URL above for details on the packets)
      packetBuffer[0] = 0b11100011;   // LI, Version, Mode
      packetBuffer[1] = 0;     // Stratum, or type of clock
      packetBuffer[2] = 6;     // Polling Interval
      packetBuffer[3] = 0xEC;  // Peer Clock Precision
      // 8 bytes of zero for Root Delay & Root Dispersion
      packetBuffer[12] = 49;
      packetBuffer[13] = 0x4E;
      packetBuffer[14] = 49;
      packetBuffer[15] = 52;
      udp.beginPacket(address, 123); //NTP requests are to port 123
      udp.write(packetBuffer, NTP_PACKET_SIZE);
      udp.endPacket();
    }


    }
     

    Вложения:

    • for_forum.zip
      Размер файла:
      8,9 КБ
      Просмотров:
      233
    Последнее редактирование: 8 ноя 2018
  2. Securbond

    Securbond Гуру

    Код (C++):

    void setup_wifi() { //функция подключения к WiFi
      Serial.print("Connecting to ");
      Serial.println(ssid);
      WiFi.persistent(false);
      WiFi.mode(WIFI_OFF);
      WiFi.begin(ssid, password);
      WiFi.setAutoReconnect(true);                                   //automatically reconnects to hwAP in case it is disconnected
      int i = 0;
      while (WiFi.status() != WL_CONNECTED && i < 15) {
        delay(500);
        i++;
        Serial.print(".");
      }
      Serial.println();
      Serial.println(" WiFi connected");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
      WIFI_connected = true;
    }

    void callback(char* topic, byte* payload, unsigned int length) {//callback MQTT

      if(String(topic) == mqtt_sub_inform) {
         txtInfoLen =  length;
         txtInfo = "";
        for (int i = 0; i < length; i++) {                                          
           txtInfo += ((char)payload[i]);
        }
           txtInfo.toCharArray(TempStr, 10);  // копируем 10 символов в массив TempStr
           float temp = atof(TempStr);
           txtInfo = (String(temp,1)+"\xB0"+"C"); //Добавляем к температуре  "°С" (в сериал выводит квадратик)
        }  
    }
    void reconnect(){//функция реконнекта к серверу MQTT
      if(!ESPclient.connected() && WiFi.status() == WL_CONNECTED) {
        Serial.print("Attempting MQTT connection...");
        if(MQTTclient.connect(mqtt_name, mqtt_user, mqtt_pass)) {
          Serial.println("connected");
          MQTTclient.subscribe(mqtt_sub_inform);
          MQTTclient.subscribe(mqtt_sub_mess);
        } else {
          Serial.print("failed, rc= ");
          Serial.println(MQTTclient.state());
          delay(1000);
          ESP.restart(); //Если не удалось подключиться к MQTTht,ребутимся через 1 сек. (костыль, потому что я рукожоп)
        }
      }
    }

    void showText( String t, uint16_t color, int wait, int len){//функция вывода текста на матрицу
      matrix.setTextColor(color);

      while (--x > (len*(-1)))
      {
        matrix.fillScreen(0);
        matrix.setBrightness(brightness);
        matrix.setCursor(x,0);
        matrix.print(utf8rus(t));
        matrix.show();
        delay(wait); // Проблема тут !
      }
        x = matrix.width();
    }
    void DisplayTemp(void){ //функция вывода температуры из топика на матрицу
        stat = true;
        showText(txtInfo,matrix.Color(252, 240, 5), matrixspeed, 52);
        Serial.println(txtInfo);      
        stat = false;
    }
    void DisplayTime(void){//функция вывода времени на матрицу

       int m = rtc.getMinute();
       int h = rtc.getHour();

    if (m < 10) {
       Serial.print("Time: ");
       Serial.print(h);
       Serial.print(":0");
       Serial.println(m);
       sprintf(TimeStr,"   %d:0%d ",h,m );
       showText(TimeStr,matrix.Color(199, 21, 133), matrixspeed, 60);
       }
       else {
       Serial.print("Time: ");
       Serial.print(h);
       Serial.print(":");
       Serial.println(m);
       sprintf(TimeStr,"   %d:%d ",h,m );
       showText(TimeStr,matrix.Color(199, 21, 133), matrixspeed, 60);
       }
       DisplayTemp();
    }
       //ОСНОВНОЙ ЦИКЛ !!!
    void loop() {
    if (RTC_OK == 0) { // Если флаг обновления времени == 0, то запускаем синхронизацию с NTP сервером.
    ntp_update();
    }
    RTC_Minute =  rtc.getMinute();
    RTC_Second =  rtc.getSecond();

    if (Hour_old != rtc.getHour()){ //если обновился час, сверяем время с NTP сервером
      Serial.println("Обновился час");
      RTC_OK = 0;  //Раз в час обнуляем переменную, что бы сверить время с Ntp серверо
    }

    if (!MQTTclient.connected()) { // проверяем подключение к MQTT
        reconnect();
      }

    MQTTclient.loop();

    if  (stat == false && tFlag == false ){
        char msg[10];
        sprintf(msg, "%d", (ESP.getFreeHeap()));
        MQTTclient.publish(mqtt_pub_inform, msg);
        DisplayTime();
        tFlag = true;
        timeout2 = millis() + 500;      
    } else {
          if (millis() > timeout2 ) tFlag = false;
           }
    }

    Проблемная функция - void showText
     
    Последнее редактирование: 8 ноя 2018
  3. Daniil

    Daniil Гуру

    Гуглить "задержка без delay"
     
  4. Securbond

    Securbond Гуру

    Спасибо, что подсказали. Сам как то не гуглил и не пробовал и не пытался.. дня 4 как не пытался.. (сарказм)...
     
  5. ИгорьК

    ИгорьК Гуру

    На адафруте есть три статьи для ардуинщиков по multi-tasking
    Вторая и третья рекомендую к прочтению.
    Вот вторая: https://learn.adafruit.com/multi-tasking-the-arduino-part-2?view=all
    Вот третья: https://learn.adafruit.com/multi-tasking-the-arduino-part-3?view=all
     
    Daniil, Сусемьбек и arkadyf нравится это.
  6. ИгорьК

    ИгорьК Гуру

    (С учетом второй ссылки выше)
    То есть, надо тебе затолкать всю твою работу с лентой в класс и апдейтить его по кругу. Но это переписать весь код.
    Все ИМХО. До прихода старших товарищей, если придут.
     
    arkadyf нравится это.
  7. parovoZZ

    parovoZZ Гуру

    юзай таймер, прерывания от него и остановку проца.
     
  8. DetSimen

    DetSimen Guest

    А чо надо то?
    Весь авнакод не буду матреть, выклади суда проблемную функцию
     
  9. DIYMan

    DIYMan Guest

    Как у вас с памятью-то всё вольно - String по значению передаётся, плюс постоянно в цикле перекодировка из UTF-8 вызывается :( Короче, немного взгрустнулось. Это я про одну функцию showText, другие не смотрел даже.

    Варианты решения проблемы есть:

    1. Юзаем таймер, по его тику меняем положение x (как я понял, у вас бегущая строка), перерисовываем матрицу;

    2. Отказываемся от блокирующей перерисовки бегущей строки, делаем её неблокирующей: заводим переменную под значение millis(), в loop проверяем - настало время сдвинуть текст? Если да - сдвигаем, перерисовываем, если нет - курим.
     
    Securbond и DetSimen нравится это.
  10. Securbond

    Securbond Гуру

    Перекодировка нужна для вывода русских букв.
    Второй вариант более мне понятен с ним пока и воюю
     
  11. DIYMan

    DIYMan Guest

    Я понимаю, что перекодировка нужна, вопрос в другом: ЗАЧЕМ её делать КАЖДЫЙ раз внутри цикла while, если достаточно - сделать ОДИН раз - до цикла while?

    Про передачу String по значению вы, наверное, не поняли, да?
     
  12. Securbond

    Securbond Гуру

    Нее не понял.., хотелось бы решать проблемы постепенно. Пока что с millis(); бьюсь.
     
  13. Securbond

    Securbond Гуру

    Решил начать с основ.. Дальше больше.
    Матрицу пока оставил впокое, там слишком много условий нужно сделать, что бы избавиться от delay и не нарушить работу всего остального. а ниже элементарный пример применения millis() вместо delay().
    Код (C++):
    #include <Adafruit_NeoPixel.h>
    #define PIN 2 //Пин с лентой
    #define NUMPIXELS 8 //количество светодиодов в ленте
    long previousMillis = 0;
    long previousMillis2 = 0;
    long interval = 500;
    long interval2 = 2000;
    int i = 0;
    byte stat=0;

    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    void setup() {
    Serial.begin(115200);
    pinMode(LED_BUILTIN,OUTPUT);
    pixels.begin(); // This initializes the NeoPixel library.
    }

    void loop() {
    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis2 > interval2) {
    previousMillis2 = currentMillis;
    digitalWrite(LED_BUILTIN,stat);
    Serial.println("тик");
    stat=!stat;
      }

      if(currentMillis - previousMillis > interval) {
       previousMillis = currentMillis;
       pixels.setPixelColor(i, pixels.Color(0,0,0)); // гасим пиксель/
       i++;
      }

      if (i < NUMPIXELS){
        pixels.setPixelColor(i, pixels.Color(0,150,0)); // Зажигаем пиксель i
        pixels.show();
      } else i = 0;
           
      }

    Может пригодится таким же нубам как я :D
     
    Последнее редактирование: 9 ноя 2018
    ИгорьК нравится это.
  14. ИгорьК

    ИгорьК Гуру

    Все таки подчитай те ссылки что я прислал. Без них, ИМХО, развиваться будет трудно.
     
  15. SergeiL

    SergeiL Оракул Модератор

    Все правильно!
    От delay() нужно избавляться, но руки пока не дошли.
    Писал об этом здесь .
    Как временное решение, для работы MQTT, можно добавить
    Код (C++):
    client.loop();
    в цикл, например, после delay();
     
  16. DIYMan

    DIYMan Guest

    Ещё лучше - объявить yield и внутри неё делать client.loop() ;)
     
  17. Securbond

    Securbond Гуру

    Потихоньку изучаю статьи из Ваших ссылок, но создание классов для меня это пока что то из разряда запуска спутника на орбиту марса :confused:
     
  18. Securbond

    Securbond Гуру

    Продолжение эпопеи с выводом текста.. Текст уже выводиться без delay, но нужно добавить условия, при которых новые данные не выводятся на матрицу, пока не прошел цикл вывода старого сообщения.
    Код (C++):
    #include <Adafruit_GFX.h>
    #include <Adafruit_NeoMatrix.h>
    #include <Adafruit_NeoPixel.h>
    #include <Wire.h>

    #define PIN 2 //Пин с лентой
    #define NUMPIXELS 8 //количество светодиодов в ленте
    uint8_t brightness = 250; //Яркость матрицы

    String txtInfo = "Privet Medved";
    int txtInfoLen;

    long previousMillis = 0;
    long previousMillis2 = 0;
    long interval = 100;
    long interval2 = 2000;
    int i = 0;
    byte stat=0;
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(128, PIN, NEO_GRB + NEO_KHZ800); //В данном скетче можно не использовать
    Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 8, PIN,
      NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT +  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
      NEO_GRB           + NEO_KHZ800);
    const uint16_t colors[] = {
      matrix.Color(255, 0, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255) };

    int x = matrix.width();
    void showText( String t, uint16_t color, int wait, int x){//функция вывода текста на матрицу
        matrix.setTextColor(color);
        t = (utf8rus(t));
        matrix.fillScreen(0);
        matrix.setBrightness(brightness);
        matrix.setCursor(x,0);
        matrix.print(t);
        matrix.show();
    }
    void setup() {
    Serial.begin(115200);
    pinMode(LED_BUILTIN,OUTPUT);
      matrix.begin();
      matrix.setTextWrap(false);
      matrix.setBrightness(40);
      matrix.setTextColor(colors[0]);
    }

    void loop() {
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis2 > interval2) { //просто тикаем в сериал подмигивая при этом светиком на плате
    previousMillis2 = currentMillis;
    digitalWrite(LED_BUILTIN,stat);
    Serial.println("тик");
    stat=!stat;
      }

      //через необходимый интервал вызываем функцию вывода текста, передавая ей x- необходимое значение сдвига текста
      if(currentMillis - previousMillis > interval) { //вызываем функцию вывода
       previousMillis = currentMillis;
        if  ((--x > (52*(-1)))){
          showText(txtInfo,matrix.Color(0, 0, 139), 100, x);
        } else x = matrix.width();
      }
      }
     
    Последнее редактирование: 9 ноя 2018
  19. DetSimen

    DetSimen Guest

    Достаточно начать атоматизировать свой газовый котел и у марса будет новый спутнег.
     
    Securbond нравится это.
  20. ИгорьК

    ИгорьК Гуру

    Не верьте :)

    Это если автоматизировать котлы - которые дедовы ровесники :)
     
    Последнее редактирование: 9 ноя 2018