• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Вопрос Переподключение к wifi

YDen

Member
Здравствуйте.
Подскажите пожалуйста. Стоит задача сделать переподключение ЕСП при обрыве связи. Такой код решит эту задачу:
Код:
void setup_wifi()
{
  // We start by connecting to a WiFi network
  // Удаляем предыдущие конфигурации WIFI сети
  WiFi.disconnect(); // обрываем WIFI соединения
  WiFi.softAPdisconnect(); // отключаем отчку доступа(если она была
  WiFi.mode(WIFI_OFF); // отключаем WIFI
  delay(500);

  // присваиваем статичесий IP адрес
  WiFi.mode(WIFI_STA); // режим клиента
  WiFi.config(IPAddress(192, 168, 1, 73), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 1, 1));

  delay(10);
  WiFi.begin(ssid, password);
}
Вызов в loop:
Код:
if (WiFi.status() == WL_CONNECTED)
     {
    //код
      }
  else
  {
    //запуск подключения wifi
    if (currentMillis_wifi - previousMillis_wifi >= interval_wifi)
    {
      previousMillis_wifi = currentMillis_wifi;
      setup_wifi();
    }

  }
благодарю
 

YDen

Member
что мешает проверь это самому?
Этот код у меня работает, правда всего пару дней. Пока траблов не заметил. Ранее постоянно "терялись" ЕСП.
Интересна оптимизация кода.
Стоит ли заново инициализировать соединение с указанием ip, удалять информацию о сетях?
Этот код не лишний?
Код:
// Удаляем предыдущие конфигурации WIFI сети
  WiFi.disconnect(); // обрываем WIFI соединения
  WiFi.softAPdisconnect(); // отключаем отчку доступа(если она была
  WiFi.mode(WIFI_OFF); // отключаем WIFI
  delay(500);
  // присваиваем статичесий IP адрес
  WiFi.mode(WIFI_STA); // режим клиента
  WiFi.config(IPAddress(192, 168, 1, 73), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 1, 1));
благодарю
 

enjoynering

Well-known member
зачем изобретать велосипед. все уже есть в arduino core

Код:
if (WiFi.getAutoConnect() != true) WiFi.setAutoConnect(true);  //on power-on automatically connects to last used hwAP
WiFi.setAutoReconnect(true);                                   //automatically reconnects to hwAP in case it is disconnected
 

enjoynering

Well-known member
Код:
WiFi.softAPdisconnect(); // отключаем отчку доступа(если она была
WiFi.mode(WIFI_OFF); // отключаем WIFI
WiFi.softAPdisconnect(false) - не выключает передатчик, тупо обрывает связь c клиентами путем установления пароля и юзера в NULL. а вот WiFi.softAPdisconnect(true) вызывает WiFi.enableAP(false) которая красиво вызывает WiFi.mode() и отключает AP на совсем.

WiFi.softAPdisconnect(false) достаточно для безглючной смены паролей, ip и тд. То есть не надо дополнительно выключать передатчик командой WiFi.mode(WIFI_OFF).

Уж если хотите временно выключить wifi то делайте так:
Код:
if (WiFi.getPersistent() == true) WiFi.persistent(false);   //disable saving wifi config into SDK flash area
WiFi.mode(WIFI_OFF); // отключаем WIFI
WiFi.persistent(true);   //enable saving wifi config into SDK flash area
перед временным WiFi.softAPdisconnect() тоже надо делать так:
Код:
if (WiFi.getPersistent() == true) WiFi.persistent(false);   //disable saving wifi config into SDK flash area
WiFi.softAPdisconnect(false); //setting ssid/pass to null
WiFi.persistent(true);   //enable saving wifi config into SDK flash area
иначе дырку во флешке протрете
 
Последнее редактирование:

enjoynering

Well-known member
Благодарю.
Эти две строки в сетап поставить?

вас в гугле забанили? один из первых запросов поиске выдал это

* @brief Set whether the ESP8266 station will connect to the recorded AP
* automatically when the power is on. It will do so by default.
*
* @attention 1. If this API is called in user_init, it is effective immediately
* after the power is on. If it is called in other places, it will
* be effective the next time when the power is on.
* @attention 2. This configuration will be saved in Flash system parameter area if changed.

*
* @param bool set : If it will automatically connect to the AP when the power is on
* - true : it will connect automatically
* - false: it will not connect automatically
ну и так как происходит постоянная запись этих параметров во flash при их вызове, то лучше вызывать так

