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

Нужна помощь Определение наличия подключения к сети WiFi

iG2019

New member
Нужно чтобы ESP при включении работала не зависмо от наличия сети WiFi, т.е. снимала показания датчиков, щелкала реле и т.д. Как только появляется сеть - подключалась к серверу mqtt и передавала на него показания. В случае пропадания сети продолжала нормальную работу.
Начал с простенького кода
Код:
#include <OLED_I2C.h>
#include <ESP8266WiFi.h>            
#include <WiFiClient.h>

char *ssid = "********"; // Имя роутера
char *pass = "********"; // Пароль роутера

OLED  myOLED(D2, D1); 

extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
extern uint8_t BigNumbers[];
void setup() {
   Serial.begin(115200);
  WiFi.begin(ssid, pass);

if(!myOLED.begin(SSD1306_128X32))
 while(1);   // In case the library failed to allocate enough RAM for the display buffer...

  myOLED.setFont(SmallFont);

}

void loop() {
  if(WiFi.status() == WL_CONNECTED) {
digitalWrite(LED_BUILTIN, LOW);
Serial.println("WiFi");}
    }
  
  if(WiFi.status() != WL_CONNECTED) { 
    digitalWrite(LED_BUILTIN, HIGH);
  Serial.println("NO WiFi");
  }

}
Включаю ESP - светодиод загорается, в сериал сыпятся сообщения о наличии WiFi. Выключаю на роутере WiFi - светодиод гаснет, в порт сыпятся сообщения об отсутствии сети. Опять включаю на роутере сеть и ... вот здесь начинаются непонятки. ESP может подключится к сети (светодиод загорается), а может и не подключиться. Аналогичная ситуация происходит если включить ESP при отсутствии сети WiFi. Какой либо закономерности не заметил.
Подскажите что я делаю не так?
 

CodeNameHawk

Moderator
Команда форума
Так наверное вы все практически и сделали. События отключения и подключения отстают от фактического состояния сети.
Установите свойства autoconnect и autoreconect, есп будет сама пере подключаться к сети.
 

enjoynering

Well-known member
WiFi.status() == WL_CONNECTED показывает только наличие соединения с точкой доступа/роутером, а у него то как раз связи с инетом может не быть :). Вам нужен ping

перед отправкой пингуете google.com или православный yandex.ru и если ответ пришел значит интернет есть. Неплохая библиотека обертка ping для arduino esp8266 framework-а ТУТ
 

nikolz

Well-known member
если сеть формируется роутером, то в общем случае надо определять три события
1) работает ли роутер
2) тот ли адрес дал вам роутер (нужный адрес, если он задан фиксировано в ESP, может быть занят)
3) работает в сети устройство с котором обмениваемся данными
подумайте над этим
 

CodeNameHawk

Moderator
Команда форума
1) работает ли роутер
2) тот ли адрес дал вам роутер (нужный адрес, если он задан фиксировано в ESP, может быть занят)
3) работает в сети устройство с котором обмениваемся данными
подумайте над этим
Первые два пункты и проверяются WiFi.status().
Насчет третьего, важно проверять, принял ли сервер посланные данные, а не доступен ли он.
Проверка доступен ли сервер ничего не дает, проверка может пройти, а следующее обращение к нему может не пройти.
 

kotyara12

New member
Не знаю, пригодится ли... Но я делаю это примерно так (см. ниже).
wifiLoop() вставляю в основной цикл, вся "работа внутри" соединения выполняется в callback функции _cbWiFiConnWork().
В этом случае ESP в случае обрыва соединения самостоятельно его восстановит, а при отсутствии - может спокойно функционировать и без интернета, автоматика спокойно работает в автономном режиме.

Код:
typedef enum {
  wifiNotConnected, wifiConnectWait, wifiConnectInit, wifiConnectWork
} wifiConnStage_t; 

