diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4d3bbe9..33506d2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,6 +38,7 @@ firmware-build: - pio run --environment basic - pio run --environment mesh - pio run --environment meshMqttBridge + - pio run --environment standalone artifacts: paths: - .pioenvs/*/firmware.* diff --git a/data/config.json b/data/config.json index c7e58b1..ead392c 100644 --- a/data/config.json +++ b/data/config.json @@ -4,7 +4,10 @@ "meshPort": 5555, "meshSSID": "whateverYouLike", "meshPassword": "somethingSneaky", + "apSSID": "MyAP", + "apPassword": "myApPwd", "stationSSID": "tErAx1d", "stationPassword": "ramalamadingdong", - "hostname": "dbuggy" + "hostname": "dbuggy", + "connectTimeout": 10000 } \ No newline at end of file diff --git a/data/example.config.json b/data/example.config.json index e20959f..91562a2 100644 --- a/data/example.config.json +++ b/data/example.config.json @@ -6,5 +6,6 @@ "meshPassword": "th3r31sn0sp00n", "stationSSID": "MyAP", "stationPassword": "myApPassword", - "hostname": "mesh-node" + "hostname": "mesh-node", + "connectTimeout": 10000 } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 7a26082..db1c121 100644 --- a/platformio.ini +++ b/platformio.ini @@ -70,4 +70,16 @@ monitor_baud = ${common.monitor_baud} framework = ${common.framework} lib_deps = ${common.lib_deps} painlessMesh - PubSubClient \ No newline at end of file + PubSubClient + + +[env:standalone] +src_filter = +<*> - + +platform = ${common.platform} +board = ${common.board} +upload_speed = ${common.upload_speed} +monitor_baud = ${common.monitor_baud} +framework = ${common.framework} +lib_deps = ${common.lib_deps} + ArduinoOTA + ESP Async WebServer \ No newline at end of file diff --git a/src/MeshNet.cpp b/src/MeshNet.cpp index 2160e6f..aa28adf 100644 --- a/src/MeshNet.cpp +++ b/src/MeshNet.cpp @@ -29,13 +29,14 @@ Network* MeshNet::init(){ return this; } -Network* MeshNet::connectStation(int doConnect) { +int MeshNet::connectStation(int doConnect) { if(doConnect){ Serial.println("connect station"); mesh.stationManual(config.stationSSID, config.stationPassword); mesh.setHostname(config.hostname.c_str()); + return 1; } - return this; + return 0; } void MeshNet::sendTo(uint32_t target, String msg){ mesh.sendSingle(target, msg); diff --git a/src/MeshNet.h b/src/MeshNet.h index 6996b1c..6ff9463 100644 --- a/src/MeshNet.h +++ b/src/MeshNet.h @@ -22,7 +22,7 @@ class MeshNet : public Network { MeshSprocketConfig config; MeshNet(MeshConfig cfg); Network* init(); - Network* connectStation(int); + int connectStation(int); void configure(MeshSprocketConfig cfg); void update(); void newConnectionCallback(uint32_t nodeId); diff --git a/src/Network.h b/src/Network.h index d559959..62bbec9 100644 --- a/src/Network.h +++ b/src/Network.h @@ -12,13 +12,13 @@ class Network { Scheduler* scheduler; virtual Network* init() { return this; }; virtual Network* init(Scheduler* s) { scheduler = s; return init(); }; - virtual Network* connect() { return this; }; - virtual Network* connectStation() { return this; }; + virtual int connect() { return 0; }; + virtual int connectStation() { return 0; }; virtual int isConnected(){ return 0; }; virtual void update() {}; virtual void broadcast(String msg){}; virtual void sendTo(uint32_t target, String msg) {}; - virtual void onReceive(std::function); + virtual void onReceive(std::function) {}; Network* setScheduler(Scheduler* s) { scheduler = s; return this; diff --git a/src/Sprocket.h b/src/Sprocket.h index d13b4b7..94d39a8 100644 --- a/src/Sprocket.h +++ b/src/Sprocket.h @@ -2,6 +2,7 @@ #define __SPROCKET_H__ #include +//#include #include #include #include "FS.h" diff --git a/src/WiFiNet.cpp b/src/WiFiNet.cpp new file mode 100644 index 0000000..039fb43 --- /dev/null +++ b/src/WiFiNet.cpp @@ -0,0 +1,77 @@ +#include "WiFiNet.h" + +WiFiNet::WiFiNet( + int stationMode, + const char* stationSSID, + const char* stationPassword, + const char* apSSID, + const char* apPassword, + const char* hostname, + int connectTimeout){ + config.stationMode = stationMode; + config.stationSSID = String(stationSSID); + config.stationPassword = String(stationPassword); + config.apSSID = String(apSSID); + config.apPassword = String(apPassword); + config.hostname = String(hostname); + config.connectTimeout = connectTimeout; +} + +Network* WiFiNet::init() { + config.fromFile("/config.json"); + return this; +} +int WiFiNet::connect(){ + if(config.valid){ + WiFi.hostname(config.hostname); + Serial.println("Hostname: " + config.hostname); + if(!connectStation()) { + createAccessPoint(); + } + startDNS(); + } + return 1; +} + +int WiFiNet::connectStation(){ + if(config.stationMode == 0) return 0; + + int wifiConnectStart = millis(); + WiFi.mode(WIFI_STA); + WiFi.begin(config.stationSSID.c_str(), config.stationPassword.c_str()); + Serial.println("connect to " + config.stationSSID); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + if(millis() - wifiConnectStart >= (uint)config.connectTimeout) { + Serial.println("wifi connect timeout"); + return 0; + } + } + Serial.println("IP address: " + WiFi.localIP().toString()); + Serial.println(WiFi.localIP().toString()); + return 1; +} + +int WiFiNet::createAccessPoint(){ + Serial.println("Starting SoftAP: " + String(config.apSSID)); + WiFi.disconnect(); + WiFi.mode(WIFI_AP); + WiFi.softAP(config.apSSID.c_str(), config.apPassword.c_str()); + Serial.println("SoftAP started! IP address: "); + Serial.println(WiFi.softAPIP().toString()); + return 1; +} + +// TODO make user configurable services +int WiFiNet::startDNS() { + if (!MDNS.begin(config.hostname.c_str())) { + Serial.println("Error setting up MDNS responder!"); + return 0; + } else { + Serial.println("mDNS responder started"); + MDNS.addService("http", "tcp", 80); + } + return 1; +} \ No newline at end of file diff --git a/src/WiFiNet.h b/src/WiFiNet.h new file mode 100644 index 0000000..ffe23e7 --- /dev/null +++ b/src/WiFiNet.h @@ -0,0 +1,75 @@ +#ifndef __WIFI_NET__ +#define __WIFI_NET__ + +#include +#include + +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif // ESP32 + +#include "Network.h" + +#include +#include +#include "JsonStruct.h" +using namespace std; +using namespace std::placeholders; + +#define JSON_stationMode "stationMode" +#define JSON_stationSSID "stationSSID" +#define JSON_apPassword "apPassword" +#define JSON_apSSID "apSSID" +#define JSON_stationPassword "stationPassword" +#define JSON_hostname "hostname" +#define JSON_connect_timeout "connectTimeout" + +struct WiFiConfig : public JsonStruct { + int stationMode; + String stationSSID; + String stationPassword; + String apSSID; + String apPassword; + String hostname; + int connectTimeout; + + void mapJsonObject(JsonObject& root) { + root[JSON_stationMode] = stationMode; + root[JSON_stationSSID] = stationSSID; + root[JSON_stationPassword] = stationPassword; + root[JSON_apSSID] = apSSID; + root[JSON_apPassword] = apPassword; + root[JSON_hostname] = hostname; + root[JSON_connect_timeout] = connectTimeout; + } + + // Map a json object to this struct. + void fromJsonObject(JsonObject& json) { + stationMode = getIntAttrFromJson(json, JSON_stationMode, stationMode); + stationSSID = getAttrFromJson(json, JSON_stationSSID, stationSSID); + stationPassword = getAttrFromJson(json, JSON_stationPassword, stationPassword); + apSSID = getAttrFromJson(json, JSON_apSSID, apSSID); + apPassword = getAttrFromJson(json, JSON_apPassword, apPassword); + hostname = getAttrFromJson(json, JSON_hostname, hostname); + connectTimeout = getIntAttrFromJson(json, JSON_connect_timeout, connectTimeout); + }; +}; + +class WiFiNet : public Network { + public: + WiFiConfig config; + WiFiNet(int stationMode, const char* stationSSID, const char* stationPassword, const char* apSSID, const char* apPassword, const char* hostname, int connectTimeout); + Network* init(); + int connect(); + int connectStation(); + int createAccessPoint(); + int startDNS(); + void configure(WiFiConfig); + int isConnected(){ + return WiFi.status() == WL_CONNECTED; + } +}; + +#endif \ No newline at end of file diff --git a/src/examples/standalone/WiFiApp.h b/src/examples/standalone/WiFiApp.h new file mode 100644 index 0000000..46f31ae --- /dev/null +++ b/src/examples/standalone/WiFiApp.h @@ -0,0 +1,46 @@ + +#ifndef __WIFI_APP__ +#define __WIFI_APP__ + +#include +#include +#include +#include +#include +#include +#include "Mediator.h" + +using namespace std; +using namespace std::placeholders; + +AsyncWebServer WEBSERVER(80); + +class WiFiApp : public Sprocket { + public: + Scheduler* ts; + Task someTask; + WiFiApp(SprocketConfig cfg, OtaConfig otaCfg, WebServerConfig webCfg) : Sprocket(cfg) { + //addPlugin(new OtaTcpPlugin(otaCfg)); + addPlugin(new WebServerPlugin(webCfg, &WEBSERVER)); + addPlugin(new WebConfigPlugin(&WEBSERVER)); + ts = new Scheduler(); + } + + Sprocket* activate(Scheduler* scheduler, Network* network) { + Sprocket::activate(ts, network); + Serial.println("activate WiFiApp"); + // add a task + someTask.set(TASK_SECOND, TASK_FOREVER, [](){ + Serial.println("do stuff in task"); + }); + //addTask(someTask); + return this; + } using Sprocket::activate; + void loop(){ + //Sprocket::loop(); + ts->execute(); + yield(); + } +}; + +#endif \ No newline at end of file diff --git a/src/examples/standalone/config.h b/src/examples/standalone/config.h new file mode 100644 index 0000000..6e39aeb --- /dev/null +++ b/src/examples/standalone/config.h @@ -0,0 +1,31 @@ +#ifndef __STANDALONE_CONFIG__ +#define __STANDALONE_CONFIG__ + +// Scheduler config +#define _TASK_PRIORITY // Support for layered scheduling priority +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION + +// Chip config +#define SERIAL_BAUD_RATE 115200 +#define STARTUP_DELAY 3000 + +// network config +#define SPROCKET_MODE 0 +#define AP_SSID "MyAP" +#define AP_PASSWORD "myApPwd" +#define STATION_SSID "Th1ngs4p" +#define STATION_PASSWORD "th3r31sn0sp00n" +#define HOSTNAME "standalone-node" +#define CONNECT_TIMEOUT 10000 + +// OTA config +#define OTA_PORT 8266 +#define OTA_PASSWORD "" + +// WebServer +#define WEB_CONTEXT_PATH "/" +#define WEB_DOC_ROOT "/www" +#define WEB_DEFAULT_FILE "index.html" + +#endif \ No newline at end of file diff --git a/src/examples/standalone/main.cpp b/src/examples/standalone/main.cpp new file mode 100644 index 0000000..7f263f2 --- /dev/null +++ b/src/examples/standalone/main.cpp @@ -0,0 +1,30 @@ +#include "config.h" +#include "WiFiNet.h" +#include "WiFiApp.h" + +/* WiFiConfig wifiCfg = { + .stationMode=SPROCKET_MODE, + .stationSSID=STATION_SSID, + .stationPassword=STATION_PASSWORD, + .apSSID=AP_SSID, + .apPassword=AP_PASSWORD, + .hostname=HOSTNAME, + .connectTimeout=CONNECT_TIMEOUT +}; */ + +WiFiNet net(SPROCKET_MODE,STATION_SSID, STATION_PASSWORD,AP_SSID, AP_PASSWORD,HOSTNAME,CONNECT_TIMEOUT); +WiFiApp sprocket( + { STARTUP_DELAY, SERIAL_BAUD_RATE }, + { OTA_PORT, OTA_PASSWORD }, + { WEB_CONTEXT_PATH, WEB_DOC_ROOT, WEB_DEFAULT_FILE } +); + +void setup() { + delay(3000); + sprocket.join(net); +} + +void loop() { + sprocket.loop(); + yield(); +} \ No newline at end of file diff --git a/src/plugins/WebConfigPlugin.cpp b/src/plugins/WebConfigPlugin.cpp index 3f64646..7118b2b 100644 --- a/src/plugins/WebConfigPlugin.cpp +++ b/src/plugins/WebConfigPlugin.cpp @@ -4,10 +4,8 @@ #include #include "TaskSchedulerDeclarations.h" #include "ArduinoOTA.h" -#include "MeshNet.h" #include "Plugin.h" #include -#include using namespace std; using namespace std::placeholders; @@ -15,7 +13,6 @@ using namespace std::placeholders; class WebConfigPlugin : public Plugin { private: - MeshNet* net; AsyncWebServer* server; public: WebConfigPlugin(AsyncWebServer* webServer){ @@ -23,9 +20,6 @@ class WebConfigPlugin : public Plugin { server->serveStatic("/config.json", SPIFFS, "config.json"); } void activate(Scheduler* userScheduler, Network* network){ - - net = static_cast(network); - server->on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ Serial.println("GET /heap"); request->send(200, "text/plain", String(ESP.getFreeHeap())); @@ -49,6 +43,7 @@ class WebConfigPlugin : public Plugin { } request->redirect("/"); }); + Serial.println("WebConfig activated"); } }; diff --git a/src/plugins/WebServerPlugin.cpp b/src/plugins/WebServerPlugin.cpp index cf6c036..6758e9a 100644 --- a/src/plugins/WebServerPlugin.cpp +++ b/src/plugins/WebServerPlugin.cpp @@ -3,7 +3,6 @@ #include #include "TaskSchedulerDeclarations.h" -#include "MeshNet.h" #include "Plugin.h" #include @@ -13,7 +12,6 @@ using namespace std::placeholders; class WebServerPlugin : public Plugin { private: WebServerConfig config; - MeshNet* net; AsyncWebServer* server; public: WebServerPlugin(WebServerConfig cfg, AsyncWebServer* webServer){ @@ -21,7 +19,6 @@ class WebServerPlugin : public Plugin { server = webServer; } void activate(Scheduler* userScheduler, Network* network){ - net = static_cast(network); server->serveStatic(config.contextPath, SPIFFS, config.docRoot).setDefaultFile(config.defaultFile); // TODO add auth if configured // server->setAuthentication("user", "pass");