Код:
if (WiFi.getAutoConnect() != true)    //configuration will be saved into SDK flash area
{
  WiFi.setAutoConnect(true);   //on power-on automatically connects to last used hwAP
  WiFi.setAutoReconnect(true);    //automatically reconnects to hwAP in case it's disconnected
}
 
Последнее редактирование:

enjoynering

Well-known member
ну и еще не надо вызывать WiFi.mode(WIFI_STA) перед WiFi.begin(ssid, password).

WiFi.begin(ssid, password) в своем теле вызывает WiFi.enableSTA(true) которая красиво вызывает WiFi.mode(WIFI_STA).

тоже самое с точкой доступа - не надо вызывать WiFi.WiFi.enableAP(true) перед WiFi.softAP(ssid, password).

в WiFi.softAP(ssid, password) все уже есть!!!
 

YDen

Member
вас в гугле забанили? один из первых запросов поиске выдал это



ну и так как происходит постоянная запись этих параметров во flash при их вызове, то лучше вызывать так

Код:
if (WiFi.getAutoConnect() != true)    //configuration will be saved into SDK flash area
{
  WiFi.setAutoConnect(true);   //on power-on automatically connects to last used hwAP
  WiFi.setAutoReconnect(true);    //automatically reconnects to hwAP in case it's disconnected
}
Т.е в сетапе?
 

pvvx

Активный участник сообщества
ну и так как происходит постоянная запись этих параметров во flash при их вызове, то лучше вызывать так
Интересно, а почему происходит постоянная запись параметров, когда это дополнительное действие и любой стандартный пример Arduino начинается с установок режимов WiFi?
Т.е. функции по настройке WiFi у ESP8266 не соответствует базовым стандартам Arduino?
Каков смысл этой встроенной функциональности по сохранению настроек, если она нигде не используется и её надо отлючать?
В чем красота когда существуют параметры по умолчанию, которых вы не знаете, т.к. они берутся из ранее сохраненных от какой-то сессии работы с WiFi?
Всё это похоже введено для усложнения, а не красоты...
Причина разнообразия примеров по настройке WiFi строиться из описанного выше и последующих исправлений в SDK Espressif, с попыткой оставить совместимость с более старыми вариантами кода. Т.е. теперь, в текущей версии, требуются дополнительные костыли для привода кода к базовому варианту функционирования в среде Arduino. В следующей версии это может быть заменено и удалено.
Викопедия и Гугл этого не описывает и посылка туда не даст правильных ответов.

Вам как пример:
WiFi.begin() без параметров должно включать WiFi, после чего должны быть доступны функции сканирования сети без организации какой-либо точки доступа или станции.
В ESP8266 вы лишены этой функциональности.
 
Последнее редактирование:

enjoynering

Well-known member
вы скорее всего правы. весь этот зоопарк из вызова одной функциии через другую с дублированием самих себя очень похоже на костыль-совместимости с предудущими версиями arduino esp8266 core. Надо отдать должное, они вняли вашей критике и теперь перед сохранием пароля и ssid проверяют его с сохраненным на флешке и не перезаписывают зря. ну и для самых продвинутых внедрили запрет на запись путем вызова функции WiFi.persistent(false). вот пример WiFi.begin(ssid, password) для запуска клиента:

Код:
wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) {

    if(!WiFi.enableSTA(true)) {
        // enable STA failed
        return WL_CONNECT_FAILED;
    }

    if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
        // fail SSID too long or missing!
        return WL_CONNECT_FAILED;
    }

    if(passphrase && strlen(passphrase) > 64) {
        // fail passphrase too long!
        return WL_CONNECT_FAILED;
    }

    struct station_config conf;
    strcpy(reinterpret_cast<char*>(conf.ssid), ssid);

    if(passphrase) {
        if (strlen(passphrase) == 64) // it's not a passphrase, is the PSK, which is copied into conf.password without null term
            memcpy(reinterpret_cast<char*>(conf.password), passphrase, 64);
        else
            strcpy(reinterpret_cast<char*>(conf.password), passphrase);
    } else {
        *conf.password = 0;
    }

    if(bssid) {
        conf.bssid_set = 1;
        memcpy((void *) &conf.bssid[0], (void *) bssid, 6);
    } else {
        conf.bssid_set = 0;
    }

    struct station_config current_conf;
    wifi_station_get_config(&current_conf);
    if(sta_config_equal(current_conf, conf)) {
        DEBUGV("sta config unchanged");
    }
    else {
        ETS_UART_INTR_DISABLE();

        if(WiFi._persistent) {
            // workaround for #1997: make sure the value of ap_number is updated and written to flash
            // to be removed after SDK update
            wifi_station_ap_number_set(2);
            wifi_station_ap_number_set(1);

            wifi_station_set_config(&conf);
        } else {
            wifi_station_set_config_current(&conf);
        }

        ETS_UART_INTR_ENABLE();
    }

    ETS_UART_INTR_DISABLE();
    if(connect) {
        wifi_station_connect();
    }
    ETS_UART_INTR_ENABLE();

    if(channel > 0 && channel <= 13) {
        wifi_set_channel(channel);
    }

    if(!_useStaticIp) {
        wifi_station_dhcpc_start();
    }

    return status();
}
полный код находится по адресу - Arduino/ESP8266WiFiSTA.cpp at master · esp8266/Arduino · GitHub
чтоб до конца понять, надо еще заглядывать сюда - Arduino/ESP8266WiFiGeneric.cpp at master · esp8266/Arduino · GitHub
 