bool wifiConnect() 
{
  _ledWiFi->ledBlinkOn(_wifiBlinkConn);

  _wifiTryConnect++;

  Serial.print(F("WiFi :: Connect to network '"));
  Serial.print(_wifiSSID);
  Serial.print(F("', try "));
  Serial.print(_wifiTryConnect);
  Serial.print(F(" "));

  #if defined(ESP32) 
    // -- ESP32 :: begin --------------------------------------------------
    _wifiConnectedEvent = WiFi.onEvent(onWiFiConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
    _wifiGotIPEvent = WiFi.onEvent(onWiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
    if (_wifiDisconnectedEvent != 0) WiFi.removeEvent(_wifiDisconnectedEvent);
    _wifiDisconnectedEvent = 0;
    // -- ESP32 :: end ----------------------------------------------------
  #else
    // -- ESP8266 :: begin ------------------------------------------------
    _wifiConnectedHandler = WiFi.onStationModeConnected(onWiFiConnected);
    _wifiGotIPHandler = WiFi.onStationModeGotIP(onWiFiGotIP);
    _wifiDisconnectedHandler = nullptr;
    // -- ESP8266 :: end --------------------------------------------------
  #endif

  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(false);
  #if defined(ESP32) 
    // -- ESP32 :: begin --------------------------------------------------
    WiFi.begin(_wifiSSID.c_str(), _wifiPass.c_str(), 0, NULL, false);
    // -- ESP32 :: end ----------------------------------------------------
  #else
    // -- ESP8266 :: begin ------------------------------------------------
    WiFi.begin(_wifiSSID, _wifiPass, 0, NULL, false);
    // -- ESP8266 :: end --------------------------------------------------
  #endif
 
  _wifiStage = wifiConnectWait;
  wifiConnTimerStart();
  WiFi.reconnect();
}

bool wifiConnectionFailed(wl_status_t wifiStatus, const byte printStatus) 
{
  Serial.println();
  Serial.println(F("WiFi :: Connection failed!"));
  if (printStatus > 0) {
    wifiPrintStatus(wifiStatus, printStatus > 1); 
  };
 
  wifiConnTimerStop();
  WiFi.disconnect(true);

  #if defined(ESP32) 
    // -- ESP32 :: begin --------------------------------------------------
    if (_cbWiFiConnLost != nullptr) {
      _cbWiFiConnLost(WIFI_REASON_UNSPECIFIED);
    };
    // -- ESP32 :: end ----------------------------------------------------
  #else 
    // -- ESP8266 :: begin ------------------------------------------------
    if (_cbWiFiConnLost != nullptr) {
      _cbWiFiConnLost(WIFI_DISCONNECT_REASON_UNSPECIFIED);
    };
    // -- ESP8266 :: end --------------------------------------------------
  #endif

  if (_wifiTryConnect >= _wifiTryConnectMax) {
    Serial.println();
    Serial.println(F("*****************************************************************************************"));
    Serial.println(F("$$$                                    SYSTEM RESTART                                 $$$"));
    Serial.println(F("*****************************************************************************************"));
   
    wifiConnTimerStop();
    fixResetReason(RR_WIFI_CONN_ATTEMPTS_EXCEEDED);
    ESP.restart();
  };
}

bool wifiLoop()
{
  bool _result = false;

  // Проверяем статус подключения к WiFi
  wl_status_t _wifiStatus = WiFi.status();
 
  // Подключение к WiFi еще не было установлено
  if ((_wifiStatus == WL_IDLE_STATUS) || (_wifiStatus == WL_DISCONNECTED)) {
    switch (_wifiStage) {
      // Нет подключения, запускаем процедуру соединяемся...
      case wifiNotConnected:
        wifiConnect(); 
        break;

      // В процессе подключения...
      case wifiConnectWait:
        yield();
        // -- ESP8266 ONLY ------------------------------------------------------
        #if defined(ESP8266)
          // -- ESP8266 :: begin ------------------------------------------------
          if (_connTimer.onRestart()) {
            wifiConnTimerDo();
          };
          // -- ESP8266 :: end --------------------------------------------------
        #endif
        // -- ESP8266 ONLY ------------------------------------------------------
        break;

      // Что-то пошло не так, пожалуй лучше всего будет сбросить соединение
      default:
        wifiConnectionFailed(_wifiStatus, 2);
        break;
      };
  }
 
  // Подключение к WiFi установлено
  else if (_wifiStatus == WL_CONNECTED) {
    switch (_wifiStage) {
      // Соединение установлено только что
      case wifiConnectWait:
        _result = true;
        // Меняем частоту мигания светодиода
        _ledWiFi->ledBlinkOn(_wifiBlinkInit);
        // Останавливаем таймер подключения и сбрасываем счетчики
        wifiConnTimerStop();
        _wifiTimeSyncRun = 0;
        _wifiStage = wifiConnectInit;
        break;

      // Соединение установлено в предыдущем рабочем цикле
      case wifiConnectInit:
        // Синхронизация времени с NTP, но только если она еще не была ни разу запущена
        if (year() < 2000) {
          // Считаем количество попыток синхронизации времени (так как она почему-то иногда не проходит совсем)
          _wifiTimeSyncRun++;
          if (_wifiTimeSyncRun > _wifiTimeSyncRunMax) {
            // Не удалось синхронизировать время - собрасываем соединение
            wifiConnectionFailed(_wifiStatus, 0);
          }
          else {
            // Запускаем синхронизацию времени c NTP
            _result = true;
            ntpTimeSyncRun();
          };
        }
        else {
          // Ставим отметку, что инициализация завершена
          _wifiStage = wifiConnectWork;
          // Сбрасываем счетчик попыток соединения
          _wifiTryConnect = 0;
          // Выполняем процедуру при установлении соединения
          if (_cbWiFiConnInit != nullptr) {
            _result = true;
            _cbWiFiConnInit(_wifiFisrtConnect);
          };
          // Сбрасываем флаг _wifiFisrtConnect
          if (_wifiFisrtConnect) {
            _wifiFisrtConnect = false;
          };
          // Отключаем мигание светодиода
          _ledWiFi->ledBlinkOff();
        };
        break;
     
      // Соединение установлено и инициализировано
      case wifiConnectWork:
        // Выполняем процедуру при постоянном соединении
        if (_cbWiFiConnWork != nullptr) {
          _result = true;
          _cbWiFiConnWork();
        };
        break;

      // Другое состояние
      default:
        wifiConnectionFailed(_wifiStatus, 2);
        break;
    };
  }

  // Ошибки : WL_NO_SSID_AVAIL, WL_CONNECTION_LOST, WL_CONNECT_FAILED, WL_SCAN_COMPLETED
  else {
    wifiConnectionFailed(_wifiStatus, 1);
  };

  return _result;
};
Я не утверждаю, что код идеален. Нет, он далек от совершенства, но я над ним постоянно работаю :)

Выглядит это примерно так:

Код:
16:30:41.382 -> Initialization...
16:30:41.382 -> Sketch version: 20190628.01
16:30:41.382 -> Inititailize PINs...
16:30:41.382 -> Inititailize WiFi connection...
16:30:41.460 -> Inititailize MQTT connection...
16:30:41.460 -> Inititailize NTP...
16:30:41.460 -> Inititailize sensors...
16:30:41.460 -> Read settings from EEPROM...
16:30:41.460 -> syncRequests = 1
16:30:41.460 -> intvPublishSensors = 300
16:30:41.492 -> publThingSpeak = 1
16:30:41.492 -> Start timers...
16:30:41.492 -> Initialization complete
16:30:41.492 ->
16:30:41.492 -> WiFi :: Connect to network 'Kotyara12', try 1 .
16:30:42.748 -> WiFi :: Connected to ssid 'Kotyara12', channel 12, rssi: -61
16:30:43.054 -> WiFi :: Local IP address: 192.168.1.41
16:30:43.088 -> NTP :: Transmit NTP request to 'ntp1.stratum2.ru' / '88.147.254.230': ok
16:30:43.292 -> NTP :: Current time: 29.06.2019 16:30:48
16:30:43.292 -> TELEGRAM :: Message " Устройство запущено, версия скетча 20190628.01" :: added to queue, total 1 messages
16:30:43.292 -> TELEGRAM :: Send message " Устройство запущено, версия скетча 20190628.01": HTTP/1.1 200 OK
16:30:46.348 -> MQTT :: Connecting to MQTT server '********:***************@m**.cloudmqtt.com:******'...
16:30:46.958 -> MQTT :: Connection completed
16:30:47.264 -> MQTT :: publish '/village/smarthome/sync_status': "1" [0,1] :: ok
16:30:47.366 -> MQTT :: subscribe on topic '/village/smarthome/sync_status'
16:30:47.774 -> MQTT :: subscribe on topic '/village/smarthome/ota'
16:30:48.080 -> MQTT :: subscribe on topic '/village/smarthome/settings/+'
16:30:48.386 -> MQTT :: subscribe on topic '/village/smarthome/settings/+/+'
Если нужно, могу выслать весь модуль
 

nikolz

Well-known member
В SDK есть пример колбека
который позволяет отследить 10(десять) событий Wi-Fi.
 

iG2019

New member
События отключения и подключения отстают от фактического состояния сети.
это я уже понял и сам, но почему то приведенное мной решение не всегда срабатывает.

WiFi.status() == WL_CONNECTED показывает только наличие соединения с точкой доступа/роутером, а у него то как раз связи с инетом может не быть
так мне и не важно есть ли инет. Сервер может быть и локальным

В SDK есть пример колбека
который позволяет отследить 10(десять) событий Wi-Fi.
Можете ткнуть носом конкретно где посмотреть?

з/ы меня бы вполне устроило "стандартное" решение в setup
Код:
while (WiFi.status() != WL_CONNECTED) {
     delay(500);
     Serial.print(".");
но оно работает только в условиях стабильной сети WiFi. Если же сеть отсутствует в момент включения ESP то вся работа тормозится.
 

nikolz

Well-known member
iG2019,
стр 58
рекомендую подробно изучить этот док
в нем есть ответы на все вопросы по использованию ESP8266
---------------------
список событий найдете в файле user_interface.h их всего 11
 

Вложения

iG2019

New member
объяснили бы еще такому рукожопу как я как этим пользоваться...
Вставил пример из SDK в свой код
Код:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

char *ssid = "********"; // Имя роутера
char *pass = "********"; // Пароль роутера

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, pass);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    digitalWrite(LED_BUILTIN, LOW);
  }


  if (WiFi.status() != WL_CONNECTED) {
    digitalWrite(LED_BUILTIN, HIGH);
  }

