If the code hasn't been touched in this long, its probably release-worthy.
This commit is contained in:
		| @@ -26,7 +26,7 @@ BluetoothSerialTelemetry::read() | ||||
|       // Overwrite the '*' character, to leave us with a complete command | ||||
|       commandBuf[cmdSize-1] = 0; | ||||
|  | ||||
|       //Log.notice("Bluetooth read %s", commandBuf); | ||||
|       Log.verbose("Bluetooth read %s", commandBuf); | ||||
|  | ||||
|       if (commandBuf[0] == 'R') { | ||||
|         m_color = CRGB(std::atoi(&commandBuf[1]), m_color.g, m_color.b); | ||||
| @@ -49,6 +49,12 @@ BluetoothSerialTelemetry::read() | ||||
|         return InputEvent{InputEvent::SetPower, 0}; | ||||
|       } else if (commandBuf[0] == 'p') { | ||||
|         return InputEvent{InputEvent::SetPattern, &commandBuf[1]}; | ||||
|       } else if (commandBuf[0] == 'b') { | ||||
|         return InputEvent::BeatDetect; | ||||
|       } else if (commandBuf[0] == 'c') { | ||||
|         return InputEvent{InputEvent::SetDisplayLength, std::atoi(&commandBuf[1])}; | ||||
|       } else if (commandBuf[0] == 'Y') { | ||||
|         return InputEvent::SaveConfigurationRequest; | ||||
|       } else if (commandBuf[0] == 'A') { | ||||
|         char* axisVal = strtok(&commandBuf[1], ","); | ||||
|         const uint8_t accelX = std::atof(axisVal) * 10; | ||||
| @@ -75,12 +81,13 @@ BluetoothSerialTelemetry::read() | ||||
| void | ||||
| BluetoothSerialTelemetry::onStart() | ||||
| { | ||||
|   Log.notice("Starting up Bluetooth..."); | ||||
|   Log.trace("Starting up Bluetooth..."); | ||||
|   if (m_serial.begin(Platform::deviceName())) { | ||||
|     Log.notice("Bluetooth started!"); | ||||
|     Log.notice("Bluetooth started! Device name is %s", Platform::deviceName()); | ||||
|   } else { | ||||
|     Log.warning("Bluetooth could not be started!"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| STATIC_ALLOC(BluetoothSerialTelemetry); | ||||
| STATIC_TASK(BluetoothSerialTelemetry); | ||||
|   | ||||
| @@ -117,39 +117,37 @@ MQTTTelemetry::handleEventOnline(const InputEvent& evt) | ||||
|       Log.notice("Connected to MQTT"); | ||||
|       m_needHeartbeat = true; | ||||
|  | ||||
|       StaticJsonDocument<1024> configJson; | ||||
|  | ||||
|       Lightswitch.toJson(configJson); | ||||
|       m_json.clear(); | ||||
|       Lightswitch.toJson(m_json); | ||||
|  | ||||
|       int i = 0; | ||||
|       for(const Sequencer::Scene& scene : m_sequencer->scenes()) { | ||||
|         configJson["fx_list"][i++] = scene.name; | ||||
|         m_json["fx_list"][i++] = scene.name; | ||||
|       } | ||||
|       configJson["brightness"] = true; | ||||
|       configJson["rgb"] = true; | ||||
|       m_json["brightness"] = true; | ||||
|       m_json["rgb"] = true; | ||||
|  | ||||
|       char buf[1024]; | ||||
|       serializeJson(configJson, buf, sizeof(buf)); | ||||
|       publishDoc(Lightswitch.configTopic().c_str(), true); | ||||
|  | ||||
|       Log.verbose("Publish %s %s", Lightswitch.configTopic().c_str(), buf); | ||||
|       m_mqtt.publish(Lightswitch.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       //Log.verbose("Publish %s %s", Lightswitch.configTopic().c_str(), buf); | ||||
|       //m_mqtt.publish(Lightswitch.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       m_mqtt.subscribe(Lightswitch.commandTopic().c_str()); | ||||
|  | ||||
|       configJson.clear(); | ||||
|       flashlightSwitch.toJson(configJson, false); | ||||
|       configJson["cmd_t"] = "~/set"; | ||||
|       configJson["ret"] = true; | ||||
|       serializeJson(configJson, buf, sizeof(buf)); | ||||
|       m_mqtt.publish(flashlightSwitch.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       m_json.clear(); | ||||
|       flashlightSwitch.toJson(m_json, false); | ||||
|       m_json["cmd_t"] = "~/set"; | ||||
|       m_json["ret"] = true; | ||||
|       publishDoc(flashlightSwitch.configTopic().c_str(), true); | ||||
|       //m_mqtt.publish(flashlightSwitch.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       m_mqtt.subscribe(flashlightSwitch.commandTopic().c_str()); | ||||
|  | ||||
|       configJson.clear(); | ||||
|       FPSSensor.toJson(configJson, false); | ||||
|       configJson["unit_of_meas"] = "Frames/s"; | ||||
|       serializeJson(configJson, buf, sizeof(buf)); | ||||
|       m_json.clear(); | ||||
|       FPSSensor.toJson(m_json, false); | ||||
|       m_json["unit_of_meas"] = "Frames/s"; | ||||
|       publishDoc(FPSSensor.configTopic().c_str(), true); | ||||
|  | ||||
|       Log.verbose("Publish %s %s", FPSSensor.configTopic().c_str(), buf); | ||||
|       m_mqtt.publish(FPSSensor.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       //Log.verbose("Publish %s %s", FPSSensor.configTopic().c_str(), buf); | ||||
|       //m_mqtt.publish(FPSSensor.configTopic().c_str(), (uint8_t*)buf, strlen(buf), true); | ||||
|       m_mqtt.subscribe(FPSSensor.commandTopic().c_str()); | ||||
|  | ||||
| #ifdef BOARD_ESP8266 | ||||
| @@ -172,39 +170,28 @@ MQTTTelemetry::handleEventOnline(const InputEvent& evt) | ||||
|       String flashlightStatTopic = flashlightSwitch.statTopic(); | ||||
|       m_mqtt.publish(flashlightStatTopic.c_str(), "ON"); | ||||
|     } else if (evt.intent == InputEvent::SetPower) { | ||||
|       StaticJsonDocument<256> doc; | ||||
|       char buf[256]; | ||||
|       m_json.clear(); | ||||
|       m_isOn = evt.asInt() ? true : false; | ||||
|       doc["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       serializeJson(doc, buf, sizeof(buf)); | ||||
|       m_mqtt.publish(statTopic.c_str(), buf); | ||||
|       m_json["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       publishDoc(statTopic.c_str()); | ||||
|     } else if (evt.intent == InputEvent::SetBrightness) { | ||||
|       StaticJsonDocument<256> doc; | ||||
|       char buf[256]; | ||||
|       doc["brightness"] = evt.asInt(); | ||||
|       doc["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       serializeJson(doc, buf, sizeof(buf)); | ||||
|       m_mqtt.publish(statTopic.c_str(), buf); | ||||
|       m_json.clear(); | ||||
|       m_json["brightness"] = evt.asInt(); | ||||
|       m_json["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       publishDoc(statTopic.c_str()); | ||||
|     } else if (evt.intent == InputEvent::SetColor) { | ||||
|       StaticJsonDocument<256> doc; | ||||
|       char buf[256]; | ||||
|       CRGB color = evt.asRGB(); | ||||
|       doc["color"]["r"] = color.r; | ||||
|       doc["color"]["g"] = color.g; | ||||
|       doc["color"]["b"] = color.b; | ||||
|       doc["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       serializeJson(doc, buf, sizeof(buf)); | ||||
|       m_mqtt.publish(statTopic.c_str(), buf); | ||||
|       m_json.clear(); | ||||
|       m_json["color"]["r"] = color.r; | ||||
|       m_json["color"]["g"] = color.g; | ||||
|       m_json["color"]["b"] = color.b; | ||||
|       m_json["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       publishDoc(statTopic.c_str()); | ||||
|     } else if (evt.intent == InputEvent::SetPattern) { | ||||
|       StaticJsonDocument<256> doc; | ||||
|       char buf[256]; | ||||
|       doc["effect"] = evt.asString(); | ||||
|       doc["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       serializeJson(doc, buf, sizeof(buf)); | ||||
|       m_mqtt.publish(statTopic.c_str(), buf); | ||||
|     } else if (evt.intent == InputEvent::FirmwareUpdate) { | ||||
|       String updateTopic = m_debugTopic + "/firmware"; | ||||
|       m_mqtt.publish(updateTopic.c_str(), "firmware update!"); | ||||
|       m_json.clear(); | ||||
|       m_json["effect"] = evt.asString(); | ||||
|       m_json["state"] = m_isOn ? "ON" : "OFF"; | ||||
|       publishDoc(statTopic.c_str()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -241,22 +228,20 @@ MQTTTelemetry::loopOnline() | ||||
|     m_needHeartbeat = true; | ||||
|   } | ||||
|   if (m_needHeartbeat) { | ||||
|     char buf[512]; | ||||
|     StaticJsonDocument<512> response; | ||||
|     response["device_id"] = Platform::deviceID(); | ||||
|     response["sketch_version"] = ESP.getSketchMD5(); | ||||
|     response["os_version"] = ESP.getSdkVersion(); | ||||
|     response["localip"] = WiFi.localIP().toString(); | ||||
|     response["pixelCount"] = Static<ConfigService>::instance()->coordMap()->pixelCount; | ||||
|     response["startPixel"] = Static<ConfigService>::instance()->coordMap()->startPixel; | ||||
|     response["RSSI"] = WiFi.RSSI(); | ||||
|     response["free_ram"] = ESP.getFreeHeap(); | ||||
|     response["fps"] = FastLED.getFPS(); | ||||
|     serializeJson(response, buf, sizeof(buf)); | ||||
|     m_json.clear(); | ||||
|     m_json["device_id"] = Platform::deviceID(); | ||||
|     m_json["sketch_version"] = ESP.getSketchMD5(); | ||||
|     m_json["os_version"] = ESP.getSdkVersion(); | ||||
|     m_json["localip"] = WiFi.localIP().toString(); | ||||
|     m_json["pixelCount"] = Static<ConfigService>::instance()->coordMap()->physicalPixelCount(); | ||||
|     //m_json["startPixel"] = Static<ConfigService>::instance()->coordMap()->startPixel; | ||||
|     m_json["RSSI"] = WiFi.RSSI(); | ||||
|     m_json["free_ram"] = ESP.getFreeHeap(); | ||||
|     m_json["fps"] = FastLED.getFPS(); | ||||
|     String availTopic = m_rootTopic + "/available"; | ||||
|     m_mqtt.publish(Lightswitch.heartbeatTopic().c_str(), buf); | ||||
|     publishDoc(Lightswitch.heartbeatTopic().c_str()); | ||||
|     m_mqtt.publish(Device.availabilityTopic.c_str(), "online"); | ||||
|     //Log.notice("Heartbeat: %s", buf); | ||||
|     //Log.trace("Heartbeat: %s", buf); | ||||
|  | ||||
|     String fpsCounter = String(FastLED.getFPS()); | ||||
|     m_mqtt.publish(FPSSensor.statTopic().c_str(), fpsCounter.c_str()); | ||||
| @@ -280,89 +265,106 @@ MQTTTelemetry::callback(char* topic, const char* payload) | ||||
|         setEvent(InputEvent{InputEvent::SetPattern, "Idle"}); | ||||
|       } | ||||
|     } else if (Lightswitch.isCommandTopic(topic)) { | ||||
|       StaticJsonDocument<512> doc; | ||||
|       deserializeJson(doc, payload); | ||||
|       deserializeJson(m_json, payload); | ||||
|  | ||||
|       if (doc.containsKey("state")) { | ||||
|           if (doc["state"] == "ON") { | ||||
|       if (m_json.containsKey("state")) { | ||||
|           if (m_json["state"] == "ON") { | ||||
|             Log.notice("Turning on power"); | ||||
|             setEvent(InputEvent{InputEvent::SetPower, true}); | ||||
|           } else if (doc["state"] == "OFF") { | ||||
|           } else if (m_json["state"] == "OFF") { | ||||
|             Log.notice("Turning off power"); | ||||
|             setEvent(InputEvent{InputEvent::SetPattern, "Idle"}); | ||||
|             setEvent(InputEvent{InputEvent::SetPower, false}); | ||||
|           } | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("start")) { | ||||
|         strcpy(m_patternBuf, doc["start"].as<const char*>()); | ||||
|       if (m_json.containsKey("start")) { | ||||
|         strcpy(m_patternBuf, m_json["start"].as<const char*>()); | ||||
|         setEvent(InputEvent{InputEvent::StartThing, m_patternBuf}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("stop")) { | ||||
|         if (doc["stop"] == name) { | ||||
|           Log.notice("You can't kill an idea, or stop the MQTT Task via MQTT."); | ||||
|       if (m_json.containsKey("stop")) { | ||||
|         if (m_json["stop"] == name) { | ||||
|           Log.warning("You can't kill an idea, or stop the MQTT Task via MQTT."); | ||||
|         } else { | ||||
|           strcpy(m_patternBuf, doc["stop"].as<const char*>()); | ||||
|           strcpy(m_patternBuf, m_json["stop"].as<const char*>()); | ||||
|           setEvent(InputEvent{InputEvent::StopThing, m_patternBuf}); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("pixelCount")) { | ||||
|         setEvent(InputEvent{InputEvent::SetDisplayLength, (int)doc["pixelCount"]}); | ||||
|       if (m_json.containsKey("pixelCount")) { | ||||
|         Log.notice("Pixel count changed"); | ||||
|         setEvent(InputEvent{InputEvent::SetDisplayLength, m_json["pixelCount"].as<int>()}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("startPixel")) { | ||||
|         setEvent(InputEvent{InputEvent::SetDisplayOffset, (int)doc["startPixel"]}); | ||||
|       if (m_json.containsKey("startPixel")) { | ||||
|         Log.notice("Start pixel changed"); | ||||
|         setEvent(InputEvent{InputEvent::SetDisplayOffset, m_json["startPixel"].as<int>()}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("save")) { | ||||
|       if (m_json.containsKey("save")) { | ||||
|         setEvent(InputEvent{InputEvent::SaveConfigurationRequest}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("restart")) { | ||||
| #ifdef BOARD_ESP8266 | ||||
|         ESP.wdtDisable(); | ||||
|         ESP.restart(); | ||||
| #endif | ||||
|       if (m_json.containsKey("restart")) { | ||||
|         Platform::restart(); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("reconnect")) { | ||||
|       if (m_json.containsKey("reconnect")) { | ||||
|         m_mqtt.disconnect(); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("ping")) { | ||||
|       if (m_json.containsKey("ping")) { | ||||
|         m_needHeartbeat = true; | ||||
|         Log.notice("Queuing up heartbeat"); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("effect")) { | ||||
|           strcpy(m_patternBuf, doc["effect"].as<const char*>()); | ||||
|       if (m_json.containsKey("effect")) { | ||||
|           strcpy(m_patternBuf, m_json["effect"].as<const char*>()); | ||||
|           setEvent(InputEvent{InputEvent::SetPattern, m_patternBuf}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("color")) { | ||||
|           uint8_t r = doc["color"]["r"]; | ||||
|           uint8_t g = doc["color"]["g"]; | ||||
|           uint8_t b = doc["color"]["b"]; | ||||
|       if (m_json.containsKey("color")) { | ||||
|           uint8_t r = m_json["color"]["r"]; | ||||
|           uint8_t g = m_json["color"]["g"]; | ||||
|           uint8_t b = m_json["color"]["b"]; | ||||
|           setEvent(InputEvent{InputEvent::SetColor, CRGB(r, g, b)}); | ||||
|       } | ||||
|  | ||||
|       if (doc.containsKey("brightness")) { | ||||
|           setEvent(InputEvent{InputEvent::SetBrightness, (int)doc["brightness"]}); | ||||
|       if (m_json.containsKey("brightness")) { | ||||
|           setEvent(InputEvent{InputEvent::SetBrightness, m_json["brightness"].as<int>()}); | ||||
|       } | ||||
|  | ||||
|       Log.notice("Event done."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| MQTTTelemetry::s_callback(char* topic, byte* payload, unsigned int length) | ||||
| { | ||||
|     char topicBuf[128]; | ||||
|     char payloadBuf[512]; | ||||
|     strcpy(topicBuf, topic); | ||||
|     memcpy(payloadBuf, payload, length); | ||||
|     payloadBuf[std::min(sizeof(payloadBuf) - 1, length)] = 0; | ||||
|     Static<MQTTTelemetry>::instance()->callback(topicBuf, payloadBuf); | ||||
|     strcpy(s_topicBuf, topic); | ||||
|     memcpy(s_payloadBuf, payload, length); | ||||
|     s_payloadBuf[std::min(sizeof(s_payloadBuf) - 1, length)] = 0; | ||||
|     Static<MQTTTelemetry>::instance()->callback(s_topicBuf, s_payloadBuf); | ||||
| } | ||||
|  | ||||
| char MQTTTelemetry::s_topicBuf[128] = {0}; | ||||
| char MQTTTelemetry::s_payloadBuf[512] = {0}; | ||||
|  | ||||
| void | ||||
| MQTTTelemetry::publishDoc(const char* topic, bool retain) | ||||
| { | ||||
|   m_mqtt.beginPublish(topic, measureJson(m_json), retain); | ||||
|   serializeJson(m_json, m_mqtt); | ||||
|   m_mqtt.endPublish(); | ||||
| } | ||||
|  | ||||
| void | ||||
| MQTTTelemetry::publishDoc(const char* topic) | ||||
| { | ||||
|   publishDoc(topic, false); | ||||
| } | ||||
|  | ||||
|  | ||||
| STATIC_ALLOC(MQTTTelemetry); | ||||
| STATIC_TASK(MQTTTelemetry); | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include <WiFi.h> | ||||
| #endif | ||||
|  | ||||
| #include <ArduinoJson.h> | ||||
|  | ||||
| class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin { | ||||
|   public: | ||||
| @@ -25,13 +26,16 @@ class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin { | ||||
|       public: | ||||
|         LogPrinter(MQTTTelemetry* telemetry) : telemetry(telemetry) {}; | ||||
|         size_t write(uint8_t byte) { | ||||
|           char outBuf[512]; | ||||
|           if (byte == '\n') { | ||||
|             size_t bufSize = buf.write(outBuf); | ||||
|             outBuf[std::min(sizeof(outBuf), bufSize)] = 0; | ||||
|             Serial.println(outBuf); | ||||
|             char c; | ||||
|             String logTopic = telemetry->m_debugTopic + "/log"; | ||||
|             telemetry->m_mqtt.publish(logTopic.c_str(), outBuf); | ||||
|             telemetry->m_mqtt.beginPublish(logTopic.c_str(), buf.size(), false); | ||||
|             while (buf.take(c)) { | ||||
|               Serial.write(c); | ||||
|               telemetry->m_mqtt.write(c); | ||||
|             } | ||||
|             Serial.println(); | ||||
|             telemetry->m_mqtt.endPublish(); | ||||
|           } else { | ||||
|             buf.insert(byte); | ||||
|           } | ||||
| @@ -61,9 +65,15 @@ class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin { | ||||
|     char m_patternBuf[48]; | ||||
|     bool m_needHeartbeat = false; | ||||
|     bool m_isOn = true; | ||||
|     static char s_topicBuf[128]; | ||||
|     static char s_payloadBuf[512]; | ||||
|  | ||||
|     void publishDoc(const char* topic); | ||||
|     void publishDoc(const char* topic, bool retain); | ||||
|  | ||||
|     Sequencer *m_sequencer = 0; | ||||
|     WiFiClient m_wifi; | ||||
|     PubSubClient m_mqtt; | ||||
|     LogPrinter m_logPrinter; | ||||
|     StaticJsonDocument<1024> m_json; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/platform/arduino/OTA.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/platform/arduino/OTA.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #include "OTA.h" | ||||
| #include "../../Static.h" | ||||
|  | ||||
| ArduinoOTAUpdater::ArduinoOTAUpdater() : BufferedInputSource("ArduinoOTA") { | ||||
|   ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart); | ||||
|   ArduinoOTA.onProgress(&ArduinoOTAUpdater::s_onProgress); | ||||
| } | ||||
|  | ||||
| void ArduinoOTAUpdater::loop() { | ||||
|     if (m_online) { | ||||
|         ArduinoOTA.handle(); | ||||
|     } | ||||
|     BufferedInputSource::loop(); | ||||
| } | ||||
|  | ||||
| void ArduinoOTAUpdater::handleEvent(const InputEvent& evt) { | ||||
|     if (evt.intent == InputEvent::NetworkStatus && evt.asInt()) { | ||||
|         Log.notice("Booting OTA"); | ||||
|         m_online = true; | ||||
|         ArduinoOTA.begin(); | ||||
|     } | ||||
| } | ||||
| void ArduinoOTAUpdater::s_onStart() { | ||||
|     Log.notice("OTA Start!"); | ||||
|     Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent::FirmwareUpdate); | ||||
| } | ||||
|  | ||||
| void ArduinoOTAUpdater::s_onProgress(unsigned int progress, unsigned int total) { | ||||
|     Log.notice("OTA Progress! %d / %d", progress, total); | ||||
|     Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent{InputEvent::FirmwareUpdate, progress}); | ||||
| } | ||||
|  | ||||
| STATIC_ALLOC(ArduinoOTAUpdater); | ||||
| STATIC_TASK(ArduinoOTAUpdater); | ||||
							
								
								
									
										14
									
								
								src/platform/arduino/OTA.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/platform/arduino/OTA.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include <ArduinoOTA.h> | ||||
| #include <Figments.h> | ||||
|  | ||||
| class ArduinoOTAUpdater : public BufferedInputSource { | ||||
|   public: | ||||
|     ArduinoOTAUpdater(); | ||||
|     void loop() override; | ||||
|     void handleEvent(const InputEvent& evt) override; | ||||
|  | ||||
|   private: | ||||
|     bool m_online = false; | ||||
|     static void s_onStart(); | ||||
|     static void s_onProgress(unsigned int progress, unsigned int total); | ||||
| }; | ||||
							
								
								
									
										232
									
								
								src/platform/arduino/U8Display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								src/platform/arduino/U8Display.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,232 @@ | ||||
| #include <Figments.h> | ||||
| #include <U8g2lib.h> | ||||
| #include "../../Static.h" | ||||
| #include <ArduinoLog.h> | ||||
| #include "../../LogService.h" | ||||
|  | ||||
| U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, 16, 15, 4); | ||||
|  | ||||
| class U8Display : public Task { | ||||
|   public: | ||||
|     U8Display() : Task("U8Display") {} | ||||
|  | ||||
|     enum ScreenState { | ||||
|       BootSplash, | ||||
|       Running, | ||||
|       Message, | ||||
|       Idle = Running | ||||
|     }; | ||||
|  | ||||
|     void onStart() { | ||||
|       xTaskCreatePinnedToCore( | ||||
|           &U8Display::redrawTask, | ||||
|           name, | ||||
|           2000, | ||||
|           this, | ||||
|           1, | ||||
|           &m_renderTask, 0 | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     void handleEvent(const InputEvent& evt) { | ||||
|       m_lastEvent = evt; | ||||
|       if (m_state == Idle) { | ||||
|         switch(evt.intent) { | ||||
|           case InputEvent::NetworkStatus: | ||||
|             m_nextState = Message; | ||||
|             m_message = evt.asBool() ? "Online!" : "Offline :["; | ||||
|             break; | ||||
|           case InputEvent::SetPattern: | ||||
|             m_nextState = Message; | ||||
|             m_message = "Pattern: " + String(evt.asString()); | ||||
|             break; | ||||
|           case InputEvent::SetPower: | ||||
|             m_nextState = Message; | ||||
|             m_message = evt.asBool() ? "Power On" : "Power Off"; | ||||
|             break; | ||||
|           case InputEvent::SaveConfigurationRequest: | ||||
|             m_nextState = Message; | ||||
|             m_message = "Settings Saved!"; | ||||
|             break; | ||||
|           case InputEvent::FirmwareUpdate: | ||||
|             m_nextState = Message; | ||||
|             m_message = "Firmware update"; | ||||
|             break; | ||||
|           case InputEvent::PreviousPattern: | ||||
|             m_nextState = Message; | ||||
|             m_message = "Previous Pattern"; | ||||
|             break; | ||||
|           case InputEvent::NextPattern: | ||||
|             m_nextState = Message; | ||||
|             m_message = "Next Pattern"; | ||||
|             break; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     void drawSplash() { | ||||
|       //u8g2.setFont(u8g2_font_VCR_OSD_mr); | ||||
|       u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr); | ||||
|       u8g2.setDrawColor(1); | ||||
|       uint8_t splashWidth = u8g2.getStrWidth("Renderbug!"); | ||||
|       u8g2.drawStr(64 - splashWidth / 2, 32 - 15, "Renderbug!"); | ||||
|       u8g2.setFont(u8g2_font_7x13B_mr); | ||||
|       u8g2.setCursor(15, 64 - 7); | ||||
|       u8g2.print("Version "); | ||||
|       u8g2.print(RENDERBUG_VERSION); | ||||
|  | ||||
|       for(int i = 0; i < 3; i++) { | ||||
|         uint8_t sparkleX = (64 - splashWidth/2) + scale8(7-i, beatsin8(40)) * (splashWidth/7) + 5; | ||||
|         uint8_t sparkleY = scale8(3+i, beatsin8(40)) * 3 + 7; | ||||
|         u8g2.setDrawColor(2); | ||||
|         if (beatsin8(60*4) + i * 3 >= 170) { | ||||
|           u8g2.drawLine(sparkleX - 3, sparkleY - 3, sparkleX + 3, sparkleY + 3); | ||||
|           u8g2.drawLine(sparkleX + 3, sparkleY - 3, sparkleX - 3, sparkleY + 3); | ||||
|         } else if (beatsin8(60*4) + i * 2 >= 82) { | ||||
|           u8g2.drawLine(sparkleX - 4, sparkleY - 4, sparkleX + 2, sparkleY + 2); | ||||
|           u8g2.drawLine(sparkleX - 2, sparkleY - 2, sparkleX + 4, sparkleY + 4); | ||||
|           u8g2.drawLine(sparkleX + 4, sparkleY - 4, sparkleX - 2, sparkleY + 2); | ||||
|           u8g2.drawLine(sparkleX + 2, sparkleY - 2, sparkleX - 4, sparkleY + 4); | ||||
|         } else { | ||||
|           u8g2.drawLine(sparkleX - 2, sparkleY, sparkleX + 2, sparkleY); | ||||
|           u8g2.drawLine(sparkleX, sparkleY - 2, sparkleX, sparkleY + 2); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     void drawMessage() { | ||||
|       //u8g2.setFont(u8g2_font_VCR_OSD_mr); | ||||
|       u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr); | ||||
|       uint8_t splashWidth = u8g2.getStrWidth(m_message.c_str()); | ||||
|       if (splashWidth >= 128) { | ||||
|         u8g2.setFont(u8g2_font_7x13B_mr); | ||||
|         splashWidth = u8g2.getStrWidth(m_message.c_str()); | ||||
|       } | ||||
|       u8g2.drawStr(64 - splashWidth / 2, 32 - 15, m_message.c_str()); | ||||
|     } | ||||
|  | ||||
|     void drawTime() { | ||||
|         u8g2.setFont(u8g2_font_7x13O_tn); | ||||
|         u8g2.setCursor(0, 64); | ||||
|         struct tm timeinfo; | ||||
|         Platform::getLocalTime(&timeinfo); | ||||
|         uint8_t hour = timeinfo.tm_hour; | ||||
|         uint8_t minute = timeinfo.tm_min; | ||||
|         u8g2.print(hour); | ||||
|         u8g2.print(":"); | ||||
|         u8g2.print(minute); | ||||
|     } | ||||
|  | ||||
|     void drawState(ScreenState state) { | ||||
|       switch(state) { | ||||
|         case BootSplash: | ||||
|           drawSplash(); | ||||
|           break; | ||||
|         case Message: | ||||
|           drawMessage(); | ||||
|           break; | ||||
|         case Running: | ||||
|           uint8_t y = 11; | ||||
|           u8g2.setFont(u8g2_font_7x13B_mr); | ||||
|           u8g2.setCursor(0, y); | ||||
|           u8g2.print("FPS: "); | ||||
|           u8g2.setFont(u8g2_font_7x13O_tn); | ||||
|           u8g2.print(FastLED.getFPS()); | ||||
|           y += 12; | ||||
|           u8g2.setCursor(0, y); | ||||
|           u8g2.setFont(u8g2_font_7x13B_mr); | ||||
|           u8g2.print("Last event: "); | ||||
|           y += 7; | ||||
|           u8g2.setCursor(10, y); | ||||
|           u8g2.setFont(u8g2_font_4x6_tr); | ||||
|           const char* intentName = LogService::intentName(m_lastEvent.intent); | ||||
|           if (intentName) { | ||||
|             u8g2.print(intentName); | ||||
|           } else { | ||||
|             u8g2.print("<"); | ||||
|             u8g2.print(m_lastEvent.intent); | ||||
|             u8g2.print(">"); | ||||
|           } | ||||
|           y += 12; | ||||
|           u8g2.setCursor(15, y); | ||||
|           u8g2.setFont(u8g2_font_7x13O_tf); | ||||
|           u8g2.print(LogService::eventValue(m_lastEvent)); | ||||
|           drawTime(); | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     void loop() { | ||||
|       EVERY_N_MILLISECONDS(8) { | ||||
|         xTaskNotifyGive(m_renderTask); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   private: | ||||
|     ScreenState m_state = BootSplash; | ||||
|     ScreenState m_nextState = BootSplash; | ||||
|     uint8_t m_transitionFrame = 0; | ||||
|     uint8_t m_screenStartTime = 0; | ||||
|     InputEvent m_lastEvent; | ||||
|     String m_message; | ||||
|  | ||||
|     TaskHandle_t m_renderTask; | ||||
|     void redraw() { | ||||
|       if (m_state != m_nextState) { | ||||
|           EVERY_N_MILLISECONDS(8) { | ||||
|             constexpr uint8_t speed = 11; | ||||
|             if (m_transitionFrame <= 255 - speed) { | ||||
|               m_transitionFrame += speed; | ||||
|             } else { | ||||
|               m_transitionFrame = 255; | ||||
|             } | ||||
|             uint8_t offset = ease8InOutApprox(m_transitionFrame); | ||||
|             u8g2.clearBuffer(); | ||||
|             if (m_transitionFrame <= 128) { | ||||
|                 uint8_t width = scale8(128, offset * 2); | ||||
|                 u8g2.setDrawColor(1); | ||||
|                 drawState(m_state); | ||||
|                 u8g2.drawBox(0, 0, width, 64); | ||||
|                 u8g2.setDrawColor(2); | ||||
|                 u8g2.drawBox(width, 0, 8, 64); | ||||
|             } else { | ||||
|                 uint8_t width = scale8(128, offset/2)*2; | ||||
|                 u8g2.setDrawColor(1); | ||||
|                 drawState(m_nextState); | ||||
|                 u8g2.setDrawColor(2); | ||||
|                 u8g2.drawBox(std::max(0, width - 8), 0, 8, 64); | ||||
|                 u8g2.setDrawColor(1); | ||||
|                 u8g2.drawBox(width, 0, 128, 64); | ||||
|             } | ||||
|             u8g2.sendBuffer(); | ||||
|  | ||||
|             if (m_transitionFrame == 255) { | ||||
|               m_state = m_nextState; | ||||
|               m_screenStartTime = millis(); | ||||
|               m_transitionFrame = 0; | ||||
|             } | ||||
|           } | ||||
|       } else { | ||||
|         u8g2.clearBuffer(); | ||||
|         drawState(m_state); | ||||
|         u8g2.sendBuffer(); | ||||
|         uint16_t screenTime = millis() - m_screenStartTime; | ||||
|         if (screenTime >= 7000 && m_state != Idle) { | ||||
|           m_nextState = Idle; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     static void redrawTask(void* data) { | ||||
|       u8g2.begin(); | ||||
|       U8Display* self = static_cast<U8Display*>(data); | ||||
|       self->m_screenStartTime = millis(); | ||||
|       while (true) { | ||||
|         self->redraw(); | ||||
|         ulTaskNotifyTake(0, portMAX_DELAY); | ||||
|       } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| STATIC_ALLOC(U8Display); | ||||
| STATIC_TASK(U8Display); | ||||
							
								
								
									
										42
									
								
								src/platform/arduino/WiFiTask.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/platform/arduino/WiFiTask.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #include <Input.h> | ||||
| #include <ArduinoLog.h> | ||||
|  | ||||
| #ifdef BOARD_ESP8266 | ||||
| #include <ESP8266WiFi.h> | ||||
| #endif | ||||
| #ifdef BOARD_ESP32 | ||||
| #include <WiFi.h> | ||||
| #endif | ||||
| #include "Static.h" | ||||
| #include "WiFiTask.h" | ||||
|  | ||||
| WiFiTask::WiFiTask() : InputSource("WiFi"), m_lastStatus(WL_IDLE_STATUS) {} | ||||
|  | ||||
| void | ||||
| WiFiTask::onStart() | ||||
| { | ||||
|   Log.notice("Starting wifi..."); | ||||
|   WiFi.mode(WIFI_STA); | ||||
|   WiFi.begin("The Frequency", "thepasswordkenneth"); | ||||
| } | ||||
|  | ||||
| InputEvent | ||||
| WiFiTask::read() | ||||
| { | ||||
|     uint8_t curStatus = WiFi.status(); | ||||
|     if (m_lastStatus != curStatus) { | ||||
|         m_lastStatus = curStatus; | ||||
|         Log.verbose("WiFi Status: %d", curStatus); | ||||
|         if (curStatus == WL_CONNECTED) { | ||||
|             Log.notice("Connected! IP address is %s", WiFi.localIP().toString().c_str()); | ||||
|             return InputEvent{InputEvent::NetworkStatus, true}; | ||||
|         } else if (curStatus == WL_CONNECTION_LOST || curStatus == WL_DISCONNECTED) { | ||||
|             Log.notice("Lost wifi connection!"); | ||||
|             return InputEvent{InputEvent::NetworkStatus, false}; | ||||
|         } | ||||
|     } | ||||
|     return InputEvent{}; | ||||
| } | ||||
|  | ||||
| STATIC_ALLOC(WiFiTask); | ||||
| STATIC_TASK(WiFiTask); | ||||
							
								
								
									
										11
									
								
								src/platform/arduino/WiFiTask.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/platform/arduino/WiFiTask.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #include <Input.h> | ||||
| #include "Static.h" | ||||
|  | ||||
| class WiFiTask : public InputSource { | ||||
|   public: | ||||
|     WiFiTask(); | ||||
|     void onStart() override; | ||||
|     InputEvent read() override; | ||||
|   private: | ||||
|     uint8_t m_lastStatus; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user