Последнее редактирование:

pvvx

Активный участник сообщества
вы скорее всего правы. весь этот зоопарк из вызова одной функциии через другую с дублированием самих себя очень похоже на костыль-совместимости с предудущими версиями arduino esp8266 core.
Причину зоопарка я не разглашал :)
Она повязана на начальной реализации deep_sleep - чтобы ускорить подключение после просыпания.
В SDK ещё много чего зависит от начальных принятых алгоритмов, написанных кое-как и без обдумывания. Теперь исправляют, что для совместимости требует дополнительных ресурсов (страдает размер кода и скорость исполнения).
Отдельные дублирующие функции установок WiFi без записи в SDK появились вроде с 1.2 версии... До "Arduino" это дошло только в последних редакциях.
С версиями SDK, включая 2.0, с которыми пытался что-то слепить так не нашлось вариантов для установки всех необходимых параметров к WiFi не использующих функцию сохранения параметров во Flash, на что уходит много время - стирание сектора + запись его с невозможностью в это время работы кода из Flash.
SDK для ESP8266 составлено безграмотно, что накладывает многие ограничения. В ESP-32, уже год исправляют эту ситуацию в ESP-IDF.
Основа всего этого – закрытый код SDK без отделения драйвера WiFi. Только на него пока не принято давать исходники в общий доступ, и то это действие вызвано “другие не дают, и мы не будем” и никакими более вариантами.
 
Последнее редактирование:

enjoynering

Well-known member
теперь понятно, спасибо. для мня очень странно почему при вызове функции WiFi.mode() они не делают проверку конфига на флешке? тут спасение только в вызове WiFi.persistent(false) иначе после каждого изменения будет перезаписываться конфиг и лет через 5 флешке настанет каюк. спасибо хоть проверку на совпадение с текущим знвчением сделали.

Код:
bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) {
    if(wifi_get_opmode() == (uint8) m) {
        return true;
    }

    bool ret = false;

    ETS_UART_INTR_DISABLE();
    if(_persistent) {
        ret = wifi_set_opmode(m);
    } else {
        ret = wifi_set_opmode_current(m);
    }
    ETS_UART_INTR_ENABLE();

    return ret;
}
 

pvvx

Активный участник сообщества
теперь понятно, спасибо. для мня очень странно почему при вызове функции WiFi.mode() они не делают проверку конфига на флешке? тут спасение только в вызове WiFi.persistent(false) иначе после каждого изменения будет перезаписываться конфиг и лет через 5 флешке настанет каюк. спасибо хоть проверку на совпадение с текущим знвчением сделали.

Код:
bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) {
    if(wifi_get_opmode() == (uint8) m) {
        return true;
    }

    bool ret = false;

    ETS_UART_INTR_DISABLE();
    if(_persistent) {
        ret = wifi_set_opmode(m);
    } else {
        ret = wifi_set_opmode_current(m);
    }
    ETS_UART_INTR_ENABLE();

    return ret;
}
wifi_get_opmode() - это функция, вызывающая другую функцию (назовем _get_opmode) с параметром _get_opmode(1)
wifi_get_opmode_default() - это функция, вызывающая другую функцию с параметром _get_opmode(0)
_get_opmode() в зависимости от параметра:
0) выделяет в heap 1172 байта (размер блока параметров WiFi для SDK2.0), и вызывает считывание в полученный блок памяти (system_param_load()) области сохранения во Flash, считывает из полученного блока один байт, освобождает блок в heap. :)
1) обращается к структуре wifi_store, считывает и выдает из неё байт номера режима.

