Files
EMS-ESP32/src/network.cpp
2020-06-03 17:28:07 +02:00

393 lines
12 KiB
C++

/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* 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/>.
*/
// code by nomis - https://github.com/nomis
#include "network.h"
MAKE_PSTR(logger_name, "network")
namespace emsesp {
uuid::log::Logger Network::logger_{F_(logger_name), uuid::log::Facility::KERN};
void Network::start() {
#ifndef EMSESP_STANDALONE
// 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);
}
WiFi.persistent(false);
WiFi.disconnect(true);
WiFi.setAutoReconnect(false);
WiFi.mode(WIFI_STA);
#endif
#if defined(ESP8266)
sta_mode_connected_ = WiFi.onStationModeConnected(std::bind(&Network::sta_mode_connected, this, _1));
sta_mode_disconnected_ = WiFi.onStationModeDisconnected(std::bind(&Network::sta_mode_disconnected, this, _1));
sta_mode_got_ip_ = WiFi.onStationModeGotIP(std::bind(&Network::sta_mode_got_ip, this, _1));
WiFi.setSleepMode(WIFI_NONE_SLEEP); // added to possibly fix wifi dropouts in arduino core 2.5.0
// ref: https://github.com/esp8266/Arduino/issues/6471
// ref: https://github.com/esp8266/Arduino/issues/6366
// high tx power causing weird behavior, slightly lowering from 20.5 to 20.0 may help stability
// WiFi.setOutputPower(20.0); // in DBM
connect(); // connect to WiFi
ota_setup(); // initialize OTA
#elif defined(ESP32)
WiFi.mode(WIFI_MODE_MAX);
WiFi.mode(WIFI_MODE_NULL);
WiFi.onEvent(std::bind(&Network::sta_mode_connected, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
WiFi.onEvent(std::bind(&Network::sta_mode_disconnected, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.onEvent(std::bind(&Network::sta_mode_got_ip, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
WiFi.onEvent(std::bind(&Network::sta_mode_start, this, _1, _2), WiFiEvent_t::SYSTEM_EVENT_STA_START);
connect(); // connect to WiFi
ota_setup(); // initialize OTA
#endif
}
#if defined(ESP32)
// set the ESP32 Wifi hostname. Didn't work during setup and I didn't want to use a delay().
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino#L79
void Network::sta_mode_start(WiFiEvent_t event, WiFiEventInfo_t info) {
WiFi.setHostname(Settings().hostname().c_str());
}
#endif
#if defined(ESP8266)
void Network::sta_mode_connected(const WiFiEventStationModeConnected & event) {
LOG_INFO(F("Connected to %s (%02X:%02X:%02X:%02X:%02X:%02X) on channel %u"),
event.ssid.c_str(),
event.bssid[0],
event.bssid[1],
event.bssid[2],
event.bssid[3],
event.bssid[4],
event.bssid[5],
event.channel);
// turn off safe mode
System::save_safe_mode(false);
}
#elif defined(ESP32)
void Network::sta_mode_connected(WiFiEvent_t event, WiFiEventInfo_t info) {
LOG_INFO(F("Connected to %s (%02X:%02X:%02X:%02X:%02X:%02X) on channel %u"),
info.connected.ssid,
info.sta_connected.mac[0],
info.sta_connected.mac[1],
info.sta_connected.mac[2],
info.sta_connected.mac[3],
info.sta_connected.mac[4],
info.sta_connected.mac[5],
info.connected.channel);
// turn off safe mode
System::save_safe_mode(false);
}
#endif
#if defined(ESP8266)
void Network::sta_mode_disconnected(const WiFiEventStationModeDisconnected & event) {
// disconnect if failed to connect a few times, unless already in safe mode!
if (event.reason == 201) {
if (++disconnect_count_ == 3) {
if (System::safe_mode()) {
LOG_ERROR(F("Failed to connect to WiFi %s after %d attempts"), event.ssid.c_str(), disconnect_count_ - 1);
disconnect_count_ = 0;
} else {
LOG_ERROR(F("Failed to connect to WiFi. Rebooting into Safe mode"));
System::restart(true); // set safe mode and restart
}
}
}
}
#elif defined(ESP32)
void Network::sta_mode_disconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
// LOG_ERROR(F("Failed to connect to WiFi %s, reason code %d"), info.disconnected.ssid, info.disconnected.reason);
}
#endif
#if defined(ESP8266)
void Network::sta_mode_got_ip(const WiFiEventStationModeGotIP & event) {
LOG_INFO(F("Obtained IPv4 address %s/%s and gateway %s"),
uuid::printable_to_string(event.ip).c_str(),
uuid::printable_to_string(event.mask).c_str(),
uuid::printable_to_string(event.gw).c_str());
}
#elif defined(ESP32)
void Network::sta_mode_got_ip(WiFiEvent_t event, WiFiEventInfo_t info) {
LOG_INFO(F("Obtained IPv4 address %s/%s and gateway %s"),
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.ip.addr)).c_str(),
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.netmask.addr)).c_str(),
uuid::printable_to_string(IPAddress(info.got_ip.ip_info.gw.addr)).c_str());
}
#endif
void Network::connect() {
Settings settings;
if (settings.wifi_ssid().empty()) {
return;
}
#ifndef EMSESP_STANDALONE
if (!settings.hostname().empty()) {
#if defined(ESP8266)
// experiment with fixed IP
// IPAddress ip(10, 10, 10, 140);
// IPAddress gateway(10, 10, 10, 1);
// IPAddress subnet(255, 255, 255, 0);
// WiFi.config(ip, gateway, subnet);
WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY);
WiFi.hostname(settings.hostname().c_str());
#elif defined(ESP32)
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
#endif
}
LOG_INFO(F("Connecting to wifi ssid %s..."), settings.wifi_ssid().c_str());
WiFi.begin(settings.wifi_ssid().c_str(), settings.wifi_password().c_str());
#endif
}
void Network::reconnect() {
disconnect();
connect();
}
void Network::disconnect() {
#if defined(ESP8266)
WiFi.disconnect();
#elif defined(ESP32)
WiFi.disconnect(true);
#endif
}
// OTA Setup
void Network::ota_setup() {
#ifndef EMSESP_STANDALONE
if (ota_) {
delete ota_;
ota_ = nullptr;
}
ota_ = new ArduinoOTAClass;
Settings settings;
ota_->setPort(OTA_PORT);
if (settings.hostname().empty()) {
ota_->setHostname(EMSESP_DEFAULT_HOSTNAME);
} else {
ota_->setHostname(settings.hostname().c_str());
}
ota_->setPassword(settings.admin_password().c_str());
ota_->onStart([this]() {
LOG_DEBUG(F("OTA starting (send type %d)..."), ota_->getCommand());
// turn off UART stuff to stop interference on an ESP8266 only
#if defined(ESP8266)
EMSuart::stop(); // UART stop
#endif
in_ota_ = true; // set flag so all other services stop
});
ota_->onEnd([this]() { LOG_DEBUG(F("OTA done, automatically restarting")); });
ota_->onProgress([this](unsigned int progress, unsigned int total) {
/*
static unsigned int _progOld;
unsigned int _prog = (progress / (total / 100));
if (_prog != _progOld) {
LOG_DEBUG(F("[OTA] Progress: %u%%"), _prog);
_progOld = _prog;
}
*/
});
ota_->onError([this](ota_error_t error) {
if (error == OTA_AUTH_ERROR) {
LOG_ERROR(F("[OTA] Auth Failed"));
} else if (error == OTA_BEGIN_ERROR) {
LOG_ERROR(F("[OTA] Begin Failed"));
} else if (error == OTA_CONNECT_ERROR) {
LOG_ERROR(F("[OTA] Connect Failed"));
} else if (error == OTA_RECEIVE_ERROR) {
LOG_ERROR(F("[OTA] Receive Failed"));
} else if (error == OTA_END_ERROR) {
LOG_ERROR(F("[OTA] End Failed"));
} else {
LOG_ERROR(F("[OTA] Error %d"), error);
};
});
// start ota service
ota_->begin();
LOG_INFO(F("Listening to firmware updates on %s.local:%u"), ota_->getHostname().c_str(), OTA_PORT);
#endif
}
// loop - mainly calls OTA
bool Network::loop() {
#ifndef EMSESP_STANDALONE
if (ota_) {
ota_->handle();
}
#endif
return !in_ota_;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void Network::scan(uuid::console::Shell & shell) {
#ifndef EMSESP_STANDALONE
int8_t ret = WiFi.scanNetworks(true);
if (ret == WIFI_SCAN_RUNNING) {
shell.println(F("Scanning for WiFi networks..."));
shell.block_with([](uuid::console::Shell & shell, bool stop) -> bool {
int8_t ret = WiFi.scanComplete();
if (ret == WIFI_SCAN_RUNNING) {
return stop;
} else if (ret == WIFI_SCAN_FAILED || ret < 0) {
shell.println(F("WiFi scan failed"));
return true;
} else {
shell.printfln(F("Found %u networks:"), ret);
shell.println();
for (uint8_t i = 0; i < (uint8_t)ret; i++) {
shell.printfln(F(" %s (channel %u at %d dBm) %s"), WiFi.SSID(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), WiFi.BSSIDstr(i).c_str());
}
shell.println();
WiFi.scanDelete();
return true;
}
});
} else {
shell.println(F("WiFi scan failed"));
}
#endif
}
void Network::show_network(uuid::console::Shell & shell) {
#ifndef EMSESP_STANDALONE
switch (WiFi.status()) {
case WL_IDLE_STATUS:
shell.printfln(F("WiFi: idle"));
break;
case WL_NO_SSID_AVAIL:
shell.printfln(F("WiFi: network not found"));
break;
case WL_SCAN_COMPLETED:
shell.printfln(F("WiFi: network scan complete"));
break;
case WL_CONNECTED: {
shell.printfln(F("WiFi: connected"));
shell.println();
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality());
shell.println();
shell.printfln(F("MAC address: %s"), WiFi.macAddress().c_str());
#if defined(ESP8266)
shell.printfln(F("Hostname: %s"), WiFi.hostname().c_str());
#elif defined(ESP32)
shell.printfln(F("Hostname: %s"), WiFi.getHostname());
#endif
shell.println();
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
} break;
case WL_CONNECT_FAILED:
shell.printfln(F("WiFi: connection failed"));
break;
case WL_CONNECTION_LOST:
shell.printfln(F("WiFi: connection lost"));
break;
case WL_DISCONNECTED:
shell.printfln(F("WiFi: disconnected"));
break;
case WL_NO_SHIELD:
default:
shell.printfln(F("WiFi: unknown"));
break;
}
shell.println();
#endif
}
#pragma GCC diagnostic pop
// Return the quality (Received Signal Strength Indicator) of the WiFi network as a %. Or -1 if disconnected.
// High quality: 90% ~= -55dBm
// Medium quality: 50% ~= -75dBm
// Low quality: 30% ~= -85dBm
// Unusable quality: 8% ~= -96dBm
int8_t Network::wifi_quality() {
#ifndef EMSESP_STANDALONE
if (WiFi.status() != WL_CONNECTED) {
return -1;
}
int dBm = WiFi.RSSI();
#else
int8_t dBm = -70;
#endif
if (dBm <= -100) {
return 0;
}
if (dBm >= -50) {
return 100;
}
return 2 * (dBm + 100);
}
} // namespace emsesp