diff --git a/.vscode/settings.json b/.vscode/settings.json index e190734..1d4ecef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "terminal.integrated.env.linux": { - "PATH": "/home/master/.platformio/penv/bin:/home/master/.platformio/penv:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl", + "PATH": "/home/master/.platformio/penv/bin:/home/master/.platformio/penv:/home/master/.nvm/versions/node/v9.4.0/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/master/go/bin", "PLATFORMIO_CALLER": "vscode" }, "files.associations": { diff --git a/README.md b/README.md index ffd06d9..687fbd3 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,21 @@ # Illumination-Cat ## Features -- [50%] Enduser setup: initial setup where the cat opens an access point where it can be configured +- [50%] Enduser setup: initial setup where the cat opens an access point with a captive portal where it can be configured - [100%] WiFi: connect to existing AP as client or build a mesh network where all cats act as a collective -- [50%] Configurable startup LED sequence -- [50%] Configurable default LED sequence +- [30%] Web controls: colors and patterns can be changed +- [100%] Configurable default LED sequence - [0%] OctoPrint plugin: connect to an OctoPrint instance and reflect print status via colors - [80%] OTA plugin: cats connected to an AP can be updated over-the-air, either via direct TCP flash or upload of a binary -- [30%] Web controls: colors and patterns can be configured via a web interface +- [30%] Web controls: colors and patterns can be changed through the web interface - [20%] easy to flash single binary file - [0%] audio output ## Enduser Setup 1. Scan for access points 1. connect to illucat-mesh -1. open web browser with URL http://10.248.112.1 +1. open web browser with URL ..... TODO open website automatically 1. change stationMode to 1 for connecting the cat to your own AP. leave it 0 to build a mesh. 1. change stationSSID and stationPassword to match your AP settings -1. submit and hit restart \ No newline at end of file +1. submit and hit restart +1. illucat connects to your network and can be reached with http://illucat (or any other configured hostname) \ No newline at end of file diff --git a/data/pixelConfig.json b/data/pixelConfig.json index fdeb075..53511d2 100644 --- a/data/pixelConfig.json +++ b/data/pixelConfig.json @@ -1,6 +1,6 @@ { "pin": 4, - "length": 8, + "length": 3, "brightness": 32, "updateInterval": 150, "defaultColor": 100 diff --git a/lib/NeoPattern/NeoPattern.cpp b/lib/NeoPattern/NeoPattern.cpp index c7a072d..eabd2db 100644 --- a/lib/NeoPattern/NeoPattern.cpp +++ b/lib/NeoPattern/NeoPattern.cpp @@ -55,9 +55,10 @@ class NeoPattern : public Adafruit_NeoPixel void onCompleteDefault(int pixels) { if(ActivePattern != RAINBOW_CYCLE){ + //Serial.println("reversing"); Reverse(); } - Serial.println("pattern completed"); + //Serial.println("pattern completed"); } // Update the pattern @@ -109,7 +110,7 @@ class NeoPattern : public Adafruit_NeoPixel { OnComplete(numPixels()); // call the comlpetion callback } else { - Reverse(); + onCompleteDefault(numPixels()); } } } @@ -124,7 +125,7 @@ class NeoPattern : public Adafruit_NeoPixel { OnComplete(numPixels()); // call the comlpetion callback } else { - Reverse(); + onCompleteDefault(numPixels()); } } } @@ -313,7 +314,7 @@ class NeoPattern : public Adafruit_NeoPixel // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { - if(WheelPos == 0) return Color(0,0,0); + //if(WheelPos == 0) return Color(0,0,0); WheelPos = 255 - WheelPos; if(WheelPos < 85) { diff --git a/lib/NeoPattern/NeoPattern_api_modes.cpp b/lib/NeoPattern/NeoPattern_api_modes.cpp index 76f3a1f..e6698ff 100644 --- a/lib/NeoPattern/NeoPattern_api_modes.cpp +++ b/lib/NeoPattern/NeoPattern_api_modes.cpp @@ -55,8 +55,8 @@ const PIXEL_FP PIXEL_FNCS[] = { */ [](NeoPattern* pixels, const char *id){ pattern p = (pattern)atoi(id); - pixels->Interval = 50; - pixels->TotalSteps = pixels->numPixels(); + //pixels->Interval = 50; + //pixels->TotalSteps = pixels->numPixels(); pixels->ActivePattern = p; } }; diff --git a/lib/sprocket-utils/utils_ws.h b/lib/sprocket-utils/utils_ws.h index f0b972b..33ea353 100644 --- a/lib/sprocket-utils/utils_ws.h +++ b/lib/sprocket-utils/utils_ws.h @@ -48,7 +48,7 @@ class WsUtils { } return msg; } - static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + /* static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if(type == WS_EVT_CONNECT){ Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); client->printf("Hello Client %u :)", client->id()); @@ -119,7 +119,7 @@ class WsUtils { } } } - } + } */ }; diff --git a/platformio.ini b/platformio.ini index 09cc2af..8d5acb5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,4 +38,5 @@ lib_deps = ${common.lib_deps} ESP Async WebServer ESPAsyncTCP Adafruit NeoPixel + DNSServer https://gitlab.com/wirelos/sprocket-core.git#develop \ No newline at end of file diff --git a/src/IlluCat.h b/src/IlluCat.h index c37324e..6a0702b 100644 --- a/src/IlluCat.h +++ b/src/IlluCat.h @@ -4,6 +4,8 @@ #include #include #include +#include + #include "config.h" #include "NeoPattern.cpp" @@ -21,14 +23,16 @@ using namespace std; using namespace std::placeholders; +const byte DNS_PORT = 53; + class IlluCat : public MeshSprocket { public: NeoPattern* pixels; NeoPatternDto defaultState; NeoPatternDto state; - Task animation; AsyncWebServer* server; AsyncWebSocket* ws; + DNSServer* dnsServer; NeoPixelConfig pixelConfig; SprocketConfig sprocketConfig; @@ -49,13 +53,14 @@ class IlluCat : public MeshSprocket { pixelConfig.defaultColor = 100; } - void scanningAnimation() { + virtual void scanningAnimation() { pixels->Scanner(pixels->Wheel(COLOR_NOT_CONNECTED), pixelConfig.updateInterval); //pixels->Fade(0, pixels->Color(255,255,255), 4, pixelConfig.updateInterval, FORWARD); } - void defaultAnimation() { + virtual void defaultAnimation() { String defaultStr = String(defaultState.value); PIXEL_FNCS[defaultState.mode](pixels, defaultStr.c_str()); + //pixels->RainbowCycle(150); } Sprocket* activate(Scheduler* scheduler, Network* network) { @@ -71,30 +76,40 @@ class IlluCat : public MeshSprocket { pixels = new NeoPattern(pixelConfig.length, pixelConfig.pin, NEO_GRB + NEO_KHZ800); server = new AsyncWebServer(80); ws = new AsyncWebSocket("/pixel"); - ws->onEvent([=](AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - this->onWsEvent(server, client, type, arg, data, len); - }); + dnsServer = new DNSServer(); + addPlugin(new OtaTcpPlugin(otaConfig)); addPlugin(new WebServerPlugin(webConfig, server)); addPlugin(new WebConfigPlugin(server)); addPlugin(new PixelPlugin(pixelConfig, pixels)); - scanningAnimation(); + + dnsServer->setErrorReplyCode(DNSReplyCode::NoError); + dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + //scanningAnimation(); + defaultAnimation(); + + // setup web stuff + server->on("/pixel/pattern", HTTP_POST, bind(&IlluCat::patternWebRequestHandler, this, _1)); + ws->onEvent(bind(&IlluCat::onWsEvent, this, _1, _2, _3, _4, _5, _6)); server->addHandler(ws); - server->on("/pixel/pattern", HTTP_GET, [this](AsyncWebServerRequest *request){ - Serial.println("POST /pixel/pattern"); - if(request->hasParam("state", true)) { - String inStr = request->getParam("state", true)->value(); - onMessage(0, inStr); - } - request->send(200, "text/plain", "OK"); - }); + // FIXME OnDisable is triggered after last scan, aprx. 10 sec net->mesh.stationScan.task.setOnDisable(bind(&IlluCat::defaultAnimation,this)); return MeshSprocket::activate(scheduler, network); } using MeshSprocket::activate; - void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + void patternWebRequestHandler(AsyncWebServerRequest *request) { + Serial.println("POST /pixel/pattern"); + if(request->hasParam("state", true)) { + String inStr = request->getParam("state", true)->value(); + onMessage(0, inStr); + } + request->send(200, "text/plain", "OK"); + } + + virtual void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if(type == WS_EVT_DATA){ String frame = WsUtils::parseFrameAsString(type, arg, data, len, 0); onMessage(0, frame); @@ -103,22 +118,28 @@ class IlluCat : public MeshSprocket { } } - void onMessage( uint32_t from, String &msg ) { + virtual void onMessage( uint32_t from, String &msg ) { PRINT_MSG(Serial, SPROCKET_TYPE, "msg from %u = %s\n", from, msg.c_str()); state.fromJsonString(msg); PIXEL_FNCS[state.mode](pixels, state.valueStr); } - void onNewConnection(uint32_t nodeId){ + virtual void onNewConnection(uint32_t nodeId){ PRINT_MSG(Serial, SPROCKET_TYPE, "connected to %u", nodeId); defaultAnimation(); + // TODO publish current state to pixel/color on new node } - void onConnectionChanged(){ + virtual void onConnectionChanged(){ PRINT_MSG(Serial, SPROCKET_TYPE, "connection changed"); if(!net->mesh.getNodeList().size()){ defaultAnimation(); } } + + void loop(){ + MeshSprocket::loop(); + dnsServer->processNextRequest(); + } }; #endif \ No newline at end of file