mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-02 12:07:02 +00:00
refactor network code
This commit is contained in:
2
Makefile
2
Makefile
@@ -113,7 +113,7 @@ CXX := /usr/bin/g++
|
|||||||
CPPFLAGS += $(DEFINES) $(DEFAULTS) $(INCLUDE)
|
CPPFLAGS += $(DEFINES) $(DEFAULTS) $(INCLUDE)
|
||||||
CPPFLAGS += -ggdb -g3 -MMD
|
CPPFLAGS += -ggdb -g3 -MMD
|
||||||
CPPFLAGS += -flto=auto
|
CPPFLAGS += -flto=auto
|
||||||
CPPFLAGS += -Wall -Wextra -Werror -Wswitch-enum
|
CPPFLAGS += -Wall -Wextra -Werror -Wno-switch-enum
|
||||||
CPPFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-vla-cxx-extension
|
CPPFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-vla-cxx-extension
|
||||||
CPPFLAGS += -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics
|
CPPFLAGS += -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics
|
||||||
CPPFLAGS += -Os -DNDEBUG
|
CPPFLAGS += -Os -DNDEBUG
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import { numberValue, updateValueDirty, useRest } from 'utils';
|
|||||||
import { ValidationError, createAPSettingsValidator, validate } from 'validators';
|
import { ValidationError, createAPSettingsValidator, validate } from 'validators';
|
||||||
|
|
||||||
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
||||||
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
|
|
||||||
provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
||||||
|
|
||||||
// Efficient range function without recursion
|
// Efficient range function without recursion
|
||||||
@@ -108,9 +107,6 @@ const APSettings = () => {
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
>
|
>
|
||||||
<MenuItem value={APProvisionMode.AP_MODE_ALWAYS}>
|
|
||||||
{LL.AP_PROVIDE_TEXT_1()}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem value={APProvisionMode.AP_MODE_DISCONNECTED}>
|
<MenuItem value={APProvisionMode.AP_MODE_DISCONNECTED}>
|
||||||
{LL.AP_PROVIDE_TEXT_2()}
|
{LL.AP_PROVIDE_TEXT_2()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const cz: Translation = {
|
|||||||
TIME_ZONE: 'Časová zóna',
|
TIME_ZONE: 'Časová zóna',
|
||||||
ACCESS_POINT: 'Přístupový bod',
|
ACCESS_POINT: 'Přístupový bod',
|
||||||
AP_PROVIDE: 'Povolit přístupový bod',
|
AP_PROVIDE: 'Povolit přístupový bod',
|
||||||
AP_PROVIDE_TEXT_1: 'Vždy',
|
AP_PROVIDE_TEXT_2: 'Když je síťové připojení stratené',
|
||||||
AP_PROVIDE_TEXT_2: 'Když je WiFi odpojena',
|
|
||||||
AP_PROVIDE_TEXT_3: 'Nikdy',
|
AP_PROVIDE_TEXT_3: 'Nikdy',
|
||||||
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
||||||
AP_HIDE_SSID: 'Skrýt SSID',
|
AP_HIDE_SSID: 'Skrýt SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const de: Translation = {
|
|||||||
TIME_ZONE: 'Zeitzone',
|
TIME_ZONE: 'Zeitzone',
|
||||||
ACCESS_POINT: 'Zugangspunkt',
|
ACCESS_POINT: 'Zugangspunkt',
|
||||||
AP_PROVIDE: 'Aktiviere Zugangspunkt',
|
AP_PROVIDE: 'Aktiviere Zugangspunkt',
|
||||||
AP_PROVIDE_TEXT_1: 'Immer',
|
AP_PROVIDE_TEXT_2: 'Wenn Netzwerkverbindung verloren geht',
|
||||||
AP_PROVIDE_TEXT_2: 'Wenn WiFi nicht verbunden',
|
|
||||||
AP_PROVIDE_TEXT_3: 'Niemals',
|
AP_PROVIDE_TEXT_3: 'Niemals',
|
||||||
AP_PREFERRED_CHANNEL: 'Bevorzugter Kanal',
|
AP_PREFERRED_CHANNEL: 'Bevorzugter Kanal',
|
||||||
AP_HIDE_SSID: 'Verstecke SSID',
|
AP_HIDE_SSID: 'Verstecke SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const en: Translation = {
|
|||||||
TIME_ZONE: 'Time Zone',
|
TIME_ZONE: 'Time Zone',
|
||||||
ACCESS_POINT: 'Access Point',
|
ACCESS_POINT: 'Access Point',
|
||||||
AP_PROVIDE: 'Enable Access Point',
|
AP_PROVIDE: 'Enable Access Point',
|
||||||
AP_PROVIDE_TEXT_1: 'Always',
|
AP_PROVIDE_TEXT_2: 'When network connection is lost',
|
||||||
AP_PROVIDE_TEXT_2: 'When WiFi is disconnected',
|
|
||||||
AP_PROVIDE_TEXT_3: 'Never',
|
AP_PROVIDE_TEXT_3: 'Never',
|
||||||
AP_PREFERRED_CHANNEL: 'Preferred Channel',
|
AP_PREFERRED_CHANNEL: 'Preferred Channel',
|
||||||
AP_HIDE_SSID: 'Hide SSID',
|
AP_HIDE_SSID: 'Hide SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const fr: Translation = {
|
|||||||
TIME_ZONE: 'Fuseau horaire',
|
TIME_ZONE: 'Fuseau horaire',
|
||||||
ACCESS_POINT: "Point d'accès",
|
ACCESS_POINT: "Point d'accès",
|
||||||
AP_PROVIDE: "Activer le Point d'Accès",
|
AP_PROVIDE: "Activer le Point d'Accès",
|
||||||
AP_PROVIDE_TEXT_1: 'toujours',
|
AP_PROVIDE_TEXT_2: 'quand la connexion réseau est perdue',
|
||||||
AP_PROVIDE_TEXT_2: 'quand le WiFi est déconnecté',
|
|
||||||
AP_PROVIDE_TEXT_3: 'jamais',
|
AP_PROVIDE_TEXT_3: 'jamais',
|
||||||
AP_PREFERRED_CHANNEL: 'Canal préféré',
|
AP_PREFERRED_CHANNEL: 'Canal préféré',
|
||||||
AP_HIDE_SSID: 'Cacher le SSID',
|
AP_HIDE_SSID: 'Cacher le SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const it: Translation = {
|
|||||||
TIME_ZONE: 'Fuso orario',
|
TIME_ZONE: 'Fuso orario',
|
||||||
ACCESS_POINT: 'Access Point',
|
ACCESS_POINT: 'Access Point',
|
||||||
AP_PROVIDE: 'Abilita Access Point',
|
AP_PROVIDE: 'Abilita Access Point',
|
||||||
AP_PROVIDE_TEXT_1: 'sempre',
|
AP_PROVIDE_TEXT_2: 'quando la connessione di rete è persa',
|
||||||
AP_PROVIDE_TEXT_2: 'quando WiFi é disconnessa',
|
|
||||||
AP_PROVIDE_TEXT_3: 'mai',
|
AP_PROVIDE_TEXT_3: 'mai',
|
||||||
AP_PREFERRED_CHANNEL: 'Canale preferito',
|
AP_PREFERRED_CHANNEL: 'Canale preferito',
|
||||||
AP_HIDE_SSID: 'Nascondi SSID',
|
AP_HIDE_SSID: 'Nascondi SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const nl: Translation = {
|
|||||||
TIME_ZONE: 'Tijdzone',
|
TIME_ZONE: 'Tijdzone',
|
||||||
ACCESS_POINT: 'Access Point',
|
ACCESS_POINT: 'Access Point',
|
||||||
AP_PROVIDE: 'Activeer Access Point',
|
AP_PROVIDE: 'Activeer Access Point',
|
||||||
AP_PROVIDE_TEXT_1: 'altijd',
|
AP_PROVIDE_TEXT_2: 'als netwerk verbinding verloren gaat',
|
||||||
AP_PROVIDE_TEXT_2: 'als WiFi niet is verbonden',
|
|
||||||
AP_PROVIDE_TEXT_3: 'nooit',
|
AP_PROVIDE_TEXT_3: 'nooit',
|
||||||
AP_PREFERRED_CHANNEL: 'Voorkeurskanaal',
|
AP_PREFERRED_CHANNEL: 'Voorkeurskanaal',
|
||||||
AP_HIDE_SSID: 'SSID verbergen',
|
AP_HIDE_SSID: 'SSID verbergen',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const no: Translation = {
|
|||||||
TIME_ZONE: 'Tidssone',
|
TIME_ZONE: 'Tidssone',
|
||||||
ACCESS_POINT: 'Aksesspunkt',
|
ACCESS_POINT: 'Aksesspunkt',
|
||||||
AP_PROVIDE: 'Aktiver Aksesspunkt',
|
AP_PROVIDE: 'Aktiver Aksesspunkt',
|
||||||
AP_PROVIDE_TEXT_1: 'alltid',
|
AP_PROVIDE_TEXT_2: 'når nettverksforbindelsen er utilgjengelig',
|
||||||
AP_PROVIDE_TEXT_2: 'når WiFi er utilgjengelig',
|
|
||||||
AP_PROVIDE_TEXT_3: 'aldri',
|
AP_PROVIDE_TEXT_3: 'aldri',
|
||||||
AP_PREFERRED_CHANNEL: 'Foretrukket kanal',
|
AP_PREFERRED_CHANNEL: 'Foretrukket kanal',
|
||||||
AP_HIDE_SSID: 'Skjul SSID',
|
AP_HIDE_SSID: 'Skjul SSID',
|
||||||
|
|||||||
@@ -247,7 +247,6 @@ const pl: BaseTranslation = {
|
|||||||
TIME_ZONE: 'Strefa czasowa',
|
TIME_ZONE: 'Strefa czasowa',
|
||||||
ACCESS_POINT: '{{Punkt|punktu|}} {{dostępowy|dostępowego|}}',
|
ACCESS_POINT: '{{Punkt|punktu|}} {{dostępowy|dostępowego|}}',
|
||||||
AP_PROVIDE: 'Punkt dostępowy',
|
AP_PROVIDE: 'Punkt dostępowy',
|
||||||
AP_PROVIDE_TEXT_1: 'zawsze aktywny',
|
|
||||||
AP_PROVIDE_TEXT_2: 'aktywny jeśli brak połączenia z siecią',
|
AP_PROVIDE_TEXT_2: 'aktywny jeśli brak połączenia z siecią',
|
||||||
AP_PROVIDE_TEXT_3: 'nieaktywny',
|
AP_PROVIDE_TEXT_3: 'nieaktywny',
|
||||||
AP_PREFERRED_CHANNEL: 'Preferowany kanał',
|
AP_PREFERRED_CHANNEL: 'Preferowany kanał',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const sk: Translation = {
|
|||||||
TIME_ZONE: 'Časová zóna',
|
TIME_ZONE: 'Časová zóna',
|
||||||
ACCESS_POINT: 'Prístupový bod',
|
ACCESS_POINT: 'Prístupový bod',
|
||||||
AP_PROVIDE: 'Povoliť prístupový bod',
|
AP_PROVIDE: 'Povoliť prístupový bod',
|
||||||
AP_PROVIDE_TEXT_1: 'vždy',
|
AP_PROVIDE_TEXT_2: 'keď je sieťové pripojenie stratené',
|
||||||
AP_PROVIDE_TEXT_2: 'keď je WiFi odpojená',
|
|
||||||
AP_PROVIDE_TEXT_3: 'nikdy',
|
AP_PROVIDE_TEXT_3: 'nikdy',
|
||||||
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
AP_PREFERRED_CHANNEL: 'Preferovaný kanál',
|
||||||
AP_HIDE_SSID: 'Skryť SSID',
|
AP_HIDE_SSID: 'Skryť SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const sv: Translation = {
|
|||||||
TIME_ZONE: 'Tidszon',
|
TIME_ZONE: 'Tidszon',
|
||||||
ACCESS_POINT: 'Accesspunkt',
|
ACCESS_POINT: 'Accesspunkt',
|
||||||
AP_PROVIDE: 'Aktivera accesspunkt',
|
AP_PROVIDE: 'Aktivera accesspunkt',
|
||||||
AP_PROVIDE_TEXT_1: 'alltid',
|
AP_PROVIDE_TEXT_2: 'när nätverksanslutningen är bortkopplad',
|
||||||
AP_PROVIDE_TEXT_2: 'när WiFi är nedkopplat',
|
|
||||||
AP_PROVIDE_TEXT_3: 'aldrig',
|
AP_PROVIDE_TEXT_3: 'aldrig',
|
||||||
AP_PREFERRED_CHANNEL: 'Kanal',
|
AP_PREFERRED_CHANNEL: 'Kanal',
|
||||||
AP_HIDE_SSID: 'Göm SSID',
|
AP_HIDE_SSID: 'Göm SSID',
|
||||||
|
|||||||
@@ -247,8 +247,7 @@ const tr: Translation = {
|
|||||||
TIME_ZONE: 'Saat dilimi',
|
TIME_ZONE: 'Saat dilimi',
|
||||||
ACCESS_POINT: 'Erişim Noktası',
|
ACCESS_POINT: 'Erişim Noktası',
|
||||||
AP_PROVIDE: 'Erişim noktasını çalıştır',
|
AP_PROVIDE: 'Erişim noktasını çalıştır',
|
||||||
AP_PROVIDE_TEXT_1: 'her zaman',
|
AP_PROVIDE_TEXT_2: 'Ağ bağlantısı kesildiğinde',
|
||||||
AP_PROVIDE_TEXT_2: 'Kablosuz bağlantı kesildiğinde',
|
|
||||||
AP_PROVIDE_TEXT_3: 'asla',
|
AP_PROVIDE_TEXT_3: 'asla',
|
||||||
AP_PREFERRED_CHANNEL: 'Tercih edilen kanal',
|
AP_PREFERRED_CHANNEL: 'Tercih edilen kanal',
|
||||||
AP_HIDE_SSID: 'SSID yi gizle',
|
AP_HIDE_SSID: 'SSID yi gizle',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export enum APProvisionMode {
|
export enum APProvisionMode {
|
||||||
AP_MODE_ALWAYS = 0,
|
|
||||||
AP_MODE_DISCONNECTED = 1,
|
AP_MODE_DISCONNECTED = 1,
|
||||||
AP_NEVER = 2
|
AP_NEVER = 2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_
|
|||||||
: id_(id)
|
: id_(id)
|
||||||
, content_(std::move(content)) {
|
, content_(std::move(content)) {
|
||||||
// Added for EMS-ESP
|
// Added for EMS-ESP
|
||||||
if (time_good_ || emsesp::EMSESP::system_.network_connected()) {
|
if (time_good_ || emsesp::EMSESP::network_.network_connected()) {
|
||||||
#if UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
#if UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
||||||
if (gettimeofday(&time_, nullptr) != 0) {
|
if (gettimeofday(&time_, nullptr) != 0) {
|
||||||
time_.tv_sec = (time_t)-1;
|
time_.tv_sec = (time_t)-1;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "SecuritySettingsService.h"
|
#include "SecuritySettingsService.h"
|
||||||
#include "StatefulService.h"
|
#include "StatefulService.h"
|
||||||
#include "Network.h"
|
#include "Network.h"
|
||||||
|
// #include "IPAddress.h"
|
||||||
|
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
|
||||||
@@ -21,7 +22,6 @@
|
|||||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||||
|
|
||||||
#define AP_MODE_ALWAYS 0
|
|
||||||
class DummySettings {
|
class DummySettings {
|
||||||
public:
|
public:
|
||||||
// SYSTEM
|
// SYSTEM
|
||||||
@@ -49,6 +49,22 @@ class DummySettings {
|
|||||||
uint16_t keepAlive = 60;
|
uint16_t keepAlive = 60;
|
||||||
bool cleanSession = false;
|
bool cleanSession = false;
|
||||||
uint8_t entity_format = 1;
|
uint8_t entity_format = 1;
|
||||||
|
String CORSOrigin = "*";
|
||||||
|
uint8_t tx_power = 0;
|
||||||
|
String bssid = "";
|
||||||
|
String localIP = "";
|
||||||
|
String gatewayIP = "";
|
||||||
|
String subnetMask = "";
|
||||||
|
bool staticIPConfig = false;
|
||||||
|
String dnsIP1 = "";
|
||||||
|
String dnsIP2 = "";
|
||||||
|
bool enableMDNS = true;
|
||||||
|
bool enableCORS = false;
|
||||||
|
uint8_t channel = 1;
|
||||||
|
bool ssid_hidden = false;
|
||||||
|
uint8_t max_clients = 4;
|
||||||
|
bool ssidHidden = false;
|
||||||
|
uint8_t maxClients = 4;
|
||||||
|
|
||||||
uint16_t publish_time_boiler = 10;
|
uint16_t publish_time_boiler = 10;
|
||||||
uint16_t publish_time_thermostat = 10;
|
uint16_t publish_time_thermostat = 10;
|
||||||
@@ -59,21 +75,10 @@ class DummySettings {
|
|||||||
uint16_t publish_time_heartbeat = 60;
|
uint16_t publish_time_heartbeat = 60;
|
||||||
uint32_t publish_time_water = 0;
|
uint32_t publish_time_water = 0;
|
||||||
|
|
||||||
String hostname = "ems-esp";
|
String hostname = "ems-esp";
|
||||||
String jwtSecret = "ems-esp";
|
String jwtSecret = "ems-esp";
|
||||||
String ssid = "ems-esp";
|
String ssid = "ems-esp";
|
||||||
String password = "ems-esp";
|
String password = "ems-esp";
|
||||||
String bssid = "";
|
|
||||||
String localIP = "";
|
|
||||||
String gatewayIP = "";
|
|
||||||
String subnetMask = "";
|
|
||||||
bool staticIPConfig = false;
|
|
||||||
String dnsIP1 = "";
|
|
||||||
String dnsIP2 = "";
|
|
||||||
bool enableMDNS = true;
|
|
||||||
bool enableCORS = false;
|
|
||||||
String CORSOrigin = "*";
|
|
||||||
uint8_t tx_power = 0;
|
|
||||||
|
|
||||||
// AP
|
// AP
|
||||||
uint8_t provisionMode = 0;
|
uint8_t provisionMode = 0;
|
||||||
|
|||||||
@@ -4,110 +4,17 @@
|
|||||||
|
|
||||||
APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||||
: _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)
|
: _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)
|
||||||
, _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE)
|
, _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE) {
|
||||||
, _dnsServer(nullptr)
|
|
||||||
, _lastManaged(0)
|
|
||||||
, _reconfigureAp(false)
|
|
||||||
, _connected(0) {
|
|
||||||
addUpdateHandler([this] { reconfigureAP(); }, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APSettingsService::begin() {
|
void APSettingsService::begin() {
|
||||||
_fsPersistence.readFromFS();
|
_fsPersistence.readFromFS();
|
||||||
// disabled for delayed start, first try station mode
|
|
||||||
// reconfigureAP();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::reconfigureAP() {
|
|
||||||
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
|
|
||||||
_reconfigureAp = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::loop() {
|
|
||||||
const uint8_t was_connected = _connected;
|
|
||||||
if (WiFi.isConnected()) {
|
|
||||||
_connected |= 1U;
|
|
||||||
} else {
|
|
||||||
_connected &= ~1U;
|
|
||||||
}
|
|
||||||
if (ETH.connected()) {
|
|
||||||
_connected |= 2U;
|
|
||||||
} else {
|
|
||||||
_connected &= ~2U;
|
|
||||||
}
|
|
||||||
// wait 10 sec before starting AP
|
|
||||||
if (was_connected && !_connected) {
|
|
||||||
_lastManaged = uuid::get_uptime();
|
|
||||||
}
|
|
||||||
const unsigned long currentMillis = uuid::get_uptime();
|
|
||||||
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
|
|
||||||
_lastManaged = currentMillis;
|
|
||||||
manageAP();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dnsServer) {
|
|
||||||
handleDNS();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::manageAP() {
|
|
||||||
const WiFiMode_t currentWiFiMode = WiFi.getMode();
|
|
||||||
if (_state.provisionMode == AP_MODE_ALWAYS || (_state.provisionMode == AP_MODE_DISCONNECTED && !_connected)) {
|
|
||||||
if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) {
|
|
||||||
startAP();
|
|
||||||
}
|
|
||||||
} else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && _connected && (_reconfigureAp || !WiFi.softAPgetStationNum())) {
|
|
||||||
stopAP();
|
|
||||||
}
|
|
||||||
_reconfigureAp = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::startAP() {
|
|
||||||
WiFi.softAPenableIPv6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
|
||||||
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
|
||||||
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
|
||||||
#endif
|
|
||||||
if (!_dnsServer) {
|
|
||||||
const IPAddress apIp = WiFi.softAPIP();
|
|
||||||
char ipStr[16];
|
|
||||||
snprintf(ipStr, sizeof(ipStr), "%u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
|
||||||
emsesp::EMSESP::logger().info("Starting Access Point with captive portal on %s", ipStr);
|
|
||||||
_dnsServer = new DNSServer;
|
|
||||||
_dnsServer->start(DNS_PORT, "*", apIp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::stopAP() {
|
|
||||||
if (_dnsServer) {
|
|
||||||
emsesp::EMSESP::logger().info("Stopping Access Point");
|
|
||||||
_dnsServer->stop();
|
|
||||||
delete _dnsServer;
|
|
||||||
_dnsServer = nullptr;
|
|
||||||
}
|
|
||||||
WiFi.softAPdisconnect(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APSettingsService::handleDNS() {
|
|
||||||
if (_dnsServer) {
|
|
||||||
_dnsServer->processNextRequest();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
APNetworkStatus APSettingsService::getAPNetworkStatus() {
|
APNetworkStatus APSettingsService::getAPNetworkStatus() {
|
||||||
const WiFiMode_t currentWiFiMode = WiFi.getMode();
|
return emsesp::EMSESP::network_.ap_connected() ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE;
|
||||||
const bool apActive = (currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA);
|
|
||||||
|
|
||||||
if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
|
|
||||||
return APNetworkStatus::LINGERING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void APSettings::read(const APSettings & settings, JsonObject root) {
|
void APSettings::read(const APSettings & settings, JsonObject root) {
|
||||||
root["provision_mode"] = settings.provisionMode;
|
root["provision_mode"] = settings.provisionMode;
|
||||||
root["ssid"] = settings.ssid;
|
root["ssid"] = settings.ssid;
|
||||||
@@ -125,12 +32,11 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
|||||||
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
||||||
|
|
||||||
switch (settings.provisionMode) {
|
switch (settings.provisionMode) {
|
||||||
case AP_MODE_ALWAYS:
|
|
||||||
case AP_MODE_DISCONNECTED:
|
case AP_MODE_DISCONNECTED:
|
||||||
case AP_MODE_NEVER:
|
case AP_MODE_NEVER:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
newSettings.provisionMode = AP_MODE_ALWAYS;
|
newSettings.provisionMode = AP_MODE_DISCONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
|
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
|
||||||
@@ -148,5 +54,10 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
settings = newSettings;
|
settings = newSettings;
|
||||||
|
|
||||||
|
// if the AP mode has changed, force a disconnect and reconnect
|
||||||
|
if (settings.provisionMode != newSettings.provisionMode) {
|
||||||
|
emsesp::EMSESP::network_.reconnect();
|
||||||
|
}
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
#include "FSPersistence.h"
|
#include "FSPersistence.h"
|
||||||
#include "JsonUtils.h"
|
#include "JsonUtils.h"
|
||||||
|
|
||||||
#include <DNSServer.h>
|
|
||||||
#include <IPAddress.h>
|
|
||||||
|
|
||||||
#ifndef FACTORY_AP_PROVISION_MODE
|
#ifndef FACTORY_AP_PROVISION_MODE
|
||||||
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED
|
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED
|
||||||
#endif
|
#endif
|
||||||
@@ -47,14 +44,10 @@
|
|||||||
#define AP_SETTINGS_FILE "/config/apSettings.json"
|
#define AP_SETTINGS_FILE "/config/apSettings.json"
|
||||||
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
|
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
|
||||||
|
|
||||||
#define AP_MODE_ALWAYS 0
|
|
||||||
#define AP_MODE_DISCONNECTED 1
|
#define AP_MODE_DISCONNECTED 1
|
||||||
#define AP_MODE_NEVER 2
|
#define AP_MODE_NEVER 2
|
||||||
|
|
||||||
#define MANAGE_NETWORK_DELAY 10000
|
enum APNetworkStatus { ACTIVE = 0, INACTIVE };
|
||||||
#define DNS_PORT 53
|
|
||||||
|
|
||||||
enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
|
|
||||||
|
|
||||||
class APSettings {
|
class APSettings {
|
||||||
public:
|
public:
|
||||||
@@ -84,26 +77,11 @@ class APSettingsService : public StatefulService<APSettings> {
|
|||||||
APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
void loop();
|
|
||||||
APNetworkStatus getAPNetworkStatus();
|
APNetworkStatus getAPNetworkStatus();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HttpEndpoint<APSettings> _httpEndpoint;
|
HttpEndpoint<APSettings> _httpEndpoint;
|
||||||
FSPersistence<APSettings> _fsPersistence;
|
FSPersistence<APSettings> _fsPersistence;
|
||||||
|
|
||||||
// for the captive portal
|
|
||||||
DNSServer * _dnsServer;
|
|
||||||
|
|
||||||
// for the management delay loop
|
|
||||||
volatile unsigned long _lastManaged;
|
|
||||||
volatile bool _reconfigureAp;
|
|
||||||
volatile uint8_t _connected;
|
|
||||||
|
|
||||||
void reconfigureAP();
|
|
||||||
void manageAP();
|
|
||||||
void startAP();
|
|
||||||
void stopAP();
|
|
||||||
void handleDNS();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "APStatus.h"
|
#include "APStatus.h"
|
||||||
|
|
||||||
|
#include <emsesp.h>
|
||||||
|
|
||||||
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
||||||
: _apSettingsService(apSettingsService) {
|
: _apSettingsService(apSettingsService) {
|
||||||
securityManager->addEndpoint(server, AP_STATUS_SERVICE_PATH, AuthenticationPredicates::IS_AUTHENTICATED, [this](AsyncWebServerRequest * request) {
|
securityManager->addEndpoint(server, AP_STATUS_SERVICE_PATH, AuthenticationPredicates::IS_AUTHENTICATED, [this](AsyncWebServerRequest * request) {
|
||||||
@@ -12,9 +14,9 @@ void APStatus::apStatus(AsyncWebServerRequest * request) {
|
|||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||||
root["ip_address"] = WiFi.softAPIP().toString();
|
root["ip_address"] = emsesp::EMSESP::network_.getLocalIP();
|
||||||
root["mac_address"] = WiFi.softAPmacAddress();
|
root["mac_address"] = emsesp::EMSESP::network_.getMacAddress();
|
||||||
root["station_num"] = WiFi.softAPgetStationNum();
|
root["station_num"] = emsesp::EMSESP::network_.getStationNum();
|
||||||
|
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
#ifndef APStatus_h
|
#ifndef APStatus_h
|
||||||
#define APStatus_h
|
#define APStatus_h
|
||||||
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <IPAddress.h>
|
|
||||||
|
|
||||||
#include "SecurityManager.h"
|
#include "SecurityManager.h"
|
||||||
#include "APSettingsService.h"
|
#include "APSettingsService.h"
|
||||||
|
|||||||
@@ -101,8 +101,6 @@ void ESP32React::begin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ESP32React::loop() {
|
void ESP32React::loop() {
|
||||||
_networkSettingsService.loop();
|
|
||||||
_apSettingsService.loop();
|
|
||||||
_mqttSettingsService.loop();
|
_mqttSettingsService.loop();
|
||||||
_ntpSettingsService.loop();
|
_ntpSettingsService.loop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,19 +54,6 @@ class ESP32React {
|
|||||||
return _mqttSettingsService.getMqttClient();
|
return _mqttSettingsService.getMqttClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// special functions needed outside scope
|
|
||||||
//
|
|
||||||
|
|
||||||
// true if AP is active
|
|
||||||
bool apStatus() {
|
|
||||||
return _apSettingsService.getAPNetworkStatus() == APNetworkStatus::ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t getWifiReconnects() {
|
|
||||||
return _networkSettingsService.getWifiReconnects();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer * _server;
|
AsyncWebServer * _server;
|
||||||
SecuritySettingsService _securitySettingsService;
|
SecuritySettingsService _securitySettingsService;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void MqttSettingsService::startClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::loop() {
|
void MqttSettingsService::loop() {
|
||||||
if (_state.enabled && _mqttClient && _mqttClient->connected() && !emsesp::EMSESP::system_.network_connected()) {
|
if (_state.enabled && _mqttClient && _mqttClient->connected() && !emsesp::EMSESP::network_.network_connected()) {
|
||||||
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
|
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
|
||||||
_mqttClient->disconnect(true);
|
_mqttClient->disconnect(true);
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ bool MqttSettingsService::configureMqtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only connect if WiFi is connected and MQTT is enabled
|
// only connect if WiFi is connected and MQTT is enabled
|
||||||
if (_state.enabled && emsesp::EMSESP::system_.network_connected() && !_state.host.isEmpty()) {
|
if (_state.enabled && emsesp::EMSESP::network_.network_connected() && !_state.host.isEmpty()) {
|
||||||
// create the Last Will Testament topic (LWT) with the base prefixed. It has to be static because the client destroys the reference
|
// create the Last Will Testament topic (LWT) with the base prefixed. It has to be static because the client destroys the reference
|
||||||
static char will_topic[FACTORY_MQTT_MAX_TOPIC_LENGTH];
|
static char will_topic[FACTORY_MQTT_MAX_TOPIC_LENGTH];
|
||||||
if (_state.base.isEmpty()) {
|
if (_state.base.isEmpty()) {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ void NTPSettingsService::begin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NTPSettingsService::loop() {
|
void NTPSettingsService::loop() {
|
||||||
if (_connected != emsesp::EMSESP::system_.network_connected()) {
|
if (_connected != emsesp::EMSESP::network_.network_connected()) {
|
||||||
_connected = emsesp::EMSESP::system_.network_connected();
|
_connected = emsesp::EMSESP::network_.network_connected();
|
||||||
configureNTP();
|
configureNTP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,484 +4,13 @@
|
|||||||
|
|
||||||
NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||||
: _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager)
|
: _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager)
|
||||||
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE)
|
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE) {
|
||||||
, _lastConnectionAttempt(0)
|
|
||||||
, _stopping(false) {
|
|
||||||
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
|
|
||||||
// Eth is also bound to the WifiGeneric event handler
|
|
||||||
// Network.onEvent([this](arduino_event_id_t event, arduino_event_info_t info) { WiFiEvent(event, info); });
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
|
|
||||||
uint tmp[6];
|
|
||||||
if (bssid.isEmpty() || sscanf(bssid.c_str(), "%X:%X:%X:%X:%X:%X", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != 6) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (uint8_t i = 0; i < 6; i++) {
|
|
||||||
mac[i] = static_cast<uint8_t>(tmp[i]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsService::begin() {
|
void NetworkSettingsService::begin() {
|
||||||
// We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default.
|
|
||||||
// If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future.
|
|
||||||
if (WiFi.getMode() != WIFI_OFF) {
|
|
||||||
WiFi.mode(WIFI_OFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable WiFi config persistance and auto reconnect
|
|
||||||
WiFi.persistent(false);
|
|
||||||
WiFi.setAutoReconnect(false);
|
|
||||||
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.mode(WIFI_OFF);
|
|
||||||
|
|
||||||
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
|
||||||
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
|
||||||
|
|
||||||
_fsPersistence.readFromFS();
|
_fsPersistence.readFromFS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsService::reconfigureWiFiConnection() {
|
|
||||||
// do not disconnect for switching to eth, restart is needed
|
|
||||||
if (WiFi.isConnected() && _state.ssid.length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disconnect and de-configure wifi
|
|
||||||
if (WiFi.disconnect(true)) {
|
|
||||||
_stopping = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkSettingsService::loop() {
|
|
||||||
unsigned long currentMillis = millis();
|
|
||||||
if (!_lastConnectionAttempt || static_cast<uint32_t>(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) {
|
|
||||||
_lastConnectionAttempt = currentMillis;
|
|
||||||
manageSTA();
|
|
||||||
}
|
|
||||||
static uint8_t connect = 0;
|
|
||||||
enum uint8_t {
|
|
||||||
CONNECT_IDLE = 0,
|
|
||||||
CONNECT_WAIT_ETH,
|
|
||||||
CONNECT_WAIT_IP4,
|
|
||||||
CONNECT_WAIT_ETH_IP4,
|
|
||||||
CONNECT_WAIT_IP6,
|
|
||||||
CONNECT_WAIT_ETH_IP6,
|
|
||||||
CONNECT_ETH_ACTIVE,
|
|
||||||
CONNECT_WIFI_ACTIVE
|
|
||||||
};
|
|
||||||
switch (connect) {
|
|
||||||
default:
|
|
||||||
connect = CONNECT_IDLE;
|
|
||||||
break;
|
|
||||||
case CONNECT_IDLE:
|
|
||||||
if (ETH.started() && _state.ssid.length() == 0) {
|
|
||||||
emsesp::EMSESP::logger().info("ETH started");
|
|
||||||
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
|
||||||
ETH.enableIPv6(true);
|
|
||||||
if (_state.staticIPConfig) {
|
|
||||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
|
||||||
}
|
|
||||||
connect = CONNECT_WAIT_ETH;
|
|
||||||
}
|
|
||||||
if (WiFi.isConnected()) {
|
|
||||||
emsesp::EMSESP::logger().info("Wifi connected");
|
|
||||||
if (_state.tx_power == 0) {
|
|
||||||
setWiFiPowerOnRSSI();
|
|
||||||
}
|
|
||||||
mDNS_start();
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(true);
|
|
||||||
connect = CONNECT_WAIT_IP4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WAIT_ETH:
|
|
||||||
if (ETH.connected()) {
|
|
||||||
emsesp::EMSESP::logger().info("ETH connected");
|
|
||||||
emsesp::EMSESP::system_.ethernet_connected(true);
|
|
||||||
mDNS_start();
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(true);
|
|
||||||
connect = CONNECT_WAIT_ETH_IP4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WAIT_ETH_IP4:
|
|
||||||
if (ETH.hasIP()) {
|
|
||||||
emsesp::EMSESP::logger().info("ETH IPv4: %s", ETH.localIP().toString().c_str());
|
|
||||||
connect = CONNECT_WAIT_ETH_IP6;
|
|
||||||
}
|
|
||||||
if (!ETH.connected()) {
|
|
||||||
connect = CONNECT_ETH_ACTIVE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WAIT_ETH_IP6:
|
|
||||||
if (ETH.hasLinkLocalIPv6() && ETH.hasGlobalIPv6()) {
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(true);
|
|
||||||
connect = CONNECT_ETH_ACTIVE;
|
|
||||||
}
|
|
||||||
if (!ETH.connected()) {
|
|
||||||
connect = CONNECT_ETH_ACTIVE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_ETH_ACTIVE:
|
|
||||||
if (!ETH.connected()) {
|
|
||||||
emsesp::EMSESP::logger().info("ETH disconnected");
|
|
||||||
emsesp::EMSESP::system_.ethernet_connected(false);
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(false);
|
|
||||||
connect = CONNECT_IDLE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WAIT_IP4:
|
|
||||||
if (!WiFi.localIP().toString().isEmpty()) {
|
|
||||||
emsesp::EMSESP::logger().info("Wifi IPv4: %s", WiFi.localIP().toString().c_str());
|
|
||||||
connect = CONNECT_WAIT_IP6;
|
|
||||||
}
|
|
||||||
if (!WiFi.isConnected()) {
|
|
||||||
connect = CONNECT_ETH_ACTIVE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WAIT_IP6:
|
|
||||||
if (WiFi.linkLocalIPv6().toString() != "::" && WiFi.globalIPv6().toString() != "::") {
|
|
||||||
emsesp::EMSESP::logger().info("Wifi IPv6: %s, %s", WiFi.linkLocalIPv6().toString().c_str(), WiFi.globalIPv6().toString().c_str());
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(true);
|
|
||||||
connect = CONNECT_WIFI_ACTIVE;
|
|
||||||
}
|
|
||||||
if (!WiFi.isConnected()) {
|
|
||||||
connect = CONNECT_WIFI_ACTIVE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECT_WIFI_ACTIVE:
|
|
||||||
if (!WiFi.isConnected()) {
|
|
||||||
emsesp::EMSESP::logger().info("WiFi disconnected");
|
|
||||||
if (_stopping) {
|
|
||||||
_lastConnectionAttempt = 0;
|
|
||||||
_stopping = false;
|
|
||||||
}
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(false);
|
|
||||||
connect = CONNECT_IDLE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkSettingsService::manageSTA() {
|
|
||||||
// Abort if already connected, or if we have no SSID
|
|
||||||
if (WiFi.isConnected() || _state.ssid.length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect or reconnect as required
|
|
||||||
if ((WiFi.getMode() & WIFI_STA) == 0) {
|
|
||||||
WiFi.setHostname(_state.hostname.c_str()); // updates shared default_hostname buffer
|
|
||||||
WiFi.enableSTA(true); // creates the STA netif
|
|
||||||
WiFi.STA.setHostname(_state.hostname.c_str()); // pushes to esp_netif_set_hostname
|
|
||||||
WiFi.enableIPv6(true);
|
|
||||||
if (_state.staticIPConfig) {
|
|
||||||
WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// www.esp32.com/viewtopic.php?t=12055
|
|
||||||
if (_state.bandwidth20) {
|
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
|
||||||
} else {
|
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
|
||||||
}
|
|
||||||
if (_state.nosleep) {
|
|
||||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to connect to the network
|
|
||||||
uint8_t bssid[6];
|
|
||||||
if (formatBssid(_state.bssid, bssid)) {
|
|
||||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str(), 0, bssid);
|
|
||||||
} else {
|
|
||||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BOARD_C3_MINI_V1
|
|
||||||
// always hardcode Tx power for Wemos CS Mini v1
|
|
||||||
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
|
|
||||||
// https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
|
||||||
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
|
||||||
#else
|
|
||||||
if (_state.tx_power != 0) {
|
|
||||||
// if not set to Auto (0) set the Tx power now
|
|
||||||
if (!WiFi.setTxPower(static_cast<wifi_power_t>(_state.tx_power))) {
|
|
||||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else { // not connected but STA-mode active => disconnect
|
|
||||||
reconfigureWiFiConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the TxPower based on the RSSI (signal strength), picking the lowest value
|
|
||||||
// code is based of RSSI (signal strength) and copied from Tasmota's WiFiSetTXpowerBasedOnRssi() which is copied ESPEasy's ESPEasyWifi.SetWiFiTXpower() function
|
|
||||||
void NetworkSettingsService::setWiFiPowerOnRSSI() {
|
|
||||||
// Range ESP32 : 2dBm - 20dBm
|
|
||||||
// 802.11b - wifi1
|
|
||||||
// 802.11a - wifi2
|
|
||||||
// 802.11g - wifi3
|
|
||||||
// 802.11n - wifi4
|
|
||||||
// 802.11ac - wifi5
|
|
||||||
// 802.11ax - wifi6
|
|
||||||
|
|
||||||
int max_tx_pwr = MAX_TX_PWR_DBM_n; // assume wifi4
|
|
||||||
int threshold = WIFI_SENSITIVITY_n + 120; // Margin in dBm * 10 on top of threshold
|
|
||||||
|
|
||||||
// Assume AP sends with max set by ETSI standard.
|
|
||||||
// 2.4 GHz: 100 mWatt (20 dBm)
|
|
||||||
// US and some other countries allow 1000 mW (30 dBm)
|
|
||||||
int rssi = WiFi.RSSI() * 10;
|
|
||||||
int newrssi = rssi - 200; // We cannot send with over 20 dBm, thus it makes no sense to force higher TX power all the time.
|
|
||||||
|
|
||||||
int min_tx_pwr = 0;
|
|
||||||
if (newrssi < threshold) {
|
|
||||||
min_tx_pwr = threshold - newrssi;
|
|
||||||
}
|
|
||||||
if (min_tx_pwr > max_tx_pwr) {
|
|
||||||
min_tx_pwr = max_tx_pwr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// from WiFIGeneric.h use:
|
|
||||||
// WIFI_POWER_19_5dBm = 78,// 19.5dBm
|
|
||||||
// WIFI_POWER_19dBm = 76,// 19dBm
|
|
||||||
// WIFI_POWER_18_5dBm = 74,// 18.5dBm
|
|
||||||
// WIFI_POWER_17dBm = 68,// 17dBm
|
|
||||||
// WIFI_POWER_15dBm = 60,// 15dBm
|
|
||||||
// WIFI_POWER_13dBm = 52,// 13dBm
|
|
||||||
// WIFI_POWER_11dBm = 44,// 11dBm
|
|
||||||
// WIFI_POWER_8_5dBm = 34,// 8.5dBm
|
|
||||||
// WIFI_POWER_7dBm = 28,// 7dBm
|
|
||||||
// WIFI_POWER_5dBm = 20,// 5dBm
|
|
||||||
// WIFI_POWER_2dBm = 8,// 2dBm
|
|
||||||
// WIFI_POWER_MINUS_1dBm = -4// -1dBm
|
|
||||||
wifi_power_t p = WIFI_POWER_2dBm;
|
|
||||||
if (min_tx_pwr > 185)
|
|
||||||
p = WIFI_POWER_19_5dBm;
|
|
||||||
else if (min_tx_pwr > 170)
|
|
||||||
p = WIFI_POWER_18_5dBm;
|
|
||||||
else if (min_tx_pwr > 150)
|
|
||||||
p = WIFI_POWER_17dBm;
|
|
||||||
else if (min_tx_pwr > 130)
|
|
||||||
p = WIFI_POWER_15dBm;
|
|
||||||
else if (min_tx_pwr > 110)
|
|
||||||
p = WIFI_POWER_13dBm;
|
|
||||||
else if (min_tx_pwr > 85)
|
|
||||||
p = WIFI_POWER_11dBm;
|
|
||||||
else if (min_tx_pwr > 70)
|
|
||||||
p = WIFI_POWER_8_5dBm;
|
|
||||||
else if (min_tx_pwr > 50)
|
|
||||||
p = WIFI_POWER_7dBm;
|
|
||||||
else if (min_tx_pwr > 20)
|
|
||||||
p = WIFI_POWER_5dBm;
|
|
||||||
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
// emsesp::EMSESP::logger().debug("Recommended WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d)", min_tx_pwr / 10, p, rssi, threshold);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!WiFi.setTxPower(p)) {
|
|
||||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the multicast UDP service so EMS-ESP is discoverable via .local
|
|
||||||
void NetworkSettingsService::mDNS_start() const {
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
MDNS.end();
|
|
||||||
|
|
||||||
if (_state.enableMDNS) {
|
|
||||||
if (!MDNS.begin(emsesp::EMSESP::system_.hostname().c_str())) {
|
|
||||||
emsesp::EMSESP::logger().warning("Failed to start mDNS Responder service");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string address_s = emsesp::EMSESP::system_.hostname() + ".local";
|
|
||||||
|
|
||||||
MDNS.addService("http", "tcp", 80); // add our web server and rest API
|
|
||||||
MDNS.addService("telnet", "tcp", 23); // add our telnet console
|
|
||||||
|
|
||||||
// MDNS.addServiceTxt("http", "tcp", "version", EMSESP_APP_VERSION);
|
|
||||||
MDNS.addServiceTxt("http", "tcp", "address", address_s.c_str());
|
|
||||||
|
|
||||||
emsesp::EMSESP::logger().info("Starting mDNS Responder service");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * NetworkSettingsService::disconnectReason(uint8_t code) {
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
switch (code) {
|
|
||||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
|
||||||
return "unspecified";
|
|
||||||
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
|
||||||
return "auth expire";
|
|
||||||
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
|
||||||
return "auth leave";
|
|
||||||
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
|
||||||
return "assoc expired";
|
|
||||||
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
|
||||||
return "assoc too many";
|
|
||||||
case WIFI_REASON_NOT_AUTHED: // = 6,
|
|
||||||
return "not authenticated";
|
|
||||||
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
|
||||||
return "not assoc";
|
|
||||||
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
|
||||||
return "assoc leave";
|
|
||||||
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
|
||||||
return "assoc not authed";
|
|
||||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
|
||||||
return "disassoc powerCAP bad";
|
|
||||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
|
||||||
return "disassoc supchan bad";
|
|
||||||
case WIFI_REASON_IE_INVALID: // = 13,
|
|
||||||
return "IE invalid";
|
|
||||||
case WIFI_REASON_MIC_FAILURE: // = 14,
|
|
||||||
return "MIC failure";
|
|
||||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
|
||||||
return "4way handshake timeout";
|
|
||||||
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: // = 16,
|
|
||||||
return "group key-update timeout";
|
|
||||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
|
||||||
return "IE in 4way differs";
|
|
||||||
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
|
||||||
return "group cipher invalid";
|
|
||||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
|
||||||
return "pairwise cipher invalid";
|
|
||||||
case WIFI_REASON_AKMP_INVALID: // = 20,
|
|
||||||
return "AKMP invalid";
|
|
||||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
|
||||||
return "unsupported RSN_IE version";
|
|
||||||
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
|
||||||
return "invalid RSN_IE_CAP";
|
|
||||||
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
|
||||||
return "802 X1 auth failed";
|
|
||||||
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
|
||||||
return "cipher suite rejected";
|
|
||||||
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
|
||||||
return "beacon timeout";
|
|
||||||
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
|
||||||
return "no AP found";
|
|
||||||
case WIFI_REASON_AUTH_FAIL: // = 202,
|
|
||||||
return "auth fail";
|
|
||||||
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
|
||||||
return "assoc fail";
|
|
||||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
|
||||||
return "handshake timeout";
|
|
||||||
case WIFI_REASON_CONNECTION_FAIL: // 205,
|
|
||||||
return "connection fail";
|
|
||||||
case WIFI_REASON_AP_TSF_RESET: // 206,
|
|
||||||
return "AP tsf reset";
|
|
||||||
case WIFI_REASON_ROAMING: // 207,
|
|
||||||
return "roaming";
|
|
||||||
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG: // 208,
|
|
||||||
return "assoc comeback time too long";
|
|
||||||
case WIFI_REASON_SA_QUERY_TIMEOUT: // 209,
|
|
||||||
return "sa query timeout";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// handles both WiFI and Ethernet
|
|
||||||
void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_STOP:
|
|
||||||
if (_stopping) {
|
|
||||||
_lastConnectionAttempt = 0;
|
|
||||||
_stopping = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
||||||
connectcount_ = connectcount_ + 1; // count the number of WiFi reconnects
|
|
||||||
emsesp::EMSESP::logger().warning("WiFi disconnected (#%d). Reason: %s (%d)",
|
|
||||||
connectcount_,
|
|
||||||
disconnectReason(info.wifi_sta_disconnected.reason),
|
|
||||||
info.wifi_sta_disconnected.reason); // IDF 4.0
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(false);
|
|
||||||
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
||||||
char result[10];
|
|
||||||
emsesp::EMSESP::logger().info("WiFi connected (Local IP=%s, hostname=%s, TxPower=%s dBm)",
|
|
||||||
WiFi.localIP().toString().c_str(),
|
|
||||||
WiFi.getHostname(),
|
|
||||||
emsesp::Helpers::render_value(result, ((double)(WiFi.getTxPower()) / 4), 1));
|
|
||||||
mDNS_start();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_START:
|
|
||||||
// apply hostname FIRST so DHCP DISCOVER carries the correct name
|
|
||||||
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
|
||||||
if (_state.staticIPConfig) {
|
|
||||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
|
||||||
// prevent double calls to mDNS
|
|
||||||
if (!emsesp::EMSESP::system_.ethernet_connected()) {
|
|
||||||
emsesp::EMSESP::logger().info("Ethernet connected (Local IP=%s, speed %d Mbps)", ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
|
||||||
emsesp::EMSESP::system_.ethernet_connected(true);
|
|
||||||
mDNS_start();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
|
||||||
emsesp::EMSESP::logger().warning("Ethernet disconnected. Reason: %s (%d)",
|
|
||||||
disconnectReason(info.wifi_sta_disconnected.reason),
|
|
||||||
info.wifi_sta_disconnected.reason);
|
|
||||||
emsesp::EMSESP::system_.ethernet_connected(false);
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_STOP:
|
|
||||||
emsesp::EMSESP::logger().info("Ethernet stopped");
|
|
||||||
emsesp::EMSESP::system_.ethernet_connected(false);
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
|
||||||
// Set the TxPower after the connection is established, if we're using TxPower = 0 (Auto)
|
|
||||||
if (_state.tx_power == 0) {
|
|
||||||
setWiFiPowerOnRSSI();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// IPv6 specific - WiFi/Eth
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP6: {
|
|
||||||
auto ip6 = IPAddress(IPv6, (uint8_t *)info.got_ip6.ip6_info.ip.addr, 0).toString();
|
|
||||||
const char * link = event == ARDUINO_EVENT_ETH_GOT_IP6 ? "Eth" : "WiFi";
|
|
||||||
if (ip6.startsWith("fe80")) {
|
|
||||||
emsesp::EMSESP::logger().info("IPv6 (%s) local: %s", link, ip6.c_str());
|
|
||||||
} else if (ip6.startsWith("fd") || ip6.startsWith("fc")) {
|
|
||||||
emsesp::EMSESP::logger().info("IPv6 (%s) ULA: %s", link, ip6.c_str());
|
|
||||||
} else {
|
|
||||||
emsesp::EMSESP::logger().info("IPv6 (%s) global: %s", link, ip6.c_str());
|
|
||||||
}
|
|
||||||
emsesp::EMSESP::system_.has_ipv6(true);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkSettings::read(NetworkSettings & settings, JsonObject root) {
|
void NetworkSettings::read(NetworkSettings & settings, JsonObject root) {
|
||||||
// connection settings
|
// connection settings
|
||||||
root["ssid"] = settings.ssid;
|
root["ssid"] = settings.ssid;
|
||||||
@@ -544,6 +73,7 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
|
|||||||
}
|
}
|
||||||
|
|
||||||
// see if we need to inform the user of a restart
|
// see if we need to inform the user of a restart
|
||||||
|
// if tx power, enableCORS, CORSOrigin, ssid changes, we need to restart
|
||||||
if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin
|
if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin
|
||||||
|| (ssid != settings.ssid && settings.ssid.isEmpty())) {
|
|| (ssid != settings.ssid && settings.ssid.isEmpty())) {
|
||||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
||||||
|
|||||||
@@ -8,15 +8,10 @@
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include <ETH.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <ESPmDNS.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
||||||
#define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings"
|
#define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings"
|
||||||
#define WIFI_RECONNECTION_DELAY (1000 * 3)
|
|
||||||
|
|
||||||
#ifndef FACTORY_WIFI_SSID
|
#ifndef FACTORY_WIFI_SSID
|
||||||
#define FACTORY_WIFI_SSID ""
|
#define FACTORY_WIFI_SSID ""
|
||||||
@@ -30,37 +25,6 @@
|
|||||||
#define FACTORY_WIFI_HOSTNAME ""
|
#define FACTORY_WIFI_HOSTNAME ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// copied from Tasmota
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2
|
|
||||||
#define MAX_TX_PWR_DBM_11b 195
|
|
||||||
#define MAX_TX_PWR_DBM_54g 150
|
|
||||||
#define MAX_TX_PWR_DBM_n 130
|
|
||||||
#define WIFI_SENSITIVITY_11b -880
|
|
||||||
#define WIFI_SENSITIVITY_54g -750
|
|
||||||
#define WIFI_SENSITIVITY_n -720
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
|
||||||
#define MAX_TX_PWR_DBM_11b 210
|
|
||||||
#define MAX_TX_PWR_DBM_54g 190
|
|
||||||
#define MAX_TX_PWR_DBM_n 185
|
|
||||||
#define WIFI_SENSITIVITY_11b -880
|
|
||||||
#define WIFI_SENSITIVITY_54g -760
|
|
||||||
#define WIFI_SENSITIVITY_n -720
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
#define MAX_TX_PWR_DBM_11b 210
|
|
||||||
#define MAX_TX_PWR_DBM_54g 190
|
|
||||||
#define MAX_TX_PWR_DBM_n 185
|
|
||||||
#define WIFI_SENSITIVITY_11b -880
|
|
||||||
#define WIFI_SENSITIVITY_54g -760
|
|
||||||
#define WIFI_SENSITIVITY_n -730
|
|
||||||
#else
|
|
||||||
#define MAX_TX_PWR_DBM_11b 195
|
|
||||||
#define MAX_TX_PWR_DBM_54g 160
|
|
||||||
#define MAX_TX_PWR_DBM_n 140
|
|
||||||
#define WIFI_SENSITIVITY_11b -880
|
|
||||||
#define WIFI_SENSITIVITY_54g -750
|
|
||||||
#define WIFI_SENSITIVITY_n -700
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class NetworkSettings {
|
class NetworkSettings {
|
||||||
public:
|
public:
|
||||||
// core wifi configuration
|
// core wifi configuration
|
||||||
@@ -92,27 +56,10 @@ class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
|||||||
NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
void loop();
|
|
||||||
|
|
||||||
uint16_t getWifiReconnects() const {
|
|
||||||
return connectcount_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HttpEndpoint<NetworkSettings> _httpEndpoint;
|
HttpEndpoint<NetworkSettings> _httpEndpoint;
|
||||||
FSPersistence<NetworkSettings> _fsPersistence;
|
FSPersistence<NetworkSettings> _fsPersistence;
|
||||||
|
|
||||||
volatile unsigned long _lastConnectionAttempt;
|
|
||||||
volatile bool _stopping;
|
|
||||||
|
|
||||||
volatile uint16_t connectcount_ = 0; // number of wifi reconnects
|
|
||||||
|
|
||||||
void WiFiEvent(arduino_event_id_t event, arduino_event_info_t info);
|
|
||||||
void mDNS_start() const;
|
|
||||||
const char * disconnectReason(uint8_t code);
|
|
||||||
void reconfigureWiFiConnection();
|
|
||||||
void manageSTA();
|
|
||||||
void setWiFiPowerOnRSSI();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
auto * response = new AsyncJsonResponse(false);
|
auto * response = new AsyncJsonResponse(false);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
|
|
||||||
bool ethernet_connected = emsesp::EMSESP::system_.ethernet_connected();
|
wl_status_t wifi_status = WiFi.status();
|
||||||
wl_status_t wifi_status = WiFi.status();
|
|
||||||
|
|
||||||
// see if Ethernet is connected
|
// see if Ethernet is connected
|
||||||
if (ethernet_connected) {
|
if (emsesp::EMSESP::network_.ethernet_connected()) {
|
||||||
root["status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
|
root["status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
|
||||||
root["hostname"] = ETH.getHostname();
|
root["hostname"] = ETH.getHostname();
|
||||||
} else {
|
} else {
|
||||||
@@ -29,7 +28,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for both connections show ethernet
|
// for both connections show ethernet
|
||||||
if (ethernet_connected) {
|
if (emsesp::EMSESP::network_.ethernet_connected()) {
|
||||||
// Ethernet
|
// Ethernet
|
||||||
root["local_ip"] = ETH.localIP().toString();
|
root["local_ip"] = ETH.localIP().toString();
|
||||||
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
|
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
|
||||||
@@ -52,7 +51,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
|||||||
root["ssid"] = WiFi.SSID();
|
root["ssid"] = WiFi.SSID();
|
||||||
root["bssid"] = WiFi.BSSIDstr();
|
root["bssid"] = WiFi.BSSIDstr();
|
||||||
root["channel"] = WiFi.channel();
|
root["channel"] = WiFi.channel();
|
||||||
root["reconnect_count"] = emsesp::EMSESP::esp32React.getWifiReconnects();
|
root["reconnect_count"] = emsesp::EMSESP::network_.getWifiReconnects();
|
||||||
root["subnet_mask"] = WiFi.subnetMask().toString();
|
root["subnet_mask"] = WiFi.subnetMask().toString();
|
||||||
|
|
||||||
if (WiFi.gatewayIP() != INADDR_NONE) {
|
if (WiFi.gatewayIP() != INADDR_NONE) {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
commands->add_command(ShellContext::MAIN,
|
commands->add_command(ShellContext::MAIN,
|
||||||
CommandFlags::ADMIN,
|
CommandFlags::ADMIN,
|
||||||
string_vector{F_(wifi), F_(reconnect)},
|
string_vector{F_(wifi), F_(reconnect)},
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) { EMSESP::system_.wifi_reconnect(); });
|
[](Shell & shell, const std::vector<std::string> & arguments) { EMSESP::network_.reconnect(); });
|
||||||
|
|
||||||
//
|
//
|
||||||
// SET commands
|
// SET commands
|
||||||
@@ -250,12 +250,12 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||||
if (completed) {
|
if (completed) {
|
||||||
if (password1 == password2) {
|
if (password1 == password2) {
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
|
||||||
networkSettings.password = password2.c_str();
|
networkSettings.password = password2.c_str();
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
shell.println("WiFi password updated. Reconnecting...");
|
shell.println("WiFi password updated. Reconnecting...");
|
||||||
EMSESP::system_.wifi_reconnect();
|
EMSESP::network_.reconnect();
|
||||||
} else {
|
} else {
|
||||||
shell.println("Passwords do not match");
|
shell.println("Passwords do not match");
|
||||||
}
|
}
|
||||||
@@ -277,6 +277,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
networkSettings.hostname = arguments.front().c_str();
|
networkSettings.hostname = arguments.front().c_str();
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
|
EMSESP::network_.reconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
commands->add_command(ShellContext::MAIN,
|
commands->add_command(ShellContext::MAIN,
|
||||||
@@ -284,12 +285,14 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
string_vector{F_(set), F_(wifi), F_(ssid)},
|
string_vector{F_(set), F_(wifi), F_(ssid)},
|
||||||
{F_(name_mandatory)},
|
{F_(name_mandatory)},
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
shell.println("The network connection will be reset...");
|
||||||
|
Shell::loop_all();
|
||||||
|
delay(1000); // wait a second
|
||||||
|
EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
|
||||||
networkSettings.ssid = arguments.front().c_str();
|
networkSettings.ssid = arguments.front().c_str();
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
shell.println("WiFi ssid updated. Reconnecting...");
|
EMSESP::network_.reconnect();
|
||||||
EMSESP::system_.wifi_reconnect();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ TxService EMSESP::txservice_; // outgoing Telegram Tx handler
|
|||||||
Mqtt EMSESP::mqtt_; // mqtt handler
|
Mqtt EMSESP::mqtt_; // mqtt handler
|
||||||
Modbus * EMSESP::modbus_ = nullptr; // modbus handler
|
Modbus * EMSESP::modbus_ = nullptr; // modbus handler
|
||||||
System EMSESP::system_; // core system services
|
System EMSESP::system_; // core system services
|
||||||
|
Network EMSESP::network_; // network services
|
||||||
TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors
|
TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors
|
||||||
AnalogSensor EMSESP::analogsensor_; // Analog sensors
|
AnalogSensor EMSESP::analogsensor_; // Analog sensors
|
||||||
Shower EMSESP::shower_; // Shower logic
|
Shower EMSESP::shower_; // Shower logic
|
||||||
@@ -1721,13 +1722,13 @@ void EMSESP::start() {
|
|||||||
// start web log service. now we can start capturing logs to the web log
|
// start web log service. now we can start capturing logs to the web log
|
||||||
webLogService.begin();
|
webLogService.begin();
|
||||||
|
|
||||||
// loads core system services settings (network, mqtt, ap, ntp etc)
|
// loads core system services settings (mqtt, ap, ntp etc)
|
||||||
esp32React.begin();
|
esp32React.begin();
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (factory_settings) {
|
if (factory_settings) {
|
||||||
LOG_WARNING("No settings found on filesystem. Using factory settings.");
|
LOG_WARNING("No settings found on filesystem. Using factory settings.");
|
||||||
// make sure OTAdata is updated with core3 format
|
// make sure OTAdata is updated with core3 (v3.9.0) format
|
||||||
esp_ota_set_boot_partition(esp_ota_get_running_partition());
|
esp_ota_set_boot_partition(esp_ota_get_running_partition());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1777,6 +1778,9 @@ void EMSESP::start() {
|
|||||||
|
|
||||||
webSettingsService.begin(); // load EMS-ESP Application settings
|
webSettingsService.begin(); // load EMS-ESP Application settings
|
||||||
|
|
||||||
|
// start network services. This will initialise WiFi or Ethernet depending on the settings.
|
||||||
|
network_.begin();
|
||||||
|
|
||||||
// perform any system upgrades
|
// perform any system upgrades
|
||||||
if (!factory_settings) {
|
if (!factory_settings) {
|
||||||
if (system_.check_upgrade()) {
|
if (system_.check_upgrade()) {
|
||||||
@@ -1847,6 +1851,9 @@ void EMSESP::shell_prompt() {
|
|||||||
void EMSESP::loop() {
|
void EMSESP::loop() {
|
||||||
uuid::loop(); // store system uptime
|
uuid::loop(); // store system uptime
|
||||||
|
|
||||||
|
// handle network
|
||||||
|
network_.loop();
|
||||||
|
|
||||||
// handles LED and checks system health, and syslog service
|
// handles LED and checks system health, and syslog service
|
||||||
if (system_.loop()) {
|
if (system_.loop()) {
|
||||||
return; // LED flashing is active, skip the rest of the loop
|
return; // LED flashing is active, skip the rest of the loop
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
#include "modbus.h"
|
#include "modbus.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "network.h"
|
||||||
#include "temperaturesensor.h"
|
#include "temperaturesensor.h"
|
||||||
#include "analogsensor.h"
|
#include "analogsensor.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
@@ -228,6 +229,7 @@ class EMSESP {
|
|||||||
static Mqtt mqtt_;
|
static Mqtt mqtt_;
|
||||||
static Modbus * modbus_;
|
static Modbus * modbus_;
|
||||||
static System system_;
|
static System system_;
|
||||||
|
static Network network_;
|
||||||
static TemperatureSensor temperaturesensor_;
|
static TemperatureSensor temperaturesensor_;
|
||||||
static AnalogSensor analogsensor_;
|
static AnalogSensor analogsensor_;
|
||||||
static Shower shower_;
|
static Shower shower_;
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ MAKE_WORD(mqtt)
|
|||||||
MAKE_WORD(gpio)
|
MAKE_WORD(gpio)
|
||||||
MAKE_WORD(modbus)
|
MAKE_WORD(modbus)
|
||||||
MAKE_WORD(emsesp)
|
MAKE_WORD(emsesp)
|
||||||
|
MAKE_WORD(network)
|
||||||
MAKE_WORD(connected)
|
MAKE_WORD(connected)
|
||||||
MAKE_WORD(disconnected)
|
MAKE_WORD(disconnected)
|
||||||
MAKE_WORD(passwd)
|
MAKE_WORD(passwd)
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ void Mqtt::ha_status() {
|
|||||||
// create the HA sensors - must match the MQTT payload keys in the heartbeat topic
|
// create the HA sensors - must match the MQTT payload keys in the heartbeat topic
|
||||||
// Note we don't use camelCase as it would change the HA entity_id and impact historic data
|
// Note we don't use camelCase as it would change the HA entity_id and impact historic data
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (!EMSESP::system_.ethernet_connected() || WiFi.isConnected()) {
|
if (EMSESP::network_.wifi_connected()) {
|
||||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "RSSI", "rssi", DeviceValueUOM::DBM);
|
publish_system_ha_sensor_config(DeviceValueType::INT8, "RSSI", "rssi", DeviceValueUOM::DBM);
|
||||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "Signal", "wifistrength", DeviceValueUOM::PERCENT);
|
publish_system_ha_sensor_config(DeviceValueType::INT8, "Signal", "wifistrength", DeviceValueUOM::PERCENT);
|
||||||
}
|
}
|
||||||
@@ -585,7 +585,7 @@ void Mqtt::ha_status() {
|
|||||||
publish_system_ha_sensor_config(DeviceValueType::INT8, "CPU temperature", "temperature", DeviceValueUOM::DEGREES);
|
publish_system_ha_sensor_config(DeviceValueType::INT8, "CPU temperature", "temperature", DeviceValueUOM::DEGREES);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!EMSESP::system_.ethernet_connected()) {
|
if (!EMSESP::network_.ethernet_connected()) {
|
||||||
publish_system_ha_sensor_config(DeviceValueType::INT16, "WiFi reconnects", "wifireconnects", DeviceValueUOM::NONE);
|
publish_system_ha_sensor_config(DeviceValueType::INT16, "WiFi reconnects", "wifireconnects", DeviceValueUOM::NONE);
|
||||||
}
|
}
|
||||||
// This one comes from the info MQTT topic - and handled in the publish_ha_sensor_config function
|
// This one comes from the info MQTT topic - and handled in the publish_ha_sensor_config function
|
||||||
|
|||||||
673
src/core/network.cpp
Normal file
673
src/core/network.cpp
Normal file
@@ -0,0 +1,673 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020-2025 emsesp.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
uuid::log::Logger Network::logger_{F_(network), uuid::log::Facility::KERN};
|
||||||
|
|
||||||
|
void Network::begin() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// pull Network settings and store locally on stack
|
||||||
|
EMSESP::esp32React.getNetworkSettingsService()->read([&](auto & settings) {
|
||||||
|
enableMDNS_ = settings.enableMDNS;
|
||||||
|
staticIPConfig_ = settings.staticIPConfig;
|
||||||
|
localIP_ = settings.localIP;
|
||||||
|
gatewayIP_ = settings.gatewayIP;
|
||||||
|
subnetMask_ = settings.subnetMask;
|
||||||
|
dnsIP1_ = settings.dnsIP1;
|
||||||
|
dnsIP2_ = settings.dnsIP2;
|
||||||
|
hostname_ = settings.hostname;
|
||||||
|
ssid_ = settings.ssid;
|
||||||
|
password_ = settings.password;
|
||||||
|
bandwidth20_ = settings.bandwidth20;
|
||||||
|
nosleep_ = settings.nosleep;
|
||||||
|
tx_power_ = settings.tx_power;
|
||||||
|
bssid_ = settings.bssid;
|
||||||
|
});
|
||||||
|
|
||||||
|
// read Ethernet settings
|
||||||
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
|
phy_type_ = settings.phy_type;
|
||||||
|
eth_power_ = settings.eth_power;
|
||||||
|
eth_phy_addr_ = settings.eth_phy_addr;
|
||||||
|
eth_clock_mode_ = settings.eth_clock_mode;
|
||||||
|
});
|
||||||
|
|
||||||
|
// get Access Point settings
|
||||||
|
EMSESP::esp32React.getAPSettingsService()->read([&](APSettings & settings) {
|
||||||
|
ap_provisionMode_ = settings.provisionMode;
|
||||||
|
ap_ssid_ = settings.ssid;
|
||||||
|
ap_password_ = settings.password;
|
||||||
|
ap_channel_ = settings.channel;
|
||||||
|
ap_ssid_hidden_ = settings.ssidHidden;
|
||||||
|
ap_max_clients_ = settings.maxClients;
|
||||||
|
ap_localIP_ = settings.localIP;
|
||||||
|
ap_gatewayIP_ = settings.gatewayIP;
|
||||||
|
ap_subnetMask_ = settings.subnetMask;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialise WiFi - we only do this once, when the network service is started
|
||||||
|
// We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default.
|
||||||
|
// If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future.
|
||||||
|
if (WiFi.getMode() != WIFI_OFF) {
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable WiFi config persistance and auto reconnect
|
||||||
|
WiFi.persistent(false);
|
||||||
|
WiFi.setAutoReconnect(false);
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
|
||||||
|
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
||||||
|
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
||||||
|
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
||||||
|
|
||||||
|
// capture the WIFI_REASON_* code on every STA disconnect event so check_connection() can
|
||||||
|
// log a meaningful reason when its periodic poll notices we're no longer associated
|
||||||
|
WiFi.onEvent([this](WiFiEvent_t /*event*/, WiFiEventInfo_t info) { last_disconnect_reason_ = info.wifi_sta_disconnected.reason; },
|
||||||
|
ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
||||||
|
|
||||||
|
// clear the saved reason on a fresh STA association so we don't log a stale code on the next disconnect
|
||||||
|
WiFi.onEvent([this](WiFiEvent_t /*event*/, WiFiEventInfo_t /*info*/) { last_disconnect_reason_ = 0; }, ARDUINO_EVENT_WIFI_STA_GOT_IP);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// format the BSSID (MAC address) of the network interface
|
||||||
|
bool Network::formatBSSID(const String & bssid, uint8_t (&mac)[6]) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
uint tmp[6];
|
||||||
|
if (bssid.isEmpty() || sscanf(bssid.c_str(), "%X:%X:%X:%X:%X:%X", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
mac[i] = static_cast<uint8_t>(tmp[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the local IP address of the network interface
|
||||||
|
std::string Network::getLocalIP() const {
|
||||||
|
switch (network_iface_) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
case NetIface::AP:
|
||||||
|
return WiFi.softAPIP().toString().c_str();
|
||||||
|
case NetIface::WIFI:
|
||||||
|
return WiFi.localIP().toString().c_str();
|
||||||
|
case NetIface::ETHERNET:
|
||||||
|
return ETH.localIP().toString().c_str();
|
||||||
|
case NetIface::NONE:
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the MAC address of the network interface
|
||||||
|
std::string Network::getMacAddress() const {
|
||||||
|
switch (network_iface_) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
case NetIface::AP:
|
||||||
|
return WiFi.softAPmacAddress().c_str();
|
||||||
|
case NetIface::WIFI:
|
||||||
|
return WiFi.macAddress().c_str();
|
||||||
|
case NetIface::ETHERNET:
|
||||||
|
return ETH.macAddress().c_str();
|
||||||
|
case NetIface::NONE:
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the number of stations connected to the AP
|
||||||
|
uint8_t Network::getStationNum() const {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
return network_iface_ == NetIface::AP ? WiFi.softAPgetStationNum() : 0;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// disconnect all WiFi, Eth and AP
|
||||||
|
// so we can starts searching again to reconnect
|
||||||
|
void Network::reconnect() {
|
||||||
|
LOG_DEBUG("Disconnecting all networks");
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// disconnect WiFi
|
||||||
|
if (wifi_connected()) {
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disconnect AP
|
||||||
|
|
||||||
|
if (WiFi.getMode() & WIFI_AP) {
|
||||||
|
stopAP();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// reset network state
|
||||||
|
network_ip_ = 0;
|
||||||
|
network_iface_ = NetIface::NONE;
|
||||||
|
has_ipv6_ = false;
|
||||||
|
juststopped_ = true;
|
||||||
|
|
||||||
|
// reload the network settings
|
||||||
|
begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// network loop, looking for new and disconnecting networks
|
||||||
|
void Network::loop() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// if we already have a Wifi or Ethernet connection then re-check every NETWORK_RECONNECTION_DELAY_LONG, otherwise NETWORK_RECONNECTION_DELAY_SHORT
|
||||||
|
const unsigned long currentMillis = millis();
|
||||||
|
const uint32_t reconnectDelay =
|
||||||
|
(network_iface_ == NetIface::WIFI || network_iface_ == NetIface::ETHERNET) ? NETWORK_RECONNECTION_DELAY_LONG : NETWORK_RECONNECTION_DELAY_SHORT;
|
||||||
|
if (!lastConnectionAttempt_ || static_cast<uint32_t>(currentMillis - lastConnectionAttempt_) >= reconnectDelay) {
|
||||||
|
lastConnectionAttempt_ = currentMillis;
|
||||||
|
|
||||||
|
// manage network interfaces
|
||||||
|
startAP(); // Captive Portal (AP)
|
||||||
|
startWIFI(); // WiFi
|
||||||
|
startEthernet(); // Ethernet
|
||||||
|
|
||||||
|
if (network_ip_ != 0) {
|
||||||
|
checkConnection(); // already have a connection: verify it's still alive
|
||||||
|
}
|
||||||
|
findNetworks(); // detect new connections
|
||||||
|
}
|
||||||
|
|
||||||
|
// process DNS requests for the captive portal while the soft-AP is up
|
||||||
|
if (ap_dnsServer_) {
|
||||||
|
ap_dnsServer_->processNextRequest();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-validate the currently active connection
|
||||||
|
// if a netif is no longer up or has lost its IP (cable unplugged, AP gone, DHCP lease lost, ...) we drop our state so
|
||||||
|
// find_networks() can pick up a new one
|
||||||
|
void Network::checkConnection() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
bool still_up = false;
|
||||||
|
for (esp_netif_t * netif = esp_netif_next_unsafe(NULL); netif != NULL; netif = esp_netif_next_unsafe(netif)) {
|
||||||
|
if (iface_from_desc(esp_netif_get_desc(netif)) != network_iface_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
esp_netif_ip_info_t ip_info = {};
|
||||||
|
if (esp_netif_is_netif_up(netif) && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK && ip_info.ip.addr == network_ip_) {
|
||||||
|
still_up = true;
|
||||||
|
}
|
||||||
|
break; // only one active netif per kind in our world (sta / eth / ap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!still_up) {
|
||||||
|
if (network_iface_ == NetIface::WIFI) {
|
||||||
|
uint8_t reason = last_disconnect_reason_;
|
||||||
|
if (reason == 0) {
|
||||||
|
reason = WIFI_REASON_UNSPECIFIED; // event hasn't fired yet (or was cleared); avoid logging "0"
|
||||||
|
}
|
||||||
|
LOG_INFO("WiFi connection lost (reason %u: %s)", reason, disconnectReason(reason));
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Ethernet connection lost");
|
||||||
|
}
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the WiFi TxPower based on the RSSI (signal strength), picking the lowest value
|
||||||
|
// code is based of RSSI (signal strength) and copied from Tasmota's WiFiSetTXpowerBasedOnRssi() which is copied ESPEasy's ESPEasyWifi.SetWiFiTXpower() function
|
||||||
|
// Range ESP32 : 2dBm - 20dBm
|
||||||
|
// 802.11b - wifi1
|
||||||
|
// 802.11a - wifi2
|
||||||
|
// 802.11g - wifi3
|
||||||
|
// 802.11n - wifi4
|
||||||
|
// 802.11ac - wifi5
|
||||||
|
// 802.11ax - wifi6
|
||||||
|
// tx_power is the Tx power to set, 0 for auto
|
||||||
|
void Network::setWiFiPower(uint8_t tx_power) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
if (tx_power != 0) {
|
||||||
|
if (!WiFi.setTxPower(static_cast<wifi_power_t>(tx_power))) {
|
||||||
|
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_tx_pwr = MAX_TX_PWR_DBM_n; // assume wifi4
|
||||||
|
int threshold = WIFI_SENSITIVITY_n + 120; // Margin in dBm * 10 on top of threshold
|
||||||
|
|
||||||
|
// Assume AP sends with max set by ETSI standard.
|
||||||
|
// 2.4 GHz: 100 mWatt (20 dBm)
|
||||||
|
// US and some other countries allow 1000 mW (30 dBm)
|
||||||
|
int rssi = WiFi.RSSI() * 10;
|
||||||
|
int newrssi = rssi - 200; // We cannot send with over 20 dBm, thus it makes no sense to force higher TX power all the time.
|
||||||
|
|
||||||
|
int min_tx_pwr = 0;
|
||||||
|
if (newrssi < threshold) {
|
||||||
|
min_tx_pwr = threshold - newrssi;
|
||||||
|
}
|
||||||
|
if (min_tx_pwr > max_tx_pwr) {
|
||||||
|
min_tx_pwr = max_tx_pwr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from WiFIGeneric.h use:
|
||||||
|
wifi_power_t p = WIFI_POWER_2dBm;
|
||||||
|
if (min_tx_pwr > 185)
|
||||||
|
p = WIFI_POWER_19_5dBm;
|
||||||
|
else if (min_tx_pwr > 170)
|
||||||
|
p = WIFI_POWER_18_5dBm;
|
||||||
|
else if (min_tx_pwr > 150)
|
||||||
|
p = WIFI_POWER_17dBm;
|
||||||
|
else if (min_tx_pwr > 130)
|
||||||
|
p = WIFI_POWER_15dBm;
|
||||||
|
else if (min_tx_pwr > 110)
|
||||||
|
p = WIFI_POWER_13dBm;
|
||||||
|
else if (min_tx_pwr > 85)
|
||||||
|
p = WIFI_POWER_11dBm;
|
||||||
|
else if (min_tx_pwr > 70)
|
||||||
|
p = WIFI_POWER_8_5dBm;
|
||||||
|
else if (min_tx_pwr > 50)
|
||||||
|
p = WIFI_POWER_7dBm;
|
||||||
|
else if (min_tx_pwr > 20)
|
||||||
|
p = WIFI_POWER_5dBm;
|
||||||
|
|
||||||
|
#if defined(EMSESP_DEBUG)
|
||||||
|
// emsesp::EMSESP::logger().debug("Recommended WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d)", min_tx_pwr / 10, p, rssi, threshold);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!WiFi.setTxPower(p)) {
|
||||||
|
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the multicast UDP service so EMS-ESP is discoverable via .local
|
||||||
|
void Network::startmDNS() const {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
MDNS.end();
|
||||||
|
|
||||||
|
if (!enableMDNS_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MDNS.begin(emsesp::EMSESP::system_.hostname().c_str())) {
|
||||||
|
emsesp::EMSESP::logger().warning("Failed to start mDNS Responder service");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string address_s = emsesp::EMSESP::system_.hostname() + ".local";
|
||||||
|
|
||||||
|
MDNS.addService("http", "tcp", 80); // add our web server and rest API
|
||||||
|
MDNS.addService("telnet", "tcp", 23); // add our telnet console
|
||||||
|
MDNS.addServiceTxt("http", "tcp", "address", address_s.c_str());
|
||||||
|
|
||||||
|
emsesp::EMSESP::logger().info("Starting mDNS Responder service");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi disconnect reason code to string
|
||||||
|
const char * Network::disconnectReason(uint8_t code) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
switch (code) {
|
||||||
|
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||||
|
return "unspecified";
|
||||||
|
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
||||||
|
return "auth expire";
|
||||||
|
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
||||||
|
return "auth leave";
|
||||||
|
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
||||||
|
return "assoc expired";
|
||||||
|
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
||||||
|
return "assoc too many";
|
||||||
|
case WIFI_REASON_NOT_AUTHED: // = 6,
|
||||||
|
return "not authenticated";
|
||||||
|
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
||||||
|
return "not assoc";
|
||||||
|
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
||||||
|
return "assoc leave";
|
||||||
|
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
||||||
|
return "assoc not authed";
|
||||||
|
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
||||||
|
return "disassoc powerCAP bad";
|
||||||
|
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
||||||
|
return "disassoc supchan bad";
|
||||||
|
case WIFI_REASON_IE_INVALID: // = 13,
|
||||||
|
return "IE invalid";
|
||||||
|
case WIFI_REASON_MIC_FAILURE: // = 14,
|
||||||
|
return "MIC failure";
|
||||||
|
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
||||||
|
return "4way handshake timeout";
|
||||||
|
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: // = 16,
|
||||||
|
return "group key-update timeout";
|
||||||
|
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
||||||
|
return "IE in 4way differs";
|
||||||
|
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
||||||
|
return "group cipher invalid";
|
||||||
|
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
||||||
|
return "pairwise cipher invalid";
|
||||||
|
case WIFI_REASON_AKMP_INVALID: // = 20,
|
||||||
|
return "AKMP invalid";
|
||||||
|
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
||||||
|
return "unsupported RSN_IE version";
|
||||||
|
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
||||||
|
return "invalid RSN_IE_CAP";
|
||||||
|
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
||||||
|
return "802 X1 auth failed";
|
||||||
|
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
||||||
|
return "cipher suite rejected";
|
||||||
|
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
||||||
|
return "beacon timeout";
|
||||||
|
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
||||||
|
return "no AP found";
|
||||||
|
case WIFI_REASON_AUTH_FAIL: // = 202,
|
||||||
|
return "auth fail";
|
||||||
|
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
||||||
|
return "assoc fail";
|
||||||
|
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
||||||
|
return "handshake timeout";
|
||||||
|
case WIFI_REASON_CONNECTION_FAIL: // 205,
|
||||||
|
return "connection fail";
|
||||||
|
case WIFI_REASON_AP_TSF_RESET: // 206,
|
||||||
|
return "AP tsf reset";
|
||||||
|
case WIFI_REASON_ROAMING: // 207,
|
||||||
|
return "roaming";
|
||||||
|
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG: // 208,
|
||||||
|
return "assoc comeback time too long";
|
||||||
|
case WIFI_REASON_SA_QUERY_TIMEOUT: // 209,
|
||||||
|
return "sa query timeout";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi management
|
||||||
|
void Network::startWIFI() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// Abort if already connected, or if we have no SSID
|
||||||
|
if (WiFi.isConnected() || ssid_.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("Managing WiFi"); // TODO remove
|
||||||
|
|
||||||
|
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
||||||
|
WiFi.enableSTA(true); // creates the STA netif
|
||||||
|
WiFi.STA.setHostname(hostname_.c_str()); // pushes to esp_netif_set_hostname
|
||||||
|
WiFi.enableIPv6(true);
|
||||||
|
if (staticIPConfig_) {
|
||||||
|
WiFi.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); // configure for static IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// www.esp32.com/viewtopic.php?t=12055
|
||||||
|
if (bandwidth20_) {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
||||||
|
} else {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
||||||
|
}
|
||||||
|
if (nosleep_) {
|
||||||
|
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to connect to the network
|
||||||
|
uint8_t bssid[6];
|
||||||
|
wl_status_t status;
|
||||||
|
if (formatBSSID(bssid_, bssid)) {
|
||||||
|
status = WiFi.begin(ssid_.c_str(), password_.c_str(), 0, bssid);
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Connecting to WiFi SSID %s with password %s, hostname %s", ssid_.c_str(), password_.c_str(), hostname_.c_str()); // TODO remove
|
||||||
|
status = WiFi.begin(ssid_.c_str(), password_.c_str());
|
||||||
|
}
|
||||||
|
if (status == WL_CONNECT_FAILED) {
|
||||||
|
LOG_ERROR("WiFi connection failed (code %d)", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOARD_C3_MINI_V1
|
||||||
|
// always hardcode Tx power for Wemos CS Mini v1
|
||||||
|
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
|
||||||
|
// https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||||
|
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
||||||
|
#else
|
||||||
|
setWiFiPower(tx_power_);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ethernet management
|
||||||
|
// Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver
|
||||||
|
// continues to run autonomously (link negotiation, DHCP, etc); the loop must NOT call ETH.begin()
|
||||||
|
// again on every iteration because that thrashes the netif and can prevent DHCP from completing
|
||||||
|
void Network::startEthernet() {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
// already up and running, nothing to do
|
||||||
|
if (eth_started_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
|
// no ethernet present or wifi takes precedence
|
||||||
|
if (phy_type_ == PHY_type::PHY_TYPE_NONE || (ssid_.length() > 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("Managing Ethernet"); // TODO remove
|
||||||
|
|
||||||
|
// configure Ethernet
|
||||||
|
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
||||||
|
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
||||||
|
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||||
|
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
||||||
|
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
||||||
|
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
||||||
|
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||||
|
// clock mode:
|
||||||
|
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||||
|
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||||
|
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||||
|
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||||
|
auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
||||||
|
|
||||||
|
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
||||||
|
if (eth_power_ != -1) {
|
||||||
|
pinMode(eth_power_, OUTPUT);
|
||||||
|
digitalWrite(eth_power_, LOW);
|
||||||
|
delay(500);
|
||||||
|
digitalWrite(eth_power_, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode)) {
|
||||||
|
eth_started_ = true; // mark up; do not re-enter this block until reboot / explicit teardown
|
||||||
|
ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
||||||
|
ETH.enableIPv6(true);
|
||||||
|
if (staticIPConfig_) {
|
||||||
|
ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Failed to start Ethernet");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the network is connected and set network_ip_ / network_iface_ / has_ipv6_
|
||||||
|
// Iterates over every esp-netif that exists, prioritizing Ethernet > WiFi > AP
|
||||||
|
bool Network::findNetworks() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
|
// exit if already have a connection, unless in AP mode
|
||||||
|
// when in AP mode, it will always try and connect to the WiFi
|
||||||
|
if (network_ip_ != 0 && !(WiFi.getMode() & WIFI_AP)) {
|
||||||
|
// const esp_ip4_addr_t ip4 = {.addr = network_ip_};
|
||||||
|
// LOG_DEBUG("Network already connected via IPv4: " IPSTR, IP2STR(&ip4));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NetInfo {
|
||||||
|
esp_ip4_addr_t ip;
|
||||||
|
esp_ip6_addr_t ip6;
|
||||||
|
char desc[8];
|
||||||
|
bool has_ipv6;
|
||||||
|
} info = {};
|
||||||
|
|
||||||
|
// Preference order: ETHERNET > WIFI (STA) > AP
|
||||||
|
auto iface_priority = [](NetIface iface) -> uint8_t {
|
||||||
|
switch (iface) {
|
||||||
|
case NetIface::ETHERNET:
|
||||||
|
return 3;
|
||||||
|
case NetIface::WIFI:
|
||||||
|
return 2;
|
||||||
|
case NetIface::AP:
|
||||||
|
return 1;
|
||||||
|
case NetIface::NONE:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NetIface best_iface = NetIface::NONE;
|
||||||
|
for (esp_netif_t * netif = esp_netif_next_unsafe(NULL); netif != NULL; netif = esp_netif_next_unsafe(netif)) {
|
||||||
|
const char * desc = esp_netif_get_desc(netif);
|
||||||
|
bool is_up = esp_netif_is_netif_up(netif);
|
||||||
|
esp_netif_ip_info_t ip_info = {};
|
||||||
|
esp_err_t err = esp_netif_get_ip_info(netif, &ip_info);
|
||||||
|
|
||||||
|
if (!is_up || err != ESP_OK || ip_info.ip.addr == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NetIface candidate = iface_from_desc(desc);
|
||||||
|
if (iface_priority(candidate) <= iface_priority(best_iface)) {
|
||||||
|
continue; // already have something at least as good
|
||||||
|
}
|
||||||
|
|
||||||
|
info.ip = ip_info.ip;
|
||||||
|
if (desc) {
|
||||||
|
strlcpy(info.desc, desc, sizeof(info.desc));
|
||||||
|
}
|
||||||
|
info.has_ipv6 = (esp_netif_get_ip6_linklocal(netif, &info.ip6) == ESP_OK);
|
||||||
|
best_iface = candidate;
|
||||||
|
|
||||||
|
if (best_iface == NetIface::ETHERNET) {
|
||||||
|
break; // top priority, can't be beaten by anything later in the list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto previous_iface = NetIface::NONE;
|
||||||
|
// LOG_DEBUG("best_iface: %d, previous_iface: %d, network_iface_: %d", best_iface, previous_iface, network_iface_); // TODO remove
|
||||||
|
|
||||||
|
// if we have a connection and it's a new one, set it up
|
||||||
|
if (best_iface != NetIface::NONE && best_iface != previous_iface) {
|
||||||
|
previous_iface = network_iface_; // save the previous interface for comparison next time
|
||||||
|
network_ip_ = info.ip.addr;
|
||||||
|
network_iface_ = iface_from_desc(info.desc); // "sta"/"ap"/"eth*"
|
||||||
|
has_ipv6_ = info.has_ipv6;
|
||||||
|
connnect_retry_ = 0;
|
||||||
|
|
||||||
|
LOG_INFO("Network connected via %s (IP: " IPSTR ")",
|
||||||
|
network_iface_ == NetIface::ETHERNET ? "Ethernet"
|
||||||
|
: network_iface_ == NetIface::WIFI ? "WiFi"
|
||||||
|
: network_iface_ == NetIface::AP ? "AP"
|
||||||
|
: "unknown",
|
||||||
|
IP2STR(&info.ip));
|
||||||
|
|
||||||
|
// if we have a Eth or Wifi connection and the AP is running, stop it
|
||||||
|
if (network_iface_ != NetIface::AP && WiFi.getMode() & WIFI_AP) {
|
||||||
|
stopAP();
|
||||||
|
}
|
||||||
|
|
||||||
|
// count the number of restarts (for Wifi and Eth)
|
||||||
|
if (juststopped_) {
|
||||||
|
juststopped_ = false;
|
||||||
|
connectcount_++;
|
||||||
|
LOG_DEBUG("Network re-connection count %d", connectcount_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start mDNS for any real network interface (skip the SoftAP since the captive portal handles its own DNS)
|
||||||
|
if (enableMDNS_ && network_iface_ != NetIface::AP && network_iface_ != NetIface::NONE) {
|
||||||
|
startmDNS();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // we have a network connection
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
network_ip_ = 0;
|
||||||
|
network_iface_ = NetIface::NONE;
|
||||||
|
has_ipv6_ = false;
|
||||||
|
connnect_retry_++;
|
||||||
|
LOG_DEBUG("No active network interfaces found yet, re-connection count %d", connnect_retry_);
|
||||||
|
#endif
|
||||||
|
return false; // no connection found yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// access point (soft-AP) and the captive portal
|
||||||
|
void Network::startAP() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// Only start AP as a fallback if the Network has failed
|
||||||
|
if (connnect_retry_ < MAX_NETWORK_RECONNECTION_ATTEMPTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't start the soft-AP if it is disabled, or Ethernet has taken over or we have a real WiFi connection or it's already running
|
||||||
|
if (ap_provisionMode_ == AP_MODE_NEVER || network_connected() || WiFi.getMode() & WIFI_AP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.softAPenableIPv6(); // force IPv6, same as for STA - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
|
||||||
|
WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_);
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
||||||
|
WiFi.softAP(ap_ssid_.c_str(), ap_password_.c_str(), ap_channel_, ap_ssid_hidden_, ap_max_clients_);
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||||
|
#endif
|
||||||
|
const IPAddress apIp = WiFi.softAPIP();
|
||||||
|
LOG_INFO("Starting Access Point with captive portal on %u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
||||||
|
|
||||||
|
// start DNS server for Captive Portal
|
||||||
|
ap_dnsServer_ = new DNSServer;
|
||||||
|
ap_dnsServer_->start(DNS_PORT, "*", apIp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop AP
|
||||||
|
void Network::stopAP() {
|
||||||
|
LOG_INFO("Stopping Access Point");
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
WiFi.softAPdisconnect(true);
|
||||||
|
if (ap_dnsServer_) {
|
||||||
|
ap_dnsServer_->stop();
|
||||||
|
delete ap_dnsServer_;
|
||||||
|
ap_dnsServer_ = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace emsesp
|
||||||
215
src/core/network.h
Normal file
215
src/core/network.h
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020-2025 emsesp.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EMSESP_NETWORK_H_
|
||||||
|
#define EMSESP_NETWORK_H_
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <ETH.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <esp32-psram.h>
|
||||||
|
#include <uuid/log.h>
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
#define NETWORK_RECONNECTION_DELAY_SHORT 2000 // 2 seconds
|
||||||
|
#define NETWORK_RECONNECTION_DELAY_LONG 60000 // 60 seconds
|
||||||
|
|
||||||
|
#define MAX_NETWORK_RECONNECTION_ATTEMPTS 3 // maximum number of network reconnection attempts
|
||||||
|
|
||||||
|
#define DNS_PORT 53
|
||||||
|
|
||||||
|
// copied from Tasmota
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define MAX_TX_PWR_DBM_11b 195
|
||||||
|
#define MAX_TX_PWR_DBM_54g 150
|
||||||
|
#define MAX_TX_PWR_DBM_n 130
|
||||||
|
#define WIFI_SENSITIVITY_11b -880
|
||||||
|
#define WIFI_SENSITIVITY_54g -750
|
||||||
|
#define WIFI_SENSITIVITY_n -720
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define MAX_TX_PWR_DBM_11b 210
|
||||||
|
#define MAX_TX_PWR_DBM_54g 190
|
||||||
|
#define MAX_TX_PWR_DBM_n 185
|
||||||
|
#define WIFI_SENSITIVITY_11b -880
|
||||||
|
#define WIFI_SENSITIVITY_54g -760
|
||||||
|
#define WIFI_SENSITIVITY_n -720
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define MAX_TX_PWR_DBM_11b 210
|
||||||
|
#define MAX_TX_PWR_DBM_54g 190
|
||||||
|
#define MAX_TX_PWR_DBM_n 185
|
||||||
|
#define WIFI_SENSITIVITY_11b -880
|
||||||
|
#define WIFI_SENSITIVITY_54g -760
|
||||||
|
#define WIFI_SENSITIVITY_n -730
|
||||||
|
#else
|
||||||
|
#define MAX_TX_PWR_DBM_11b 195
|
||||||
|
#define MAX_TX_PWR_DBM_54g 160
|
||||||
|
#define MAX_TX_PWR_DBM_n 140
|
||||||
|
#define WIFI_SENSITIVITY_11b -880
|
||||||
|
#define WIFI_SENSITIVITY_54g -750
|
||||||
|
#define WIFI_SENSITIVITY_n -700
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// which physical interface we are currently using for the active network connection.
|
||||||
|
// Mapped from the esp-netif description string returned by esp_netif_get_desc(): "sta" -> WIFI,
|
||||||
|
// "ap" -> AP, "eth"/"eth1"/"eth2"/... (arduino-esp32 v3.x suffixes ETH netifs because it supports
|
||||||
|
// multiple ETH instances) -> ETHERNET. Anything else stays as NONE.
|
||||||
|
enum class NetIface : uint8_t {
|
||||||
|
NONE = 0,
|
||||||
|
WIFI,
|
||||||
|
ETHERNET,
|
||||||
|
AP,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Network {
|
||||||
|
public:
|
||||||
|
void begin();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
uint16_t getWifiReconnects() const {
|
||||||
|
return connectcount_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t network_ip() const {
|
||||||
|
return network_ip_;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetIface network_iface() const {
|
||||||
|
return network_iface_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ethernet_connected() const {
|
||||||
|
return network_iface_ == NetIface::ETHERNET && network_ip_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_connected() const {
|
||||||
|
return network_iface_ == NetIface::WIFI && network_ip_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ap_connected() const {
|
||||||
|
return network_iface_ == NetIface::AP && network_ip_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool network_connected() const {
|
||||||
|
return ethernet_connected() || wifi_connected(); // don't include AP here
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_ipv6() const {
|
||||||
|
return has_ipv6_ && (network_iface_ == NetIface::WIFI || network_iface_ == NetIface::ETHERNET);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t getWifiReconnects() {
|
||||||
|
return connectcount_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getLocalIP() const;
|
||||||
|
std::string getMacAddress() const;
|
||||||
|
uint8_t getStationNum() const;
|
||||||
|
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
// map a netif description string (from esp_netif_get_desc) to a NetIface
|
||||||
|
static NetIface iface_from_desc(const char * desc) {
|
||||||
|
if (!desc) {
|
||||||
|
return NetIface::NONE;
|
||||||
|
}
|
||||||
|
if (strcmp(desc, "sta") == 0) {
|
||||||
|
return NetIface::WIFI;
|
||||||
|
}
|
||||||
|
if (strcmp(desc, "ap") == 0) {
|
||||||
|
return NetIface::AP;
|
||||||
|
}
|
||||||
|
// any "eth*" (eth, eth0, eth1, ...) is treated as Ethernet
|
||||||
|
if (strncmp(desc, "eth", 3) == 0) {
|
||||||
|
return NetIface::ETHERNET;
|
||||||
|
}
|
||||||
|
return NetIface::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static uuid::log::Logger logger_;
|
||||||
|
|
||||||
|
bool findNetworks();
|
||||||
|
void checkConnection();
|
||||||
|
void startmDNS() const;
|
||||||
|
bool formatBSSID(const String & bssid, uint8_t (&mac)[6]);
|
||||||
|
void startAP();
|
||||||
|
void startWIFI();
|
||||||
|
void startEthernet();
|
||||||
|
void setWiFiPower(uint8_t tx_power);
|
||||||
|
const char * disconnectReason(uint8_t code);
|
||||||
|
void stopAP();
|
||||||
|
|
||||||
|
unsigned long lastConnectionAttempt_ = 0;
|
||||||
|
uint16_t connectcount_ = 0; // number of network reconnects
|
||||||
|
uint32_t network_ip_ = 0;
|
||||||
|
NetIface network_iface_ = NetIface::NONE;
|
||||||
|
bool has_ipv6_ = false;
|
||||||
|
bool juststopped_ = false;
|
||||||
|
bool eth_started_ = false; // true after ETH.begin() has succeeded once; prevents repeated re-init while DHCP is still running
|
||||||
|
volatile uint8_t last_disconnect_reason_ = 0;
|
||||||
|
uint16_t connnect_retry_ = 0; // number of network re-connection attempts
|
||||||
|
|
||||||
|
// Network and AP settings
|
||||||
|
bool enableMDNS_;
|
||||||
|
bool staticIPConfig_;
|
||||||
|
IPAddress localIP_;
|
||||||
|
IPAddress gatewayIP_;
|
||||||
|
IPAddress subnetMask_;
|
||||||
|
IPAddress dnsIP1_;
|
||||||
|
IPAddress dnsIP2_;
|
||||||
|
String hostname_;
|
||||||
|
String ssid_;
|
||||||
|
String password_;
|
||||||
|
bool bandwidth20_;
|
||||||
|
bool nosleep_;
|
||||||
|
uint8_t tx_power_;
|
||||||
|
String bssid_;
|
||||||
|
uint8_t phy_type_;
|
||||||
|
int8_t eth_power_;
|
||||||
|
uint8_t eth_phy_addr_;
|
||||||
|
uint8_t eth_clock_mode_;
|
||||||
|
|
||||||
|
// AP settings
|
||||||
|
uint8_t ap_provisionMode_;
|
||||||
|
String ap_ssid_;
|
||||||
|
String ap_password_;
|
||||||
|
uint8_t ap_channel_;
|
||||||
|
bool ap_ssid_hidden_;
|
||||||
|
uint8_t ap_max_clients_;
|
||||||
|
IPAddress ap_localIP_;
|
||||||
|
IPAddress ap_gatewayIP_;
|
||||||
|
IPAddress ap_subnetMask_;
|
||||||
|
|
||||||
|
// for the captive portal in AP mode
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
DNSServer * ap_dnsServer_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace emsesp
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "network.h"
|
||||||
#include "emsesp.h" // for send_raw_telegram() command
|
#include "emsesp.h" // for send_raw_telegram() command
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -485,7 +486,7 @@ void System::set_partition_install_date() {
|
|||||||
snprintf(c, sizeof(c), "d_%s", current_partition);
|
snprintf(c, sizeof(c), "d_%s", current_partition);
|
||||||
time_t d = EMSESP::nvs_.getULong(c, 0);
|
time_t d = EMSESP::nvs_.getULong(c, 0);
|
||||||
if (d < 1500000000L) {
|
if (d < 1500000000L) {
|
||||||
LOG_DEBUG("Setting the install date in partition %s", current_partition);
|
LOG_DEBUG("Setting the NTP install date in partition %s", current_partition);
|
||||||
auto t = time(nullptr) - uuid::get_uptime_sec();
|
auto t = time(nullptr) - uuid::get_uptime_sec();
|
||||||
EMSESP::nvs_.putULong(c, t);
|
EMSESP::nvs_.putULong(c, t);
|
||||||
}
|
}
|
||||||
@@ -586,15 +587,6 @@ void System::system_restart(const char * partitionname) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// saves all settings
|
|
||||||
void System::wifi_reconnect() {
|
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read(
|
|
||||||
[](NetworkSettings & networkSettings) { LOG_INFO("WiFi reconnecting to SSID '%s'...", networkSettings.ssid.c_str()); });
|
|
||||||
delay(500); // wait
|
|
||||||
EMSESP::webSettingsService.save(); // save local settings
|
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::syslog_init() {
|
void System::syslog_init() {
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
syslog_enabled_ = settings.syslog_enabled;
|
syslog_enabled_ = settings.syslog_enabled;
|
||||||
@@ -673,14 +665,9 @@ void System::store_settings(WebSettings & settings) {
|
|||||||
bool_dashboard_ = settings.bool_dashboard;
|
bool_dashboard_ = settings.bool_dashboard;
|
||||||
enum_format_ = settings.enum_format;
|
enum_format_ = settings.enum_format;
|
||||||
readonly_mode_ = settings.readonly_mode;
|
readonly_mode_ = settings.readonly_mode;
|
||||||
|
|
||||||
phy_type_ = settings.phy_type;
|
|
||||||
eth_power_ = settings.eth_power;
|
|
||||||
eth_phy_addr_ = settings.eth_phy_addr;
|
|
||||||
eth_clock_mode_ = settings.eth_clock_mode;
|
|
||||||
|
|
||||||
locale_ = settings.locale;
|
locale_ = settings.locale;
|
||||||
developer_mode_ = settings.developer_mode;
|
developer_mode_ = settings.developer_mode;
|
||||||
|
|
||||||
// start services
|
// start services
|
||||||
if (settings.modbus_enabled) {
|
if (settings.modbus_enabled) {
|
||||||
if (EMSESP::modbus_ == nullptr) {
|
if (EMSESP::modbus_ == nullptr) {
|
||||||
@@ -736,9 +723,10 @@ void System::start() {
|
|||||||
commands_init(); // console & api commands
|
commands_init(); // console & api commands
|
||||||
led_init(); // init LED
|
led_init(); // init LED
|
||||||
button_init(); // button
|
button_init(); // button
|
||||||
network_init(); // network
|
|
||||||
uart_init(); // start UART
|
last_system_check_ = 0; // force the LED to go from fast flash to pulse
|
||||||
syslog_init(); // start syslog
|
uart_init(); // start UART
|
||||||
|
syslog_init(); // start syslog
|
||||||
}
|
}
|
||||||
|
|
||||||
// button single click
|
// button single click
|
||||||
@@ -756,9 +744,10 @@ void System::button_OnClick(PButton & b) {
|
|||||||
// button double click
|
// button double click
|
||||||
void System::button_OnDblClick(PButton & b) {
|
void System::button_OnDblClick(PButton & b) {
|
||||||
LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
|
LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
// set AP mode to always so will join AP if wifi ssid fails to connect
|
// set AP mode to always so will join AP if wifi ssid fails to connect
|
||||||
EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) {
|
EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) {
|
||||||
apSettings.provisionMode = AP_MODE_ALWAYS;
|
apSettings.provisionMode = AP_MODE_DISCONNECTED;
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
// remove SSID from network settings
|
// remove SSID from network settings
|
||||||
@@ -766,7 +755,8 @@ void System::button_OnDblClick(PButton & b) {
|
|||||||
networkSettings.ssid = "";
|
networkSettings.ssid = "";
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
|
EMSESP::network_.reconnect(); // reconnect to the network
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// LED flash every 100ms
|
// LED flash every 100ms
|
||||||
@@ -904,7 +894,8 @@ bool System::loop() {
|
|||||||
// this is only done once when the connection is established
|
// this is only done once when the connection is established
|
||||||
void System::send_info_mqtt() {
|
void System::send_info_mqtt() {
|
||||||
static uint8_t _connection = 0;
|
static uint8_t _connection = 0;
|
||||||
uint8_t connection = (ethernet_connected() ? 1 : 0) + ((WiFi.status() == WL_CONNECTED) ? 2 : 0) + (ntp_connected_ ? 4 : 0) + (has_ipv6_ ? 8 : 0);
|
uint8_t connection = (EMSESP::network_.ethernet_connected() ? 1 : 0) + (EMSESP::network_.wifi_connected() ? 2 : 0) + (ntp_connected_ ? 4 : 0)
|
||||||
|
+ (EMSESP::network_.has_ipv6() ? 8 : 0);
|
||||||
// check if connection status has changed
|
// check if connection status has changed
|
||||||
if (!Mqtt::connected() || connection == _connection) {
|
if (!Mqtt::connected() || connection == _connection) {
|
||||||
return;
|
return;
|
||||||
@@ -924,7 +915,7 @@ void System::send_info_mqtt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (EMSESP::system_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
doc["network"] = "ethernet";
|
doc["network"] = "ethernet";
|
||||||
doc["hostname"] = ETH.getHostname();
|
doc["hostname"] = ETH.getHostname();
|
||||||
/*
|
/*
|
||||||
@@ -1003,11 +994,11 @@ void System::heartbeat_json(JsonObject output) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (!ethernet_connected_) {
|
if (!EMSESP::network_.ethernet_connected()) {
|
||||||
int8_t rssi = WiFi.RSSI();
|
int8_t rssi = WiFi.RSSI();
|
||||||
output["rssi"] = rssi;
|
output["rssi"] = rssi;
|
||||||
output["wifistrength"] = wifi_quality(rssi);
|
output["wifistrength"] = wifi_quality(rssi);
|
||||||
output["wifireconnects"] = EMSESP::esp32React.getWifiReconnects();
|
output["wifireconnects"] = EMSESP::network_.getWifiReconnects();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1023,49 +1014,6 @@ void System::send_heartbeat() {
|
|||||||
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializes network
|
|
||||||
void System::network_init() {
|
|
||||||
last_system_check_ = 0; // force the LED to go from fast flash to pulse
|
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
|
||||||
bool disableEth;
|
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { disableEth = settings.ssid.length() > 0; });
|
|
||||||
|
|
||||||
// no ethernet present or disabled
|
|
||||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE || disableEth) {
|
|
||||||
return;
|
|
||||||
} // no ethernet present
|
|
||||||
|
|
||||||
// configure Ethernet
|
|
||||||
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
|
||||||
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
|
||||||
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
|
||||||
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
|
||||||
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
|
||||||
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
|
||||||
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
|
||||||
// clock mode:
|
|
||||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
|
||||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
|
||||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
|
||||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
|
||||||
auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
|
||||||
|
|
||||||
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
|
||||||
if (eth_power_ != -1) {
|
|
||||||
pinMode(eth_power_, OUTPUT);
|
|
||||||
digitalWrite(eth_power_, LOW);
|
|
||||||
delay(500);
|
|
||||||
digitalWrite(eth_power_, HIGH);
|
|
||||||
}
|
|
||||||
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
|
|
||||||
if (eth_present_) {
|
|
||||||
// Push hostname to the ETH netif immediately after it's created
|
|
||||||
ETH.setHostname(hostname_.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// check health of system, done every 5 seconds
|
// check health of system, done every 5 seconds
|
||||||
void System::system_check() {
|
void System::system_check() {
|
||||||
uint32_t current_uptime = uuid::get_uptime();
|
uint32_t current_uptime = uuid::get_uptime();
|
||||||
@@ -1084,7 +1032,7 @@ void System::system_check() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// check if we have a valid network connection
|
// check if we have a valid network connection
|
||||||
if (!ethernet_connected() && (WiFi.status() != WL_CONNECTED)) {
|
if (!EMSESP::network_.network_connected()) {
|
||||||
healthcheck_ |= HEALTHCHECK_NO_NETWORK;
|
healthcheck_ |= HEALTHCHECK_NO_NETWORK;
|
||||||
} else {
|
} else {
|
||||||
healthcheck_ &= ~HEALTHCHECK_NO_NETWORK;
|
healthcheck_ &= ~HEALTHCHECK_NO_NETWORK;
|
||||||
@@ -1402,7 +1350,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show Ethernet if connected
|
// show Ethernet if connected
|
||||||
if (ethernet_connected_) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
shell.println();
|
shell.println();
|
||||||
shell.printfln(" Ethernet Status: connected");
|
shell.printfln(" Ethernet Status: connected");
|
||||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||||
@@ -1694,6 +1642,7 @@ bool System::check_upgrade() {
|
|||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
});
|
});
|
||||||
|
EMSESP::network_.reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// changes to application settings
|
// changes to application settings
|
||||||
@@ -2348,7 +2297,6 @@ std::string System::get_metrics_prometheus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += info_metric;
|
result += info_metric;
|
||||||
// TODO fix, as local_info_labels is always empty here
|
|
||||||
if (!local_info_labels.empty()) {
|
if (!local_info_labels.empty()) {
|
||||||
result += "{";
|
result += "{";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
@@ -2379,14 +2327,14 @@ String System::get_ip_or_hostname() {
|
|||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||||
if (settings.enableMDNS) {
|
if (settings.enableMDNS) {
|
||||||
if (EMSESP::system_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
result = ETH.getHostname();
|
result = ETH.getHostname();
|
||||||
} else if (WiFi.status() == WL_CONNECTED) {
|
} else if (WiFi.status() == WL_CONNECTED) {
|
||||||
result = WiFi.getHostname();
|
result = WiFi.getHostname();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no DNS, use the IP
|
// no DNS, use the IP
|
||||||
if (EMSESP::system_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
result = ETH.localIP().toString();
|
result = ETH.localIP().toString();
|
||||||
} else if (WiFi.status() == WL_CONNECTED) {
|
} else if (WiFi.status() == WL_CONNECTED) {
|
||||||
result = WiFi.localIP().toString();
|
result = WiFi.localIP().toString();
|
||||||
@@ -2470,7 +2418,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
|||||||
// Network Status
|
// Network Status
|
||||||
node = output["network"].to<JsonObject>();
|
node = output["network"].to<JsonObject>();
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (EMSESP::system_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
node["network"] = "Ethernet";
|
node["network"] = "Ethernet";
|
||||||
node["hostname"] = ETH.getHostname();
|
node["hostname"] = ETH.getHostname();
|
||||||
// node["MAC"] = ETH.macAddress();
|
// node["MAC"] = ETH.macAddress();
|
||||||
@@ -2484,7 +2432,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
|||||||
node["network"] = "WiFi";
|
node["network"] = "WiFi";
|
||||||
node["hostname"] = WiFi.getHostname();
|
node["hostname"] = WiFi.getHostname();
|
||||||
node["RSSI"] = WiFi.RSSI();
|
node["RSSI"] = WiFi.RSSI();
|
||||||
node["WIFIReconnects"] = EMSESP::esp32React.getWifiReconnects();
|
node["WIFIReconnects"] = EMSESP::network_.getWifiReconnects();
|
||||||
// node["MAC"] = WiFi.macAddress();
|
// node["MAC"] = WiFi.macAddress();
|
||||||
// node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
// node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
||||||
// node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
// node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ class System {
|
|||||||
static bool uploadFirmwareURL(const char * url = nullptr);
|
static bool uploadFirmwareURL(const char * url = nullptr);
|
||||||
|
|
||||||
void led_init();
|
void led_init();
|
||||||
void network_init();
|
|
||||||
void button_init();
|
void button_init();
|
||||||
void commands_init();
|
void commands_init();
|
||||||
void uart_init();
|
void uart_init();
|
||||||
@@ -260,33 +259,9 @@ class System {
|
|||||||
hostname_ = hostname;
|
hostname_ = hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ethernet_connected() {
|
|
||||||
return ethernet_connected_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ethernet_connected(bool b) {
|
|
||||||
ethernet_connected_ = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void has_ipv6(bool b) {
|
|
||||||
has_ipv6_ = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_ipv6() {
|
|
||||||
return has_ipv6_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ntp_connected(bool b);
|
void ntp_connected(bool b);
|
||||||
bool ntp_connected();
|
bool ntp_connected();
|
||||||
|
|
||||||
bool network_connected() {
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
return (ethernet_connected() || WiFi.isConnected());
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void fahrenheit(bool b) {
|
void fahrenheit(bool b) {
|
||||||
fahrenheit_ = b;
|
fahrenheit_ = b;
|
||||||
}
|
}
|
||||||
@@ -312,8 +287,6 @@ class System {
|
|||||||
void show_system(uuid::console::Shell & shell);
|
void show_system(uuid::console::Shell & shell);
|
||||||
void show_users(uuid::console::Shell & shell);
|
void show_users(uuid::console::Shell & shell);
|
||||||
|
|
||||||
void wifi_reconnect();
|
|
||||||
|
|
||||||
static std::string languages_string();
|
static std::string languages_string();
|
||||||
|
|
||||||
uint32_t FStotal() {
|
uint32_t FStotal() {
|
||||||
@@ -442,15 +415,11 @@ class System {
|
|||||||
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
||||||
uint32_t last_system_check_ = 0;
|
uint32_t last_system_check_ = 0;
|
||||||
|
|
||||||
bool upload_isrunning_ = false; // true if we're in the middle of a OTA firmware upload
|
bool upload_isrunning_ = false; // true if we're in the middle of a OTA firmware upload
|
||||||
bool ethernet_connected_ = false;
|
|
||||||
bool has_ipv6_ = false;
|
|
||||||
|
|
||||||
bool ntp_connected_ = false;
|
bool ntp_connected_ = false;
|
||||||
uint32_t ntp_last_check_ = 0;
|
uint32_t ntp_last_check_ = 0;
|
||||||
|
|
||||||
bool eth_present_ = false;
|
|
||||||
|
|
||||||
// EMS-ESP settings
|
// EMS-ESP settings
|
||||||
std::string hostname_;
|
std::string hostname_;
|
||||||
String locale_;
|
String locale_;
|
||||||
@@ -482,17 +451,10 @@ class System {
|
|||||||
uint8_t modbus_max_clients_;
|
uint8_t modbus_max_clients_;
|
||||||
uint32_t modbus_timeout_;
|
uint32_t modbus_timeout_;
|
||||||
bool developer_mode_;
|
bool developer_mode_;
|
||||||
|
uint32_t fstotal_;
|
||||||
// ethernet
|
uint32_t psram_;
|
||||||
uint8_t phy_type_;
|
uint32_t appused_;
|
||||||
int8_t eth_power_;
|
uint32_t appfree_;
|
||||||
uint8_t eth_phy_addr_;
|
|
||||||
uint8_t eth_clock_mode_;
|
|
||||||
|
|
||||||
uint32_t fstotal_;
|
|
||||||
uint32_t psram_;
|
|
||||||
uint32_t appused_;
|
|
||||||
uint32_t appfree_;
|
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
temperature_sensor_handle_t temperature_handle_ = NULL;
|
temperature_sensor_handle_t temperature_handle_ = NULL;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.0"
|
#define EMSESP_APP_VERSION "3.9.0-dev.1"
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
root["ap_status"] = EMSESP::esp32React.apStatus();
|
root["ap_status"] = EMSESP::network_.ap_connected();
|
||||||
|
|
||||||
if (EMSESP::system_.ethernet_connected()) {
|
if (EMSESP::network_.ethernet_connected()) {
|
||||||
root["network_status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
|
root["network_status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
|
||||||
root["wifi_rssi"] = 0;
|
root["wifi_rssi"] = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user