void wifi_handle_event_cb(System_Event_t *evt);


}



void wifi_handle_event_cb(System_Event_t *evt)
{
  os_printf("event %x\n", evt->event);
  switch (evt->event) {
    case EVENT_STAMODE_CONNECTED:
      os_printf("connect to ssid %s, channel %d\n",
                evt->event_info.connected.ssid,
                evt->event_info.connected.channel);
      break;
    case EVENT_STAMODE_DISCONNECTED:
      os_printf("disconnect from ssid %s, reason %d\n",
                evt->event_info.disconnected.ssid,
                evt->event_info.disconnected.reason);
      break;
    case EVENT_STAMODE_AUTHMODE_CHANGE:
      os_printf("mode: %d -> %d\n",
                evt->event_info.auth_change.old_mode,
                evt->event_info.auth_change.new_mode);
      break;
    case EVENT_STAMODE_GOT_IP:
      os_printf("ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR,
                IP2STR(&evt->event_info.got_ip.ip),
                IP2STR(&evt->event_info.got_ip.mask),
                IP2STR(&evt->event_info.got_ip.gw));
      os_printf("\n");
      break;
    case EVENT_SOFTAPMODE_STACONNECTED:
      os_printf("station: " MACSTR "join, AID = %d\n",
                MAC2STR(evt->event_info.sta_connected.mac),
                evt->event_info.sta_connected.aid);
      break;
    case EVENT_SOFTAPMODE_STADISCONNECTED:
      os_printf("station: " MACSTR "leave, AID = %d\n",
                MAC2STR(evt->event_info.sta_disconnected.mac),
                evt->event_info.sta_disconnected.aid);
      break;
    default:
      break;
  }
}
если я правильно понял то в сериал должны печататься события, но там пусто ...
 