Драйвер WiFi, обычно, может может работать в разных режимах. Т.к. у нас два основных типа - AP и STA, то драйвер WiFi делиться на два типа работы. Т.е. фактически при AP+STA загружены и включены два драйвера. Проверить какой драйвер и в каком режиме сейчас активен по данным из flash не выйдет, если установка его произошла без сохранения параметров во flash.

wifi_set_opmode(x) - это тоже функция, с вызывающая другую _set_opmode(x, 1)
wifi_set_opmode_current(x) - это тоже функция, с вызывающая другую _set_opmode(x, 0)

_set_opmode(x, flag) примерно то-же самое - в зависимости от flag вызывает считывание из flash (system_param_load(...), system_param_save_with_protect(...) ) структуры wifi_store[1172] с запросом размещения в heap pvPortMalloc(1172, id функции для отладки)....
Миллион ненужных действий и диких китайских алгоритмов... Описывать даже не охота... :)

Что примечательного в этом всём:
system_param_save_with_protect() вызывает wifi_param_save_protect_with_check() в которой
pvPortZalloc(), (#) spi_flash_erase_sector(), spi_flash_write(), spi_flash_read(), ets_memcmp() и если не равно - возврат к началу стирания и записи (#) без какого либо счета неудач, в конце - vPortFree().

Если у вас упало или плавает питание, то процесс стирания и записи происходит до победного конца - дыры в Flash.
Первые варианты SDK у Espressif писал какой-то школьник, не проходивший программ обучения программированию (наверно денег у Espressif не было нанять проф.программиста). К версии SDK2.0 осталось 40% его алгоритмов. Остальные, более ужасные, были исправлены. Но множество "фич" так и осталось... К примеру SDK собирается только с выключенными всеми wraning у компилятора. Иначе образуется большой список ошибок по поводу не проинициализированных переменных, неверных вызовов пр параметрам функций и т.д. По этому от версии к версии разные баги в SDK и потом выдают новые патчи (назвав это оптимаза :) ). Так-же был прикол - на вопрос что была за ошибка и почему патч, программист ответил - пересобрал заново либы и оно исправилось... :)
На таком материале и сделана Arduino на ESP8266 другими, в основном энтузиастами... Ну и никогда не получите исходников SDK - это будет шоу.
По этому когда указываете какой код - надо приводить номер версии SDK и патчей.
 
Последнее редактирование:

nikolz

Well-known member
теперь понятно, спасибо. для мня очень странно почему при вызове функции WiFi.mode() они не делают проверку конфига на флешке? тут спасение только в вызове WiFi.persistent(false) иначе после каждого изменения будет перезаписываться конфиг и лет через 5 флешке настанет каюк. спасибо хоть проверку на совпадение с текущим знвчением сделали.
никто не мешает добавить проверку на Си например так :
Код:
//-----------------------это фрагмент из рабочей программы ---------------------
if (wifi_get_opmode()!=STATION_MODE) wifi_set_opmode(STATION_MODE);
if(wifi_get_phy_mode() != PHY_MODE_11N)    wifi_set_phy_mode(PHY_MODE_11N);
if (wifi_station_get_auto_connect()==0)    wifi_station_set_auto_connect(1);
    uint8 e1=strcmp(stationConf.ssid,_SSID);
    uint8 e2=strcmp(stationConf.password,_PASSWORD);
    if (e1!=0 ||e2!=0){
          os_bzero(&stationConf,sizeof(struct station_config));
        ets_strcpy(stationConf.ssid,_SSID);
        ets_strcpy(stationConf.password,_PASSWORD);
          wifi_station_set_config(&stationConf);
        wifi_station_set_reconnect_policy(true);
}
 

pvvx

Активный участник сообщества
никто не мешает добавить проверку на Си например так :
Код:
//-----------------------это фрагмент из рабочей программы ---------------------
if (wifi_get_opmode()!=STATION_MODE) wifi_set_opmode(STATION_MODE);
if(wifi_get_phy_mode() != PHY_MODE_11N)    wifi_set_phy_mode(PHY_MODE_11N);
if (wifi_station_get_auto_connect()==0)    wifi_station_set_auto_connect(1);
    uint8 e1=strcmp(stationConf.ssid,_SSID);
    uint8 e2=strcmp(stationConf.password,_PASSWORD);
    if (e1!=0 ||e2!=0){
          os_bzero(&stationConf,sizeof(struct station_config));
        ets_strcpy(stationConf.ssid,_SSID);
        ets_strcpy(stationConf.password,_PASSWORD);
          wifi_station_set_config(&stationConf);
        wifi_station_set_reconnect_policy(true);
}
Никто не мешает поставить 16МБайт Flash, но XIP ограничен 1МБ и туда уже не лезет "оптимаза" от китайцев.
Так-же вам только 'никто' мешает поставить psRAM и копировать в неё Flash при старте или указать компилятору заменить вызов обращения к функциям записи-чтения flash на свои и вставить там проверку на запись одного и того-же, но при каждом соединении в этих 1172 байтах что-то изменяется...
Но все эти костыли не исправят ровным счетом ничего.
 

