• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Вопрос Как организовать вызов двух функций с разным циклом выполнения?

Cadil

New member
Используя примеры из Arduino IDE, составил скетч термометра. Информация о температуре отправляется сервер с периодом 1 сек и на е-мейл каждый час. Периодичность отправки определяется delay(). Команды в скетче выполняются последовательно и после отправки письма процесс замораживается на 1 час, естественно, в это время браузер выдает ошибку ВРЕМЯ ОЖИДАНИЯ ИСТЕКЛО ...
Код:
//Сервер со статическим адресом 192.168.1.109
//отправка письма на адрес cadil@ukr.net, 1 час

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <OneWire.h>
OneWire  ds(2);
#include <Arduino.h>

#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;

const char *ssid = "Best_VB";
const char *password = "vbvb3591";
MDNSResponder mdns;

ESP8266WebServer server ( 80 );


byte addr[8];
float celsius;

float getTemp(){
  byte data[12];

  if (!ds.search(addr)) {
    Serial.println("No more addresses.");
    while(1);
  }
   ds.reset_search();
  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      while(1);
  }

 
  ds.reset();         
  ds.select(addr);     
  ds.write(0x44);   
  delay(1000);
 
  ds.reset();
  ds.select(addr); 
  ds.write(0xBE);       

  for (int i = 0; i < 9; i++) {        
    data[i] = ds.read();
  }

  int raw = (data[1] << 8) | data[0];
  if (data[7] == 0x10) raw = (raw & 0xFFF0) + 12 - data[6];
  return raw / 16.0;
 
}


const int led = 13;

void handleRoot() {
  digitalWrite ( led, 1 );
  char temp[400];
celsius = getTemp();
Serial.println(celsius);
int tepm= celsius;
  snprintf ( temp, 400,

"<html>\
  <head>\
    <meta charset='UTF-8' >\
    <meta http-equiv='refresh' content='5'/>\
    <title>Термометр</title>\
    <style>\
      body { background-color: #ffffdf; font-family: Arial, Helvetica, Sans-Serif; Color: #bb0000; }\
    </style>\
  </head>\
  <body>\
    <center><h1>Температура:  %d *C</h1></center>\
    <center><p> Tested! </p></center>\
  </body>\
</html>",tepm
  );
  server.send ( 200, "text/html", temp );
  digitalWrite ( led, 0 );
}

void handleNotFound() {
  digitalWrite ( led, 1 );
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for ( uint8_t i = 0; i < server.args(); i++ ) {
    message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
  }

  server.send ( 404, "text/plain", message );
  digitalWrite ( led, 0 );
}

void browser ( ) {
IPAddress ip(192, 168, 1, 109); //Node static IP
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

  pinMode ( led, OUTPUT );
  digitalWrite ( led, 0 );
  Serial.begin ( 115200 );
  WiFi.begin ( ssid, password );
  WiFi.config(ip, gateway, subnet);   ////Node static IP
  Serial.println ( "" );

  // Wait for connection
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  Serial.println ( "" );
  Serial.print ( "Connected to " );
  Serial.println ( ssid );
  Serial.print ( "IP address: " );
  Serial.println ( WiFi.localIP() );

  if ( mdns.begin ( "esp8266", WiFi.localIP() ) ) {
    Serial.println ( "MDNS responder started" );
  }

  server.on ( "/", handleRoot );
  server.on ( "/test.svg", drawGraph );
  server.on ( "/inline", []() {
    server.send ( 200, "text/plain", "this works as well" );
  } );
  server.onNotFound ( handleNotFound );
  server.begin();
  Serial.println ( "HTTP server started" );
  ////

    USE_SERIAL.begin(115200);
   // USE_SERIAL.setDebugOutput(true);

    USE_SERIAL.println();
    USE_SERIAL.println();
    USE_SERIAL.println();

    for(uint8_t t = 4; t > 0; t--) {
        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
        USE_SERIAL.flush();
        delay(1000);
    }

    WiFiMulti.addAP("Best_VB", "vbvb3591");
}

void mailer() {
    // wait for WiFi connection
    if((WiFiMulti.run() == WL_CONNECTED)) {

        HTTPClient http;
float celsius;
celsius = getTemp();      //?
String adresse;    
adresse = "cadil@ukr.net";

        USE_SERIAL.print("[HTTP] begin...\n");
        // configure traged server and url
      http.begin("http://skorovoda.in.ua/php/un35.php?mymail="+String(adresse)+"&t="+String(celsius));
 
      USE_SERIAL.print("[HTTP] GET...\n");
        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if(httpCode > 0) {
            // HTTP header has been send and Server response header has been handled
            USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);

            // file found at server
            if(httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                USE_SERIAL.println(payload);
            }
        } else {
            USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }

        http.end();
    }

// delay(60000);    //1 минута
delay(3600000);    //1 час
}

void drawGraph() {
  String out = "";
  char temp[100];
  out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
  out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
  out += "<g stroke=\"black\">\n";
  int y = rand() % 130;
  for (int x = 10; x < 390; x+= 10) {
    int y2 = rand() % 130;
    sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
    out += temp;
    y = y2;
  }
  out += "</g>\n</svg>\n";

  server.send ( 200, "image/svg+xml", out);
}
//////////////////////////////////////////
void setup () {
browser ();
}
void loop () {
  mdns.update();
  server.handleClient();
/////
mailer();
}
Казалось бы можно решить эту задачу через цикл, в котором функция отправки письма вызывается 1 раз через 360 000. Вот примерчик для иллюстрации, только j <360 000 вместо 5:
Код:
#include <iostream>
using namespace std;