nikolz

Well-known member
объяснили бы еще такому рукожопу как я как этим пользоваться...
Вставил пример из SDK в свой код
Код:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

char *ssid = "********"; // Имя роутера
char *pass = "********"; // Пароль роутера

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, pass);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    digitalWrite(LED_BUILTIN, LOW);
  }


  if (WiFi.status() != WL_CONNECTED) {
    digitalWrite(LED_BUILTIN, HIGH);
  }

void wifi_handle_event_cb(System_Event_t *evt);


}



void wifi_handle_event_cb(System_Event_t *evt)
{
  os_printf("event %x\n", evt->event);
  switch (evt->event) {
    case EVENT_STAMODE_CONNECTED:
      os_printf("connect to ssid %s, channel %d\n",
                evt->event_info.connected.ssid,
                evt->event_info.connected.channel);
      break;
    case EVENT_STAMODE_DISCONNECTED:
      os_printf("disconnect from ssid %s, reason %d\n",
                evt->event_info.disconnected.ssid,
                evt->event_info.disconnected.reason);
      break;
    case EVENT_STAMODE_AUTHMODE_CHANGE:
      os_printf("mode: %d -> %d\n",
                evt->event_info.auth_change.old_mode,
                evt->event_info.auth_change.new_mode);
      break;
    case EVENT_STAMODE_GOT_IP:
      os_printf("ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR,
                IP2STR(&evt->event_info.got_ip.ip),
                IP2STR(&evt->event_info.got_ip.mask),
                IP2STR(&evt->event_info.got_ip.gw));
      os_printf("\n");
      break;
    case EVENT_SOFTAPMODE_STACONNECTED:
      os_printf("station: " MACSTR "join, AID = %d\n",
                MAC2STR(evt->event_info.sta_connected.mac),
                evt->event_info.sta_connected.aid);
      break;
    case EVENT_SOFTAPMODE_STADISCONNECTED:
      os_printf("station: " MACSTR "leave, AID = %d\n",
                MAC2STR(evt->event_info.sta_disconnected.mac),
                evt->event_info.sta_disconnected.aid);
      break;
    default:
      break;
  }
}
если я правильно понял то в сериал должны печататься события, но там пусто ...
это функция колбека wifi
колбеки это такие функции, которые вызывает не пользователь а система
чтобы система знала что эту функцию надо вызвать при наступлении события
надо эту функцию зарегистрировать
регистрация делается один раз
при инициализации
в примере на стр 58 это void user_init() - в самом конце
но это для СИ
для дурины
можно поставить
wifi_set_event_hander_cb(wifi_handle_event_cb);
в setup
---------------------------
оговорюсь, что я на дурине не пишу поэтому возможно и не сработает
можно поискать по документации дурины как регистрировать колбеки
--------------------
 