Aleksandr_Sam

New member
вы скорее всего правы. весь этот зоопарк из вызова одной функциии через другую с дублированием самих себя очень похоже на костыль-совместимости с предудущими версиями arduino esp8266 core. Надо отдать должное, они вняли вашей критике и теперь перед сохранием пароля и ssid проверяют его с сохраненным на флешке и не перезаписывают зря. ну и для самых продвинутых внедрили запрет на запись путем вызова функции WiFi.persistent(false). вот пример WiFi.begin(ssid, password) для запуска клиента:

Код:
wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) {

    if(!WiFi.enableSTA(true)) {
        // enable STA failed
        return WL_CONNECT_FAILED;
    }

    if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
        // fail SSID too long or missing!
        return WL_CONNECT_FAILED;
    }

    if(passphrase && strlen(passphrase) > 64) {
        // fail passphrase too long!
        return WL_CONNECT_FAILED;
    }

    struct station_config conf;
    strcpy(reinterpret_cast<char*>(conf.ssid), ssid);

    if(passphrase) {
        if (strlen(passphrase) == 64) // it's not a passphrase, is the PSK, which is copied into conf.password without null term
            memcpy(reinterpret_cast<char*>(conf.password), passphrase, 64);
        else
            strcpy(reinterpret_cast<char*>(conf.password), passphrase);
    } else {
        *conf.password = 0;
    }

    if(bssid) {
        conf.bssid_set = 1;
        memcpy((void *) &conf.bssid[0], (void *) bssid, 6);
    } else {
        conf.bssid_set = 0;
    }

    struct station_config current_conf;
    wifi_station_get_config(&current_conf);
    if(sta_config_equal(current_conf, conf)) {
        DEBUGV("sta config unchanged");
    }
    else {
        ETS_UART_INTR_DISABLE();

        if(WiFi._persistent) {
            // workaround for #1997: make sure the value of ap_number is updated and written to flash
            // to be removed after SDK update
            wifi_station_ap_number_set(2);
            wifi_station_ap_number_set(1);

            wifi_station_set_config(&conf);
        } else {
            wifi_station_set_config_current(&conf);
        }

        ETS_UART_INTR_ENABLE();
    }

    ETS_UART_INTR_DISABLE();
    if(connect) {
        wifi_station_connect();
    }
    ETS_UART_INTR_ENABLE();

    if(channel > 0 && channel <= 13) {
        wifi_set_channel(channel);
    }

    if(!_useStaticIp) {
        wifi_station_dhcpc_start();
    }

    return status();
}
полный код находится по адресу - Arduino/ESP8266WiFiSTA.cpp at master · esp8266/Arduino · GitHub
чтоб до конца понять, надо еще заглядывать сюда - Arduino/ESP8266WiFiGeneric.cpp at master · esp8266/Arduino · GitHub
Помогите разобраться.
Где можно найти тело метода "wifi_station_set_config" и "wifi_station_set_config_current"?
В репозитории я не нашел.

ESP8266WiFiSTA.cpp (esp8266/Arduino)
Код:
if(WiFi._persistent) {
    wifi_station_set_config(&conf);
} else {
    wifi_station_set_config_current(&conf);
}
 

Aleksandr_Sam

New member
Aleksandr_Sam, Aleksandr_Sam,
Помогите разобраться.
Где можно найти тело метода "wifi_station_set_config" и "wifi_station_set_config_current"?
В репозитории я не нашел.

ESP8266WiFiSTA.cpp (esp8266/Arduino)
Код:
if(WiFi._persistent) {
    wifi_station_set_config(&conf);
} else {
    wifi_station_set_config_current(&conf);
}
В этом документе?
https://www.espressif.com/sites/def...on/2c-esp8266_non_os_sdk_api_reference_en.pdf
 
Сверху Снизу