7#include "ustd_platform.h"
74 const long NET_CONFIG_VERSION = 1;
75 enum Netstate { NOTDEFINED, NOTCONFIGURED, SERVING, CONNECTINGAP, CONNECTED };
76 enum Netmode { OFF, AP, STATION, BOTH };
86 ustd::jsonfile config;
97 ustd::heartbeat statePublisher = 30000;
99 ustd::heartbeat connectionMonitor = 1000;
100 ustd::timeout connectTimeout = 15000;
101 unsigned int reconnectMaxRetries = 40;
102 bool bRebootOnContinuedFailure =
true;
107 bool scanning =
false;
110 ustd::sensorprocessor rssival = ustd::sensorprocessor(20, 1800, 2.0);
112 unsigned int connections;
119 Net(uint8_t signalLed = 0xff,
bool signalLogic =
false)
120 : signalLed(signalLed), signalLogic(signalLogic) {
141 oldState = NOTDEFINED;
142 curState = NOTCONFIGURED;
145 void begin(Scheduler *pScheduler, Netmode opmode = AP,
bool restartOnMultipleFailures =
true) {
164 initHardwareAddresses();
165 readNetConfig(opmode, restartOnMultipleFailures);
166 initScheduler(pScheduler);
170 void begin(Scheduler *pScheduler, String SSID, String password =
"",
171 String hostname =
"muwerk-${macls}", Netmode opmode = AP,
172 bool restartOnMultipleFailures =
true) {
209 if (opmode != Netmode::AP && opmode != Netmode::STATION) {
210 DBG(
"ERROR: Wrong operation mode specified on Net::begin");
214 initHardwareAddresses();
215 initNetConfig(SSID, password, hostname, opmode, restartOnMultipleFailures);
216 initScheduler(pScheduler);
221 void initScheduler(Scheduler *pScheduler) {
223 tID = pSched->add([
this]() { this->loop(); },
"net");
226 tID,
"net/network/get",
227 [
this](String topic, String msg, String originator) { this->publishState(); });
230 tID,
"net/network/control",
231 [
this](String topic, String msg, String originator) { this->control(msg); });
234 tID,
"net/networks/get",
235 [
this](String topic, String msg, String originator) { this->requestScan(msg); });
239 if (mode == Netmode::OFF) {
252 unsigned int conns = WiFi.softAPgetStationNum();
253 if (conns != connections) {
255 pSched->publish(
"net/connections", String(connections));
264 if (statePublisher.beat()) {
269 if (WiFi.status() == WL_CONNECTED) {
270 curState = CONNECTED;
271 DBG(
"Connected to WiFi with ip address " + WiFi.localIP().toString());
275 if (connectTimeout.test()) {
276 DBG(
"Timout connecting to WiFi " + WiFi.SSID());
277 if (bOnceConnected) {
278 if (bRebootOnContinuedFailure) {
281 if (deathCounter == 0) {
282 DBG(
"Final connection failure, restarting...");
283 if (bRebootOnContinuedFailure) {
287 DBG(
"Reconnecting...");
289 connectTimeout.reset();
291 DBG(
"Retrying to connect...");
292 if (initialCounter > 0) {
293 if (bRebootOnContinuedFailure) {
297 connectTimeout.reset();
298 curState = CONNECTINGAP;
300 DBG(
"Final connect failure, configuration invalid?");
301 curState = NOTCONFIGURED;
302 if (bRebootOnContinuedFailure) {
310 bOnceConnected =
true;
311 deathCounter = reconnectMaxRetries;
312 if (connectionMonitor.beat()) {
313 if (WiFi.status() == WL_CONNECTED) {
314 long rssi = WiFi.RSSI();
315 if (rssival.filter(&rssi)) {
316 pSched->publish(
"net/rssi", String(rssi));
320 curState = CONNECTINGAP;
321 connectTimeout.reset();
326 if (statePublisher.beat()) {
333 if (curState != oldState) {
335 DBGP(getStringFromState(oldState));
337 DBGP(getStringFromState(curState));
338 if (curState == CONNECTED) {
346 setLed(curState == CONNECTINGAP);
353 processScan(WiFi.scanComplete());
357 void control(String msg) {
359 if (msg ==
"on" || msg ==
"start") {
360 if (curState == Netstate::NOTDEFINED || curState == Netstate::NOTCONFIGURED) {
363 }
else if (msg ==
"off" || msg ==
"stop") {
365 }
else if (msg ==
"restart") {
367 pSched->publish(
"net/network/control",
"start");
371 void initNetConfig(String SSID, String password, String hostname, Netmode opmode,
374 defaultMode = opmode;
375 defaultReboot = restart;
377 String prefix =
"net/" + getStringFromMode(opmode) +
"/";
381 deviceID = macAddress;
382 deviceID.replace(
":",
"");
385 config.clear(
false,
true);
386 config.writeBool(
"net/hardcoded",
true);
387 config.writeString(
"net/hostname", hostname);
388 config.writeString(
"net/mode", getStringFromMode(opmode));
389 config.writeString(
"net/deviceid", deviceID);
390 config.writeString(prefix +
"SSID", SSID);
391 config.writeString(prefix +
"password", password);
392 config.writeBool(
"net/station/rebootOnFailure", restart);
395 void readNetConfig(Netmode opmode,
bool restart) {
397 defaultMode = opmode;
398 defaultReboot = restart;
401 long version = config.readLong(
"net/version", 0);
403 if (config.exists(
"net/SSID")) {
405 migrateNetConfigFrom(config, version);
408 }
else if (version < NET_CONFIG_VERSION) {
410 migrateNetConfigFrom(config, version);
415 mode = getModeFromString(config.readString(
"net/mode"), defaultMode);
416 deviceID = config.readString(
"net/deviceid");
417 if (deviceID ==
"") {
419 deviceID = macAddress;
420 deviceID.replace(
":",
"");
421 config.writeString(
"net/deviceid", deviceID);
425 void cleanupNetConfig() {
427 if (!config.readBool(
"net/hardcoded",
false)) {
429 DBG(
"Freeing configuration...");
434 void migrateNetConfigFrom(ustd::jsonfile &sf,
long version) {
437 ustd::jsonfile nf(
false,
true);
438 nf.writeLong(
"net/version", NET_CONFIG_VERSION);
439 nf.writeString(
"net/mode",
"station");
440 nf.writeString(
"net/hostname", sf.readString(
"net/hostname"));
441 nf.writeString(
"net/station/SSID", sf.readString(
"net/SSID"));
442 nf.writeString(
"net/station/password", sf.readString(
"net/password"));
443 ustd::array<JSONVar> services;
444 if (sf.readJsonVarArray(
"net/services", services)) {
445 for (
unsigned int i = 0; i < services.length(); i++) {
446 DBG(
"Processing service " + String(i));
447 if (JSON.typeof(services[i]) ==
"object") {
448 if (JSON.typeof(services[i][
"timeserver"]) ==
"string") {
449 String ntphost = (
const char *)(services[i][
"timeserver"]);
450 DBG(
"Found timeserver host entry: " + ntphost);
452 host[0] = (
const char *)ntphost.c_str();
453 nf.writeJsonVar(
"net/services/ntp/host", host);
454 }
else if (JSON.typeof(services[i][
"dstrules"]) ==
"string") {
455 String dstrules = (
const char *)(services[i][
"dstrules"]);
456 DBG(
"Found timeserver dstrules entry: " + dstrules);
457 nf.writeString(
"net/services/ntp/dstrules", dstrules);
458 }
else if (JSON.typeof(services[i][
"mqttserver"]) ==
"string") {
459 String mqttserver = (
const char *)(services[i][
"mqttserver"]);
460 DBG(
"Found mqtt host entry: " + mqttserver);
461 ustd::jsonfile mqtt(
false,
true);
462 mqtt.writeString(
"mqtt/host", mqttserver);
463 mqtt.writeBool(
"mqtt/alwaysRetained",
true);
467 DBG(
"Wrong service entry");
480 void startServices() {
481 mode = getModeFromString(config.readString(
"net/mode"), defaultMode);
485 DBG(
"Network is disabled");
486 curState = Netstate::NOTCONFIGURED;
491 curState = Netstate::SERVING;
494 case Netmode::STATION:
495 if (startSTATION()) {
496 curState = Netstate::CONNECTINGAP;
500 if (startSTATION()) {
501 curState = Netstate::CONNECTINGAP;
506 if (curState == Netstate::NOTCONFIGURED) {
507 DBG(
"Failed to start network services");
512 void stopServices() {
515 DBG(
"Network is disabled");
520 WiFi.softAPdisconnect(
false);
522 case Netmode::STATION:
523 DBG(
"Disconnecting from WiFi");
524 WiFi.disconnect(
false);
527 DBG(
"Disconnecting from WiFi and stopping AP");
528 WiFi.disconnect(
false);
529 WiFi.softAPdisconnect(
false);
534 curState = Netstate::NOTCONFIGURED;
535 wifiSetMode(Netmode::OFF);
541 String hostname = replaceVars(config.readString(
"net/hostname",
"muwerk-${macls}"));
542 wifiAPSetHostname(hostname);
545 String address = config.readString(
"net/ap/address");
546 String netmask = config.readString(
"net/ap/netmask");
547 String gateway = config.readString(
"net/ap/gateway");
548 if (address.length() & netmask.length()) {
549 wifiSoftAPConfig(address, gateway, netmask);
553 String SSID = replaceVars(config.readString(
"net/ap/SSID",
"muwerk-${macls}"));
554 if (!SSID.length()) {
555 SSID = replaceVars(
"muwerk-${macls}");
557 String password = config.readString(
"net/ap/password");
558 unsigned int channel = config.readLong(
"net/ap/channel", 1, 13, 1);
559 bool hidden = config.readBool(
"net/ap/hidden",
false);
560 unsigned int maxConnections = config.readLong(
"net/ap/maxConnections", 1, 8, 4);
563 DBG(
"Starting AP with SSID " + SSID +
"...");
564 if (wifiSoftAP(SSID, password, channel, hidden, maxConnections)) {
565 wifiAPSetHostname(hostname);
574 bool startSTATION() {
576 String hostname = replaceVars(config.readString(
"net/hostname"));
577 String SSID = config.readString(
"net/station/SSID");
578 String password = config.readString(
"net/station/password");
581 ustd::array<String> dns;
582 String address = config.readString(
"net/station/address");
583 String netmask = config.readString(
"net/station/netmask");
584 String gateway = config.readString(
"net/station/gateway");
585 config.readStringArray(
"net/services/dns/host", dns);
588 connectTimeout = config.readLong(
"net/station/connectTimeout", 3, 3600, 15) * 1000;
589 reconnectMaxRetries = config.readLong(
"net/station/maxRetries", 1, 1000000000, 40);
590 bRebootOnContinuedFailure = config.readBool(
"net/station/rebootOnFailure", defaultReboot);
592 DBG(
"Connecting WiFi " + SSID);
593 wifiSetHostname(hostname);
594 if (wifiBegin(SSID, password)) {
595 deathCounter = reconnectMaxRetries;
596 initialCounter = reconnectMaxRetries;
597 bOnceConnected =
false;
598 curState = CONNECTINGAP;
599 connectTimeout.reset();
600 if (!wifiConfig(address, gateway, netmask, dns)) {
601 DBG(
"Failed to set network configuration");
603 wifiSetHostname(hostname);
613 ustd::array<String> ntpHosts;
616 void configureTime() {
617 ntpDstRules = config.readString(
"net/services/ntp/dstrules");
618 config.readStringArray(
"net/services/ntp/host", ntpHosts);
620 if (ntpDstRules.length() && ntpHosts.length()) {
622 configTzTime(ntpDstRules.c_str(), ntpHosts[0].c_str(),
623 ntpHosts.length() > 1 ? ntpHosts[1].c_str() :
nullptr,
624 ntpHosts.length() > 2 ? ntpHosts[2].c_str() :
nullptr);
625 }
else if (ntpHosts.length()) {
627 configTime(0, 0, ntpHosts[0].c_str(),
628 ntpHosts.length() > 1 ? ntpHosts[1].c_str() :
nullptr,
629 ntpHosts.length() > 2 ? ntpHosts[2].c_str() :
nullptr);
630 }
else if (ntpDstRules.length()) {
632 setenv(
"TZ", ntpDstRules.c_str(), 3);
638 void publishState() {
641 net[
"mode"] = getStringFromMode(mode);
642 net[
"mac"] = macAddress;
646 net[
"state"] =
"notconfigured";
649 net[
"state"] =
"connectingap";
650 net[
"SSID"] = WiFi.SSID();
653 net[
"state"] =
"connected";
654 net[
"SSID"] = WiFi.SSID();
655 net[
"hostname"] = wifiGetHostname();
656 net[
"ip"] = WiFi.localIP().toString();
659 net[
"state"] =
"serving";
660 net[
"hostname"] = wifiAPGetHostname();
663 net[
"state"] =
"undefined";
666 if (curState != NOTCONFIGURED && (mode == Netmode::AP || mode == Netmode::BOTH)) {
667 net[
"ap"][
"mac"] = WiFi.softAPmacAddress();
668 net[
"ap"][
"SSID"] = replaceVars(config.readString(
"net/ap/SSID",
"muwerk-${macls}"));
669 net[
"ap"][
"ip"] = WiFi.softAPIP().toString();
670 net[
"ap"][
"connections"] = (int)connections;
672 String json = JSON.stringify(net);
673 pSched->publish(
"net/network", json);
676 void requestScan(String scantype =
"async") {
679 for (String arg = shift(scantype,
','); arg.length(); arg = shift(scantype,
',')) {
683 }
else if (arg ==
"async") {
685 }
else if (arg ==
"hidden") {
689 processScan(WiFi.scanNetworks(async, hidden));
692 void processScan(
int result) {
694 case WIFI_SCAN_RUNNING:
696 DBG(
"WiFi scan running...");
700 case WIFI_SCAN_FAILED:
701 DBG(
"WiFi scan FAILED.");
705 DBG(
"WiFi scan succeeded: No network found.");
710 DBGF(
"WiFi scan succeeded: %u networks found.\r\n", result);
716 void publishScan(
int result) {
719 res[
"result"] = result < 0 ?
"error" :
"ok";
720 res[
"networks"] = JSON.parse(
"[]");
722 for (
int i = 0; i < result; i++) {
725 network[
"ssid"] = WiFi.SSID(i);
726 network[
"rssi"] = WiFi.RSSI(i);
727 network[
"channel"] = WiFi.channel(i);
728 network[
"encryption"] = getStringFromEncryption(WiFi.encryptionType(i));
729 network[
"bssid"] = WiFi.BSSIDstr(i);
731 network[
"hidden"] = WiFi.isHidden(i);
733 res[
"networks"][i] = network;
735 pSched->publish(
"net/networks", JSON.stringify(res));
739 if (signalLed != 0xff) {
740 pinMode(signalLed, OUTPUT);
745 void setLed(
bool on) {
746 if (signalLed != 0xff) {
748 digitalWrite(signalLed, on ? HIGH : LOW);
750 digitalWrite(signalLed, on ? LOW : HIGH);
755 String replaceVars(String val) {
756 String hexAddress = macAddress;
757 hexAddress.replace(
":",
"");
758 val.replace(
"${mac}", hexAddress);
759 val.replace(
"${macls}", hexAddress.substring(6));
760 val.replace(
"${macfs}", hexAddress.substring(0, 5));
764 void initHardwareAddresses() {
765 WiFiMode_t currentMode = WiFi.getMode();
766 WiFi.mode(WIFI_AP_STA);
767 apmAddress = WiFi.softAPmacAddress();
768 macAddress = WiFi.macAddress();
769 WiFi.mode(currentMode);
772 Netmode getModeFromString(String val, Netmode defVal = AP) {
776 }
else if (val ==
"station") {
778 }
else if (val ==
"both") {
785 const char *getStringFromEncryption(
int encType) {
787#if !defined(__ESP32__)
808 case WIFI_AUTH_WPA_PSK:
810 case WIFI_AUTH_WPA2_PSK:
812 case WIFI_AUTH_WPA_WPA2_PSK:
813 return "WPA_WPA2_PSK";
814 case WIFI_AUTH_WPA2_ENTERPRISE:
815 return "WPA2_ENTERPRISE";
822 String getStringFromMode(Netmode val) {
829 case Netmode::STATION:
836 const char *getStringFromState(Netstate val) {
839 case Netstate::NOTDEFINED:
841 case Netstate::NOTCONFIGURED:
842 return "NOTCONFIGURED";
843 case Netstate::SERVING:
845 case Netstate::CONNECTINGAP:
846 return "CONNECTINGAP";
847 case Netstate::CONNECTED:
854 static String wifiGetHostname() {
855#if defined(__ESP32__)
856 return WiFi.getHostname();
858 return WiFi.hostname();
862 void wifiSetHostname(String &hostname) {
863 if (!hostname.length()) {
864 hostname = replaceVars(
"muwerk-${macls}");
866#if defined(__ESP32__)
867 WiFi.setHostname(hostname.c_str());
869 WiFi.hostname(hostname.c_str());
873#if !defined(__ESP32__)
876 static String esp8266APhostname;
879 static String wifiAPGetHostname() {
880#if defined(__ESP32__)
881 return WiFi.softAPgetHostname();
883 return Net::esp8266APhostname;
887 void wifiAPSetHostname(String &hostname) {
888 if (!hostname.length()) {
889 hostname = replaceVars(
"muwerk-${macls}");
891#if defined(__ESP32__)
892 WiFi.softAPsetHostname(hostname.c_str());
894 Net::esp8266APhostname = hostname;
898 static bool wifiSoftAP(String ssid, String passphrase,
unsigned int channel,
bool hidden,
899 unsigned int max_connection) {
900#if defined(__ESP32__)
901 return WiFi.softAP(ssid.c_str(), passphrase.c_str(), channel, hidden, max_connection);
903 return WiFi.softAP(ssid, passphrase, channel, hidden, max_connection);
907 static bool wifiSoftAPConfig(String &address, String &gateway, String &netmask) {
911 addr.fromString(address);
912 mask.fromString(netmask);
913 if (gateway.length()) {
914 gate.fromString(gateway);
916 return WiFi.softAPConfig(addr, gate, mask);
919 static bool wifiConfig(String &address, String &gateway, String &netmask,
920 ustd::array<String> &dns) {
926 if (address.length() && netmask.length()) {
927 DBG(
"Setting static ip: " + address +
" " + netmask);
928 addr.fromString(address);
929 mask.fromString(netmask);
931 if (gateway.length()) {
932 DBG(
"Setting static gateway: " + gateway);
933 gate.fromString(gateway);
935 if (dns.length() > 0) {
936 DBG(
"Setting dns server 1: " + String(dns[0]));
937 dns1.fromString(dns[0]);
939 if (dns.length() > 1) {
940 DBG(
"Setting dns server 2: " + String(dns[1]));
941 dns2.fromString(dns[1]);
943 return WiFi.config(addr, gate, mask, dns1, dns2);
946 static bool wifiBegin(String &ssid, String &passphrase) {
947#if defined(__ESP32__)
948 return WiFi.begin(ssid.c_str(), passphrase.c_str());
950 return WiFi.begin(ssid, passphrase);
954 static void wifiSetMode(Netmode val) {
963 case Netmode::STATION:
967 WiFi.mode(WIFI_AP_STA);
973#if !defined(__ESP32__)
976String Net::esp8266APhostname =
"";
munet, the muwerk network class for WiFi and NTP
Definition: net.h:72
void begin(Scheduler *pScheduler, Netmode opmode=AP, bool restartOnMultipleFailures=true)
Definition: net.h:145
void begin(Scheduler *pScheduler, String SSID, String password="", String hostname="muwerk-${macls}", Netmode opmode=AP, bool restartOnMultipleFailures=true)
Definition: net.h:170
Net(uint8_t signalLed=0xff, bool signalLogic=false)
Definition: net.h:119
The muwerk namespace.
Definition: mqtt.h:21