iG2019

New member
на дурине возможно и не сработает
похоже что да, не работает
Пример WiFiEvents посмотрите.
Порылся в нете по WiFiEventHandler и кое-что нашел WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; Это, думаю, уже можно будет применить
 

=AK=

New member
Нужно чтобы ESP при включении работала не зависмо от наличия сети WiFi, т.е. снимала показания датчиков, щелкала реле и т.д. Как только появляется сеть - подключалась к серверу mqtt и передавала на него показания. В случае пропадания сети продолжала нормальную работу.
Вот пример скетча для Wemos, который это делает. В сетапе он пытается подключиться к WiFi в течении 10 сек, если не получилось - уходит в нормальный режим работы и время от времени пытается подключиться снова и снова (см. coos_task_wifi_reconnect). При пропадании WiFi точно так же, старается снова подключиться.

Когда есть соединение с WiFi , то другая задача, coos_task_mqtt_reconnect, аналогично, следит за соединением с MQTT брокером и переподключается, если связь потеряна.

Параллельно и независимо от этого скетч снимает показания с датчиков, выводит сообщения на свой дисплей, работает с локальной сетью, и т.п. Все это работает на простенькой кооперативной операционной системе, что позволило отделить задачи друг от друга и выполнять иx квази-одновременно.
 

enjoynering

Well-known member
Вот пример скетча для Wemos, который это делает. В сетапе он пытается подключиться к WiFi в течении 10 сек, если не получилось - уходит в нормальный режим работы и время от времени пытается подключиться снова и снова (см. coos_task_wifi_reconnect). При пропадании WiFi точно так же, старается снова подключиться.
очень не плохо, но появился вопрос. зачем это самописный велосипед coos, когда в arduino esp8266 давно есть ticker?
 

=AK=

New member
очень не плохо, но появился вопрос. зачем это самописный велосипед coos, когда в arduino esp8266 давно есть ticker?
Вот этот? Без единого слова описания? Он только для ESP, а coos - для любой Ардино, включая ESP.Причем неизвестно кто из них раньше появился, поскольку на всяких разных платформах coos используется уже лет 15, наверное, с небольшими вариациями.

Или вот этот? Который позволяет вызывать функции через равные интервалы времени? В нем нет гибкости.

coos позволяет варировать задержки по мере необходимости, строить сложные автоматы состояния и циклограммы, в том числе с программно управляемыми задержками, а тикер этого не умеет. Причем тикеры ничуть не в меньшей степени "самописные велосипеды", разница только в том, что более широко известны.

Все-таки coos, хоть и примитивная, но все-таки операционная система, с переключением задач и службой времени, а не просто разновидность таймера Дописав пару строчек кода, в coos можно добавить функциональность, позволяющую задачам останавливать и запускать друг друга, и т.п. Этого не сделано чтобы не пудрить мозги, кому надо - сам сделает.
 
Сверху Снизу