cleanup main.cpp, split platform code into a Platform object

This commit is contained in:
2021-03-31 11:50:00 -07:00
parent a6534bcb20
commit 10bbcd6786
21 changed files with 768 additions and 408 deletions

View File

@@ -0,0 +1,230 @@
#include "MQTTTelemetry.h"
#ifdef BOARD_ESP8266
#include <ESP8266WiFi.h>
#elif defined(BOARD_ESP32)
#include <WiFi.h>
#endif
#include <ArduinoJson.h>
#include "../../Static.h"
#include "../../Config.h"
#include "../../Platform.h"
WiFiClient wifiClient;
MQTTTelemetry::MQTTTelemetry() : BufferedInputSource("MQTT"),
m_mqtt(PubSubClient(wifiClient)),
m_logPrinter(this)
{}
void
MQTTTelemetry::handleEventOnline(const InputEvent& evt)
{
if (!m_mqtt.connected()) {
Log.notice("Connecting to MQTT...");
const IPAddress server(10, 0, 0, 2);
const char* deviceID = Platform::deviceID();
Log.verbose("Device ID %s", deviceID);
m_mqtt.setServer(server, 1883);
m_mqtt.setBufferSize(512);
m_mqtt.setCallback(&MQTTTelemetry::s_callback);
if (m_mqtt.connect(deviceID)) {
Log.notice("Connected to MQTT");
m_needHeartbeat = true;
const String deviceName = String("Renderbug ESP8266") + (char*)deviceID;
const String rootTopic = String("homeassistant/light/renderbug/") + (char*)deviceID;
const String configTopic = rootTopic + "/config";
Log.verbose("root topic %s", rootTopic.c_str());
Log.verbose("config topic %s", configTopic.c_str());
const String statTopic = rootTopic + "/state";
const String cmdTopic = rootTopic + "/set";
const String attrTopic = rootTopic + "/attributes";
const String logTopic = rootTopic + "/log";
const String heartbeatTopic = rootTopic + "/heartbeat";
strcpy(m_statTopic, statTopic.c_str());
strcpy(m_attrTopic, attrTopic.c_str());
strcpy(m_cmdTopic, cmdTopic.c_str());
strcpy(m_logTopic, logTopic.c_str());
strcpy(m_heartbeatTopic, heartbeatTopic.c_str());
StaticJsonDocument<1024> configJson;
configJson["~"] = rootTopic;
configJson["name"] = deviceName;
configJson["ret"] = true;
configJson["unique_id"] = (char*) deviceID;
configJson["cmd_t"] = "~/set";
configJson["stat_t"] = "~/state";
configJson["json_attr_t"] = "~/attributes";
configJson["schema"] = "json";
configJson["brightness"] = true;
configJson["rgb"] = true;
int i = 0;
for(const Sequencer::Scene& scene : m_sequencer->scenes()) {
configJson["fx_list"][i++] = scene.name;
}
configJson["dev"]["name"] = "Renderbug";
#ifdef PLATFORM_PHOTON
configJson["dev"]["mdl"] = "Photon";
#elif defined(BOARD_ESP32)
configJson["dev"]["mdl"] = "ESP32";
#elif defined(BOARD_ESP8266)
configJson["dev"]["mdl"] = "ESP8266";
#else
configJson["dev"]["mdl"] = "Unknown";
#endif
configJson["dev"]["sw"] = RENDERBUG_VERSION;
configJson["dev"]["mf"] = "Phong Robotics";
configJson["dev"]["ids"][0] = (char*)deviceID;
char buf[1024];
serializeJson(configJson, buf, sizeof(buf));
Log.verbose("Publish %s %s", configTopic.c_str(), buf);
m_mqtt.publish(configTopic.c_str(), buf);
m_mqtt.subscribe(m_cmdTopic);
} else {
Log.warning("Could not connect to MQTT");
}
} else {
if (evt.intent == InputEvent::SetPower) {
StaticJsonDocument<256> doc;
char buf[256];
doc["state"] = evt.asInt() ? "ON" : "OFF";
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} else if (evt.intent == InputEvent::SetBrightness) {
StaticJsonDocument<256> doc;
char buf[256];
doc["brightness"] = evt.asInt();
doc["state"] = "ON";
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} 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"] = "ON";
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} else if (evt.intent == InputEvent::SetPattern) {
StaticJsonDocument<256> doc;
char buf[256];
doc["effect"] = evt.asString();
doc["state"] = "ON";
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} else if (evt.intent == InputEvent::FirmwareUpdate) {
m_mqtt.publish("renderbug/debug/firmware", "firmware update!");
}
}
}
void
MQTTTelemetry::loop()
{
BufferedInputSource::loop();
OnlineTaskMixin::loop();
}
void
MQTTTelemetry::loopOnline()
{
m_mqtt.loop();
EVERY_N_SECONDS(10) {
m_needHeartbeat = true;
}
if (m_needHeartbeat) {
char buf[512];
StaticJsonDocument<512> response;
response["fps"] = FastLED.getFPS();
response["RSSI"] = WiFi.RSSI();
response["localip"] = WiFi.localIP().toString();
response["free_ram"] = ESP.getFreeHeap();
response["os_version"] = ESP.getSdkVersion();
response["sketch_version"] = ESP.getSketchMD5();
serializeJson(response, buf, sizeof(buf));
m_mqtt.publish(m_attrTopic, buf);
m_mqtt.publish(m_heartbeatTopic, buf);
Log.notice("Heartbeat: %s", buf);
response.clear();
auto sched = MainLoop::instance()->scheduler;
for(auto task : sched.tasks) {
response[task->name] = task->state == Task::Running;
}
serializeJson(response, buf, sizeof(buf));
m_mqtt.publish(m_heartbeatTopic, buf);
m_needHeartbeat = false;
}
}
void
MQTTTelemetry::callback(char* topic, byte* payload, unsigned int length)
{
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload, length);
if (doc.containsKey("state")) {
if (doc["state"] == "ON") {
setEvent(InputEvent{InputEvent::SetPower, true});
} else if (doc["state"] == "OFF") {
setEvent(InputEvent{InputEvent::SetPower, false});
}
}
if (doc.containsKey("start")) {
strcpy(m_patternBuf, doc["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.");
} else {
strcpy(m_patternBuf, doc["stop"].as<const char*>());
setEvent(InputEvent{InputEvent::StopThing, m_patternBuf});
}
}
if (doc.containsKey("pixelCount")) {
setEvent(InputEvent{InputEvent::SetDisplayLength, (int)doc["pixelCount"]});
}
if (doc.containsKey("startPixel")) {
setEvent(InputEvent{InputEvent::SetDisplayOffset, (int)doc["startPixel"]});
}
if (doc.containsKey("save")) {
setEvent(InputEvent{InputEvent::SaveConfigurationRequest});
}
if (doc.containsKey("effect")) {
strcpy(m_patternBuf, doc["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"];
setEvent(InputEvent{InputEvent::SetColor, CRGB(r, g, b)});
}
if (doc.containsKey("brightness")) {
setEvent(InputEvent{InputEvent::SetBrightness, (int)doc["brightness"]});
}
}
void
MQTTTelemetry::s_callback(char* topic, byte* payload, unsigned int length)
{
Static<MQTTTelemetry>::instance()->callback(topic, payload, length);
}
STATIC_ALLOC(MQTTTelemetry);