int main()
{
    setlocale(LC_ALL, "rus");
    int symbInLines = 0;
    char ms = 0;

    cout << endl;
    {
 
        {
                for (int j = 1; j <5; j++)
            {
                cout << "browser ";
            }
            {
                cout << "messenger ";
            }
     
        }
        cout << endl;
    }
    cout << "Введите символ: ";
    cin >> ms;
    cout << endl;
    return 0;
}

Но тут другая проблема: команды для браузера прописаны void setup (), а отправки сообщений - в void loop (). Я пытался перенести отправку сообщений в setup () и наоборот - ничего не получается, поскольку тут замешаны еще и опрос датчика, и подключение к точке доступа Wi-Fi, и т.п. ...

Как решить эту проблему?
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
В ваш код не вникал, но подобное реализуется например так :
В цикле
процедура_которая_выполняется_через_каждую_секунду
задержка_1_сек
увеличение_переменной_прошедших_секунд на единицу
если переменная_прошедших_секунд больше заданной задержки выполняем вторую_процедуру и обнуляем переменную_прошедших_секунд

Второй вариант использовать millis()
 

Cadil

New member
В цикле
процедура_которая_выполняется_через_каждую_секунду
задержка_1_сек
увеличение_переменной_прошедших_секунд на единицу
если переменная_прошедших_секунд больше заданной задержки выполняем вторую_процедуру и обнуляем переменную_прошедших_секунд
Верно! Этот алгоритм только другими словами описан у меня в примере выше. Но как его реализовать, когда процедуру_1 и процедуру_2 выделить в чистом виде затруднительно?
 

Сергей_Ф

Moderator
Команда форума
@Cadil они у вас выделены в чистейшем виде. Никаких проблем нет и переносить ничего не надо.
Отправка письма - mailer();
Обработка запросов веб - handleClient().

Просто вызывайте mailer раз в час и всё.
 

pazyzy

New member
Выше вам @CodeNameHawk все верно ответил.
Я не эксперт в этих библиотеках, поэтому предполагаю на основе методов и их названий.
server.handleClient() - отвечает за обработку запроса на web server (надеюсь, что он не ждет, если от клиента ничего не пришло).
mailer - ваш метод отправки почты.

Вызывая sleep в методе mailer, вы замораживаете систему, и поэтому ваш web server не отвечает браузеру на запрос.
Попробуйте сделать следующее:
1. В строке 199 убрать строку delay(3600000);
2. Метод loop будет выглядеть так
Код:
int seconds_count = 0;
void loop () {
  mdns.update();
  server.handleClient();
/////
if(seconds_count >= 3600)
{
mailer();
seconds_count = 0;
}
seconds_count++;
sleep(1000);
}
Таким образом loop будет работать каждую секунду, позволяя работать обработчику webServer, а почта будет отправляться раз в час.
 

Сергей_Ф

Moderator
Команда форума
@pazyzy зачем тут sleep(1000)? Для проблем работы с обработкой http?
Есть millis. Его и надо пользовать.
 

pazyzy

New member
@pazyzy зачем тут sleep(1000)? Для проблем работы с обработкой http?
Есть millis. Его и надо пользовать.
Про проблемы с обработкой http не в курсе. Просто так использовал, т.к. @Cadil уже использовал sleep в примере, поэтому sleep (или delay) было проще понять и объяснить в этом куске кода.
Можно сделать тоже самое с использованием millis.

Относительно данного примера (если игнорировать возможную задержку ответа http serverа до одной секунды) есть какие-то преимущества использования millis vs sleep?
 

Сергей_Ф

Moderator
Команда форума
какие-то преимущества использования millis vs sleep?
1.не вести собственный счётчик
2.ваш счётчик имеет непредсказуемый шаг, поскольку помимо задержки в секунду будут ещё задержки обработки http.
3.возможность обработки запросов с нескольких клиентов.
 

pazyzy

New member
1.не вести собственный счётчик
2.ваш счётчик имеет непредсказуемый шаг, поскольку помимо задержки в секунду будут ещё задержки обработки http.
1. Нужно хранить время последнего выполнения метода для сравнения со счетчиком, плюс помнить о преполнении (раз в 50 дней?).
2. Тут согласен, будет точнее. Однако все равно все может замереть на несколько секунд при отправке уведомления на почту. Все зависит на сколько нужна какая точность.

@Cadil итого: sleep использовать можно, и все будет работать, но best practice будет использовать millis.
 

Сергей_Ф

Moderator
Команда форума
Нужно хранить время последнего выполнения метода для сравнения со счетчиком, плюс помнить о преполнении (раз в 50 дней?).
Если это сложно, то можно воспользоваться библиотекой Ticker и ничего не хранить и не проверять.
 

Cadil

New member
@Cadil они у вас выделены в чистейшем виде. Никаких проблем нет и переносить ничего не надо.
Отправка письма - mailer();
Обработка запросов веб - handleClient().
Спасибо! Обработка запросов веб - handleClient() - ключевая мысль, которая расставила все по своим местам.
 

Cadil

New member
Попробуйте сделать следующее:
1. В строке 199 убрать строку delay(3600000);
2. Метод loop будет выглядеть так
....
Искренне благодарю за проделанную работу! Скетч с учетом ваших предложений - работает. Единственное, пришлось заменить функцию sleep() (не задекларирована) на delay(). Хотя, как я понял, и sleep() и delay() в этом случае не есть хорошо...
Успехов!
 
Сверху Снизу