View File

@@ -1,163 +1,58 @@
#include <Input.h>
#include <Figments.h>
#include <Ringbuf.h>
#include <PubSubClient.h>
#include <ArduinoLog.h>
#include <ArduinoJson.h>
#include <ArduinoUniqueID.h>
WiFiClient wifi;
PubSubClient m_mqtt(wifi);
#include "../../Sequencer.h"
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
public:
MQTTTelemetry() : BufferedInputSource("MQTT") {}
MQTTTelemetry();
void setSequencer(Sequencer* seq) { m_sequencer = seq; }
void handleEventOnline(const InputEvent& evt) override {
if (!m_mqtt.connected()) {
Log.notice("Connecting to MQTT...");
const IPAddress server(10, 0, 0, 2);
uint64_t chipid = ESP.getEfuseMac();
char deviceID[15];
snprintf(deviceID, 15, "%08X", (uint32_t)chipid);
Log.verbose("Device ID %s", deviceID);
m_mqtt.setServer(server, 1883);
m_mqtt.setBufferSize(512);
m_mqtt.setCallback(&MQTTTelemetry::s_callback);
if (m_mqtt.connect(deviceID)) {
Log.notice("Connected to MQTT");
const String deviceName = String("Renderbug ESP32 ") + (char*)deviceID;
const String rootTopic = String("homeassistant/light/renderbug/") + (char*)deviceID;
const String configTopic = rootTopic + "/config";
Log.verbose("root topic %s", rootTopic.c_str());
Log.verbose("config topic %s", configTopic.c_str());
const String statTopic = rootTopic + "/state";
const String cmdTopic = rootTopic + "/set";
const String attrTopic = rootTopic + "/attributes";
strcpy(m_statTopic, statTopic.c_str());
strcpy(m_attrTopic, attrTopic.c_str());
strcpy(m_cmdTopic, cmdTopic.c_str());
StaticJsonDocument<1024> configJson;
configJson["~"] = rootTopic;
configJson["name"] = deviceName;
configJson["ret"] = true;
configJson["unique_id"] = (char*) deviceID;
configJson["cmd_t"] = "~/set";
configJson["stat_t"] = "~/state";
configJson["json_attr_t"] = "~/attributes";
configJson["schema"] = "json";
configJson["brightness"] = true;
configJson["rgb"] = true;
configJson["dev"]["name"] = "Renderbug";
#ifdef PLATFORM_PHOTON
configJson["dev"]["mdl"] = "Photon";
#else
configJson["dev"]["mdl"] = "ESP32";
#endif
configJson["dev"]["sw"] = RENDERBUG_VERSION;
configJson["dev"]["mf"] = "Phong Robotics";
configJson["dev"]["ids"][0] = (char*)deviceID;
char buf[1024];
serializeJson(configJson, buf, sizeof(buf));
Log.verbose("Publish %s %s", configTopic.c_str(), buf);
m_mqtt.publish(configTopic.c_str(), buf);
m_mqtt.subscribe(m_cmdTopic);
} else {
Log.warning("Could not connect to MQTT");
class LogPrinter : public Print {
private:
MQTTTelemetry* telemetry;
Ringbuf<char, 512> buf;
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;
telemetry->m_mqtt.publish(telemetry->m_logTopic, outBuf);
} else {
buf.insert(byte);
}
return sizeof(byte);
}
} else {
if (evt.intent == InputEvent::SetPower) {
StaticJsonDocument<256> doc;
char buf[256];
doc["state"] = evt.asInt() ? "ON" : "OFF";
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} else if (evt.intent == InputEvent::SetBrightness) {
StaticJsonDocument<256> doc;
char buf[256];
doc["brightness"] = evt.asInt();
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} 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;
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
} else if (evt.intent == InputEvent::SetPattern) {
StaticJsonDocument<256> doc;
char buf[256];
doc["effect"] = evt.asString();
serializeJson(doc, buf, sizeof(buf));
m_mqtt.publish(m_statTopic, buf);
}
}
};
Print* logPrinter() {
return &m_logPrinter;
}
void loop() override {
BufferedInputSource::loop();
OnlineTaskMixin::loop();
}
void handleEventOnline(const InputEvent& evt) override;
void loop() override;
void loopOnline() override {
m_mqtt.loop();
EVERY_N_SECONDS(10) {
char buf[254];
StaticJsonDocument<200> response;
response["fps"] = FastLED.getFPS();
response["RSSI"] = WiFi.RSSI();
response["localip"] = WiFi.localIP().toString();
response["free_ram"] = ESP.getFreeHeap();
response["os_version"] = ESP.getSdkVersion();
response["sketch_version"] = ESP.getSketchMD5();
serializeJson(response, buf, sizeof(buf));
m_mqtt.publish(m_attrTopic, buf);
}
}
void loopOnline() override;
private:
char m_statTopic[100];
char m_attrTopic[100];
char m_cmdTopic[100];
char m_logTopic[100];
char m_heartbeatTopic[100];
void callback(char* topic, byte* payload, unsigned int length) {
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload, length);
if (doc.containsKey("state")) {
if (doc["state"] == "ON") {
setEvent(InputEvent{InputEvent::SetPower, true});
} else if (doc["state"] == "OFF") {
setEvent(InputEvent{InputEvent::SetPower, false});
}
}
if (doc.containsKey("effect")) {
strcpy(m_patternBuf, doc["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"];
setEvent(InputEvent{InputEvent::SetColor, CRGB(r, g, b)});
}
if (doc.containsKey("brightness")) {
setEvent(InputEvent{InputEvent::SetBrightness, (int)doc["brightness"]});
}
}
static void s_callback(char* topic, byte* payload, unsigned int length) {
Static<MQTTTelemetry>::instance()->callback(topic, payload, length);
}
void callback(char* topic, byte* payload, unsigned int length);
static void s_callback(char* topic, byte* payload, unsigned int length);
char m_patternBuf[48];
};
bool m_needHeartbeat = false;
STATIC_ALLOC(MQTTTelemetry);
Sequencer *m_sequencer = 0;
PubSubClient m_mqtt;
LogPrinter m_logPrinter;
};