cleanup main.cpp, split platform code into a Platform object
This commit is contained in:
parent
a6534bcb20
commit
10bbcd6786
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
bin/*
|
bin/*
|
||||||
*.bin
|
*.bin
|
||||||
.pio
|
.pio
|
||||||
|
*.swp
|
||||||
|
@ -163,7 +163,7 @@ class OnlineTaskMixin : public virtual Loopable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void handleEventOnline(const InputEvent &evt) = 0;
|
virtual void handleEventOnline(const InputEvent &evt) {}
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
if (m_online) {
|
if (m_online) {
|
||||||
@ -171,7 +171,7 @@ class OnlineTaskMixin : public virtual Loopable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void loopOnline() = 0;
|
virtual void loopOnline() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_online = false;
|
bool m_online = false;
|
||||||
|
@ -31,6 +31,16 @@ struct Ringbuf {
|
|||||||
}
|
}
|
||||||
m_items[cur] = src;
|
m_items[cur] = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t write(T(&dest)[Size]) {
|
||||||
|
int i = 0;
|
||||||
|
size_t ret = 0;
|
||||||
|
while(take(dest[i])) {
|
||||||
|
i++;
|
||||||
|
ret += sizeof(T);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
int m_head = 0;
|
int m_head = 0;
|
||||||
int m_tail = 0;
|
int m_tail = 0;
|
||||||
|
@ -8,17 +8,37 @@
|
|||||||
; Please visit documentation for the other options and examples
|
; Please visit documentation for the other options and examples
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[common_env_data]
|
||||||
|
src_filter = "+<*> -<.git/> -<.svn/> -<platform/>"
|
||||||
|
|
||||||
[env:featheresp32]
|
[env:featheresp32]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = featheresp32
|
board = featheresp32
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
build_flags =
|
||||||
|
-D PLATFORM_ARDUINO
|
||||||
|
-D BOARD_ESP32
|
||||||
lib_deps =
|
lib_deps =
|
||||||
fastled/FastLED@^3.4.0
|
fastled/FastLED@^3.4.0
|
||||||
thijse/ArduinoLog@^1.0.3
|
thijse/ArduinoLog@^1.0.3
|
||||||
knolleary/PubSubClient@^2.8.0
|
knolleary/PubSubClient@^2.8.0
|
||||||
bblanchon/ArduinoJson@^6.17.3
|
bblanchon/ArduinoJson@^6.17.3
|
||||||
ricaun/ArduinoUniqueID@^1.1.0
|
|
||||||
sstaub/NTP@^1.4.0
|
sstaub/NTP@^1.4.0
|
||||||
src_filter = "+<*> -<.git/> -<.svn/> -<platform/> +<platform/arduino/>"
|
arduino-libraries/NTPClient@^3.1.0
|
||||||
; upload_protocol = espota
|
src_filter = "${common_env_data.src_filter} +<platform/arduino/>"
|
||||||
; upload_port = 10.0.0.171
|
|
||||||
|
[env:huzzah]
|
||||||
|
platform = espressif8266
|
||||||
|
board = huzzah
|
||||||
|
framework = arduino
|
||||||
|
build_flags =
|
||||||
|
-D PLATFORM_ARDUINO
|
||||||
|
-D BOARD_ESP8266
|
||||||
|
lib_deps =
|
||||||
|
fastled/FastLED@^3.4.0
|
||||||
|
thijse/ArduinoLog@^1.0.3
|
||||||
|
knolleary/PubSubClient@^2.8.0
|
||||||
|
bblanchon/ArduinoJson@^6.17.3
|
||||||
|
sstaub/NTP@^1.4.0
|
||||||
|
arduino-libraries/NTPClient@^3.1.0
|
||||||
|
src_filter = "${common_env_data.src_filter} +<platform/arduino/>"
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
#include "./Config.h"
|
#include "./Config.h"
|
||||||
#include "./Static.h"
|
#include "./Static.h"
|
||||||
#include <ArduinoLog.h>
|
#include <ArduinoLog.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
||||||
|
|
||||||
HardwareConfig
|
HardwareConfig
|
||||||
HardwareConfig::load() {
|
HardwareConfig::load() {
|
||||||
HardwareConfig ret;
|
HardwareConfig ret;
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
EEPROM.get(0, ret);
|
EEPROM.get(0, ret);
|
||||||
#endif
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,9 +16,7 @@ void
|
|||||||
HardwareConfig::save() {
|
HardwareConfig::save() {
|
||||||
HardwareConfig dataCopy{*this};
|
HardwareConfig dataCopy{*this};
|
||||||
dataCopy.checksum = getCRC();
|
dataCopy.checksum = getCRC();
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
EEPROM.put(0, dataCopy);
|
EEPROM.put(0, dataCopy);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearCoordinateMapping
|
LinearCoordinateMapping
|
||||||
@ -103,6 +100,8 @@ ConfigService::handleEvent(const InputEvent &evt)
|
|||||||
//Log.info("Saving configuration");
|
//Log.info("Saving configuration");
|
||||||
m_config.save();
|
m_config.save();
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
#include <Figments.h>
|
#include <Figments.h>
|
||||||
|
|
||||||
//#define PLATFORM_PHOTON
|
//#define PLATFORM_PHOTON
|
||||||
#define PLATFORM_ARDUINO
|
//#define PLATFORM_ARDUINO
|
||||||
|
#define RENDERBUG_VERSION 2
|
||||||
|
|
||||||
struct HardwareConfig {
|
struct HardwareConfig {
|
||||||
uint8_t version = 2;
|
uint8_t version = 2;
|
||||||
@ -29,7 +30,11 @@ struct HardwareConfig {
|
|||||||
LinearCoordinateMapping toCoordMap() const;
|
LinearCoordinateMapping toCoordMap() const;
|
||||||
|
|
||||||
static constexpr uint16_t MAX_LED_NUM = 255;
|
static constexpr uint16_t MAX_LED_NUM = 255;
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
static constexpr bool HAS_MPU_6050 = true;
|
||||||
|
#else
|
||||||
static constexpr bool HAS_MPU_6050 = false;
|
static constexpr bool HAS_MPU_6050 = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t getCRC() const;
|
uint8_t getCRC() const;
|
||||||
|
100
src/LogService.cpp
Normal file
100
src/LogService.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "LogService.h"
|
||||||
|
#include "Static.h"
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
LogService::handleEvent(const InputEvent& evt) {
|
||||||
|
if (evt.intent != InputEvent::None) {
|
||||||
|
const char* sourceName;
|
||||||
|
switch(evt.intent) {
|
||||||
|
case InputEvent::PowerToggle:
|
||||||
|
sourceName = "power-toggle";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetPower:
|
||||||
|
sourceName = "set-power";
|
||||||
|
break;
|
||||||
|
case InputEvent::PreviousPattern:
|
||||||
|
sourceName = "previous-pattern";
|
||||||
|
break;
|
||||||
|
case InputEvent::NextPattern:
|
||||||
|
sourceName = "next-pattern";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetPattern:
|
||||||
|
sourceName = "set-pattern";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetColor:
|
||||||
|
sourceName = "set-color";
|
||||||
|
break;
|
||||||
|
case InputEvent::Acceleration:
|
||||||
|
sourceName = "acceleration";
|
||||||
|
break;
|
||||||
|
case InputEvent::UserInput:
|
||||||
|
sourceName = "user";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetBrightness:
|
||||||
|
sourceName = "set-brightness";
|
||||||
|
break;
|
||||||
|
case InputEvent::FirmwareUpdate:
|
||||||
|
sourceName = "firmware-update";
|
||||||
|
break;
|
||||||
|
case InputEvent::NetworkStatus:
|
||||||
|
sourceName = "network-status";
|
||||||
|
break;
|
||||||
|
case InputEvent::NetworkActivity:
|
||||||
|
sourceName = "network-activity";
|
||||||
|
break;
|
||||||
|
case InputEvent::StartThing:
|
||||||
|
sourceName = "start-thing";
|
||||||
|
break;
|
||||||
|
case InputEvent::StopThing:
|
||||||
|
sourceName = "stop-thing";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetDisplayOffset:
|
||||||
|
sourceName = "set-display-offset";
|
||||||
|
break;
|
||||||
|
case InputEvent::SetDisplayLength:
|
||||||
|
sourceName = "set-display-length";
|
||||||
|
break;
|
||||||
|
case InputEvent::SaveConfigurationRequest:
|
||||||
|
sourceName = "save-configuration";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sourceName = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char valueBuf[255];
|
||||||
|
switch(evt.type) {
|
||||||
|
case InputEvent::Null:
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "null");break;
|
||||||
|
case InputEvent::Integer:
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "%d %02x", evt.asInt(), evt.asInt());break;
|
||||||
|
case InputEvent::String:
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "\"%s\"", evt.asString());break;
|
||||||
|
case InputEvent::Color:
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "[%d, %d, %d]", evt.asRGB().r, evt.asRGB().g, evt.asRGB().b);break;
|
||||||
|
}
|
||||||
|
char buf[255 * 2];
|
||||||
|
if (sourceName == 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "{\"intent\": %d, \"value\": %s}", evt.intent, valueBuf);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "{\"intent\": \"%s\", \"value\": %s}", sourceName, valueBuf);
|
||||||
|
}
|
||||||
|
if (evt.intent != m_lastEvent.intent) {
|
||||||
|
if (m_duplicateEvents > 0) {
|
||||||
|
Log.notice("Suppressed reporting %u duplicate events.", m_duplicateEvents);
|
||||||
|
}
|
||||||
|
Log.verbose("Event: %s", buf);
|
||||||
|
m_duplicateEvents = 0;
|
||||||
|
m_lastEvent = evt;
|
||||||
|
//Particle.publish("renderbug/event", buf, PRIVATE);
|
||||||
|
} else {
|
||||||
|
m_duplicateEvents++;
|
||||||
|
}
|
||||||
|
/*if (m_online) {
|
||||||
|
} else {
|
||||||
|
Log.info("[offline] Event: %s", buf);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(LogService);
|
12
src/LogService.h
Normal file
12
src/LogService.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <Figments.h>
|
||||||
|
|
||||||
|
class LogService : public Task {
|
||||||
|
public:
|
||||||
|
LogService() : Task("Logging") {}
|
||||||
|
void handleEvent(const InputEvent& event) override;
|
||||||
|
void loop() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t m_duplicateEvents = 0;
|
||||||
|
InputEvent m_lastEvent;
|
||||||
|
};
|
139
src/Platform.cpp
Normal file
139
src/Platform.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "Platform.h"
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
#include "Static.h"
|
||||||
|
|
||||||
|
#ifdef BOARD_ESP32
|
||||||
|
#include <WiFi.h>
|
||||||
|
#elif defined(BOARD_ESP8266)
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <NTPClient.h>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
WiFiUDP wifiUdp;
|
||||||
|
NTPClient timeClient(wifiUdp, "pool.ntp.org", 3600 * -7);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
STARTUP(BootOptions::initPins());
|
||||||
|
#else
|
||||||
|
#include "platform/arduino/MQTTTelemetry.h"
|
||||||
|
void printNewline(Print* logOutput) {
|
||||||
|
logOutput->print("\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int Platform::s_timezone = 0;
|
||||||
|
|
||||||
|
const char*
|
||||||
|
Platform::name()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
return "Photon";
|
||||||
|
#else
|
||||||
|
return "Unknown!";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
Platform::version()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
return System.version().c_str();
|
||||||
|
#else
|
||||||
|
return "Unknown!";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Platform::preSetup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
||||||
|
if (bootopts.isFlash) {
|
||||||
|
System.dfu();
|
||||||
|
}
|
||||||
|
if (bootopts.isSerial) {
|
||||||
|
bootopts.waitForRelease();
|
||||||
|
while(!Serial.isConnected()) {
|
||||||
|
Particle.process();
|
||||||
|
}
|
||||||
|
Log.notice("\xf0\x9f\x94\x8c Serial connected");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Log.begin(LOG_LEVEL_VERBOSE, Static<MQTTTelemetry>::instance()->logPrinter());
|
||||||
|
Log.setSuffix(printNewline);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Platform::setup()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
Time.zone(Static<Platform>::instance()->getTimezone());
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
constexpr int dst = 1;
|
||||||
|
configTime(s_timezone* 3600, 3600 * dst, "pool.ntp.org");
|
||||||
|
#elif defined(BOARD_ESP8266)
|
||||||
|
timeClient.begin();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Platform::bootSplash()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
Log.notice(u8" Boot pin configuration:");
|
||||||
|
Log.notice(u8" 2: Setup - %d", bootopts.isSetup);
|
||||||
|
Log.notice(u8" 3: Serial - %d", bootopts.isSerial);
|
||||||
|
Log.notice(u8" 4: Flash - %d", bootopts.isFlash);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Platform::loop()
|
||||||
|
{
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
timeClient.update();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Platform::getLocalTime(struct tm* timedata)
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
if (Time.isValid()) {
|
||||||
|
timedata->tm_hour = Time.hour();
|
||||||
|
timedata->tm_min = Time.minute();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
return getLocalTime(timedata);
|
||||||
|
#else
|
||||||
|
timeClient.update();
|
||||||
|
timedata->tm_hour = timeClient.getHours();
|
||||||
|
timedata->tm_min = timeClient.getMinutes();
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
Platform::deviceID()
|
||||||
|
{
|
||||||
|
uint64_t chipid;
|
||||||
|
#ifdef BOARD_ESP32
|
||||||
|
chipid = ESP.getEfuseMac();
|
||||||
|
#elif defined(BOARD_ESP8266)
|
||||||
|
chipid = ESP.getChipId();
|
||||||
|
#endif
|
||||||
|
snprintf(s_deviceID, sizeof(s_deviceID), "%08X", (uint32_t)chipid);
|
||||||
|
return s_deviceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
BootOptions
|
||||||
|
Platform::bootopts;
|
||||||
|
|
||||||
|
char
|
||||||
|
Platform::s_deviceID[15];
|
31
src/Platform.h
Normal file
31
src/Platform.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <FastLED.h>
|
||||||
|
#include "BootOptions.h"
|
||||||
|
|
||||||
|
class Platform {
|
||||||
|
static int s_timezone;
|
||||||
|
static char s_deviceID[15];
|
||||||
|
public:
|
||||||
|
static BootOptions bootopts;
|
||||||
|
static void setTimezone(int tz) { s_timezone = tz; }
|
||||||
|
static int getTimezone() { return s_timezone; }
|
||||||
|
|
||||||
|
static void addLEDs(CRGB* leds, unsigned int ledCount) {
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
FastLED.addLeds<NEOPIXEL, 6>(leds, ledCount);
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
FastLED.addLeds<WS2812B, 13, RGB>(leds, ledCount);
|
||||||
|
#else
|
||||||
|
FastLED.addLeds<WS2812B, 14, GRB>(leds, ledCount);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* name();
|
||||||
|
static const char* version();
|
||||||
|
static void preSetup();
|
||||||
|
static void bootSplash();
|
||||||
|
static void setup();
|
||||||
|
static void loop();
|
||||||
|
static bool getLocalTime(struct tm* timedata);
|
||||||
|
static const char* deviceID();
|
||||||
|
};
|
@ -1,10 +1,16 @@
|
|||||||
#include <Input.h>
|
#include <Input.h>
|
||||||
#include <ArduinoLog.h>
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#endif
|
||||||
|
#ifdef BOARD_ESP32
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#endif
|
||||||
#include "Static.h"
|
#include "Static.h"
|
||||||
#include "WiFiTask.h"
|
#include "WiFiTask.h"
|
||||||
|
|
||||||
WiFiTask::WiFiTask() : InputSource("WiFi") {}
|
WiFiTask::WiFiTask() : InputSource("WiFi"), m_lastStatus(WL_IDLE_STATUS) {}
|
||||||
|
|
||||||
void
|
void
|
||||||
WiFiTask::onStart()
|
WiFiTask::onStart()
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include <Input.h>
|
#include <Input.h>
|
||||||
#include <WiFi.h>
|
|
||||||
#include "Static.h"
|
#include "Static.h"
|
||||||
|
|
||||||
class WiFiTask : public InputSource {
|
class WiFiTask : public InputSource {
|
||||||
@ -8,5 +7,5 @@ class WiFiTask : public InputSource {
|
|||||||
void onStart() override;
|
void onStart() override;
|
||||||
InputEvent read() override;
|
InputEvent read() override;
|
||||||
private:
|
private:
|
||||||
uint8_t m_lastStatus = WL_IDLE_STATUS;
|
uint8_t m_lastStatus;
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,8 @@ public:
|
|||||||
break;
|
break;
|
||||||
case InputEvent::SetBrightness:
|
case InputEvent::SetBrightness:
|
||||||
m_brightness = evt.asInt();
|
m_brightness = evt.asInt();
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/firmware
Symbolic link
1
src/firmware
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
firmware
|
@ -1,11 +1,13 @@
|
|||||||
#include "./Buttons.h"
|
#include "./Buttons.h"
|
||||||
|
#include "../Static.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
Buttons::onStart()
|
Buttons::onStart()
|
||||||
{
|
{
|
||||||
for(int i = 0; i < 3; i++) {
|
for(int i = 0; i < 3; i++) {
|
||||||
//Log.info("Bound pin %d to button %d", 2 + i, i);
|
//Log.info("Bound pin %d to button %d", 2 + i, i);
|
||||||
m_buttons[i].attach(2 + i, INPUT_PULLDOWN);
|
//m_buttons[i].attach(2 + i, INPUT_PULLDOWN);
|
||||||
|
m_buttons[i].attach(2 + i, INPUT_PULLUP);
|
||||||
m_buttons[i].interval(15);
|
m_buttons[i].interval(15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,3 +37,5 @@ Buttons::read()
|
|||||||
}
|
}
|
||||||
return InputEvent{};
|
return InputEvent{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(Buttons);
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
#include "MPU6050.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "../Config.h"
|
||||||
|
#include "../Static.h"
|
||||||
|
|
||||||
|
MPU5060::MPU5060() : InputSource("MPU5060") {}
|
||||||
|
|
||||||
|
void
|
||||||
|
MPU5060::onStart()
|
||||||
|
{
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
// Turn on the sensor
|
||||||
|
Wire.beginTransmission(I2C_ADDRESS);
|
||||||
|
Wire.write(PWR_MGMT_1);
|
||||||
|
Wire.write(0);
|
||||||
|
Wire.endTransmission(true);
|
||||||
|
|
||||||
|
// Configure the filter
|
||||||
|
Wire.beginTransmission(I2C_ADDRESS);
|
||||||
|
Wire.write(CONFIG_REG);
|
||||||
|
Wire.write(3);
|
||||||
|
Wire.endTransmission(true);
|
||||||
|
|
||||||
|
// Configure the accel range
|
||||||
|
Wire.beginTransmission(I2C_ADDRESS);
|
||||||
|
Wire.write(ACCEL_CONFIG_REG);
|
||||||
|
// 4G
|
||||||
|
Wire.write(2 << 3);
|
||||||
|
Wire.endTransmission(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MPU5060::onStop()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(I2C_ADDRESS);
|
||||||
|
// Turn off the sensor
|
||||||
|
Wire.write(PWR_MGMT_1);
|
||||||
|
Wire.write(1);
|
||||||
|
Wire.endTransmission(true);
|
||||||
|
//Wire.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputEvent
|
||||||
|
MPU5060::read()
|
||||||
|
{
|
||||||
|
EVERY_N_MILLISECONDS(5) {
|
||||||
|
Wire.beginTransmission(I2C_ADDRESS);
|
||||||
|
Wire.write(ACCEL_XOUT_HIGH);
|
||||||
|
Wire.endTransmission(false);
|
||||||
|
Wire.requestFrom(I2C_ADDRESS, 6);
|
||||||
|
const int16_t accelX = Wire.read() << 8 | Wire.read();
|
||||||
|
const int16_t accelY = Wire.read() << 8 | Wire.read();
|
||||||
|
const int16_t accelZ = Wire.read() << 8 | Wire.read();
|
||||||
|
const uint16_t accelSum = abs(accelX) + abs(accelY) + abs(accelZ);
|
||||||
|
const uint16_t delta = abs(m_value.value() - accelSum);
|
||||||
|
m_value.add(accelSum);
|
||||||
|
if (delta > 32) {
|
||||||
|
return InputEvent{InputEvent::Acceleration, delta};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InputEvent{};
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(MPU5060);
|
@ -1,4 +1,4 @@
|
|||||||
#include <Wire.h>
|
#include <Figments.h>
|
||||||
|
|
||||||
class MPU5060: public InputSource {
|
class MPU5060: public InputSource {
|
||||||
const int ACCEL_XOUT_HIGH = 0x3B;
|
const int ACCEL_XOUT_HIGH = 0x3B;
|
||||||
@ -15,57 +15,11 @@ class MPU5060: public InputSource {
|
|||||||
const int ACCEL_CONFIG_REG = 0x1C;
|
const int ACCEL_CONFIG_REG = 0x1C;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MPU5060() : InputSource("MPU5060", HardwareConfig::HAS_MPU_6050 ? Task::Running : Task::Stopped) {}
|
MPU5060();
|
||||||
void onStart() override {
|
|
||||||
Wire.begin();
|
|
||||||
|
|
||||||
// Turn on the sensor
|
void onStart() override;
|
||||||
Wire.beginTransmission(I2C_ADDRESS);
|
void onStop() override;
|
||||||
Wire.write(PWR_MGMT_1);
|
InputEvent read() override;
|
||||||
Wire.write(0);
|
|
||||||
Wire.endTransmission(true);
|
|
||||||
|
|
||||||
// Configure the filter
|
|
||||||
Wire.beginTransmission(I2C_ADDRESS);
|
|
||||||
Wire.write(CONFIG_REG);
|
|
||||||
Wire.write(3);
|
|
||||||
Wire.endTransmission(true);
|
|
||||||
|
|
||||||
// Configure the accel range
|
|
||||||
Wire.beginTransmission(I2C_ADDRESS);
|
|
||||||
Wire.write(ACCEL_CONFIG_REG);
|
|
||||||
// 4G
|
|
||||||
Wire.write(2 << 3);
|
|
||||||
Wire.endTransmission(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onStop() override {
|
|
||||||
Wire.beginTransmission(I2C_ADDRESS);
|
|
||||||
// Turn off the sensor
|
|
||||||
Wire.write(PWR_MGMT_1);
|
|
||||||
Wire.write(1);
|
|
||||||
Wire.endTransmission(true);
|
|
||||||
//Wire.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
InputEvent read() override {
|
|
||||||
EVERY_N_MILLISECONDS(5) {
|
|
||||||
Wire.beginTransmission(I2C_ADDRESS);
|
|
||||||
Wire.write(ACCEL_XOUT_HIGH);
|
|
||||||
Wire.endTransmission(false);
|
|
||||||
Wire.requestFrom(I2C_ADDRESS, 6);
|
|
||||||
const int16_t accelX = Wire.read() << 8 | Wire.read();
|
|
||||||
const int16_t accelY = Wire.read() << 8 | Wire.read();
|
|
||||||
const int16_t accelZ = Wire.read() << 8 | Wire.read();
|
|
||||||
const uint16_t accelSum = abs(accelX) + abs(accelY) + abs(accelZ);
|
|
||||||
const uint16_t delta = abs(m_value.value() - accelSum);
|
|
||||||
m_value.add(accelSum);
|
|
||||||
if (delta > 32) {
|
|
||||||
return InputEvent{InputEvent::Acceleration, delta};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InputEvent{};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, uint8_t Size = 8>
|
template<typename T, uint8_t Size = 8>
|
||||||
struct Averager {
|
struct Averager {
|
||||||
|
169
src/main.cpp
169
src/main.cpp
@ -1,5 +1,3 @@
|
|||||||
#define RENDERBUG_VERSION 1
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
@ -10,13 +8,14 @@
|
|||||||
#include <NTP.h>
|
#include <NTP.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "BootOptions.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
#include "Static.h"
|
#include "Static.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "colors.h"
|
#include "colors.h"
|
||||||
|
|
||||||
#include "Sequencer.h"
|
#include "Sequencer.h"
|
||||||
|
#include "LogService.h"
|
||||||
|
|
||||||
#include "animations/Power.cpp"
|
#include "animations/Power.cpp"
|
||||||
#include "animations/SolidAnimation.cpp"
|
#include "animations/SolidAnimation.cpp"
|
||||||
@ -62,16 +61,6 @@ Display neckDisplay(leds, HardwareConfig::MAX_LED_NUM, &neckMap);
|
|||||||
// Setup power management
|
// Setup power management
|
||||||
Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power;
|
Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power;
|
||||||
|
|
||||||
// Clip the display at whatever is configured while still showing over-paints
|
|
||||||
FigmentFunc displayClip([](Display* dpy) {
|
|
||||||
auto coords = Static<ConfigService>::instance()->coordMap();
|
|
||||||
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
|
|
||||||
if (i < coords->startPixel || i > coords->pixelCount + coords->startPixel) {
|
|
||||||
dpy->pixelAt(i) %= 40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FigmentFunc configDisplay([](Display* dpy) {
|
FigmentFunc configDisplay([](Display* dpy) {
|
||||||
uint8_t brightness = brighten8_video(beatsin8(60));
|
uint8_t brightness = brighten8_video(beatsin8(60));
|
||||||
auto coords = Static<ConfigService>::instance()->coordMap();
|
auto coords = Static<ConfigService>::instance()->coordMap();
|
||||||
@ -112,23 +101,39 @@ InputBlip inputBlip;
|
|||||||
class ArduinoOTAUpdater : public BufferedInputSource {
|
class ArduinoOTAUpdater : public BufferedInputSource {
|
||||||
public:
|
public:
|
||||||
ArduinoOTAUpdater() : BufferedInputSource("ArduinoOTA") {
|
ArduinoOTAUpdater() : BufferedInputSource("ArduinoOTA") {
|
||||||
ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart).onProgress(&ArduinoOTAUpdater::s_onProgress);
|
ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart);
|
||||||
|
ArduinoOTA.onProgress(&ArduinoOTAUpdater::s_onProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
|
if (m_online) {
|
||||||
ArduinoOTA.handle();
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
BufferedInputSource::loop();
|
BufferedInputSource::loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleEvent(const InputEvent& evt) {
|
||||||
|
if (evt.intent == InputEvent::NetworkStatus) {
|
||||||
|
Log.notice("Booting OTA");
|
||||||
|
m_online = true;
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
|
bool m_online = false;
|
||||||
static void s_onStart() {
|
static void s_onStart() {
|
||||||
|
Log.notice("OTA Start!");
|
||||||
Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent::FirmwareUpdate);
|
Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent::FirmwareUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s_onProgress(unsigned int progress, unsigned int total) {
|
static void 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<ArduinoOTAUpdater>::instance()->setEvent(InputEvent{InputEvent::FirmwareUpdate, progress});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
STATIC_ALLOC(ArduinoOTAUpdater);
|
||||||
|
|
||||||
InputFunc randomPulse([]() {
|
InputFunc randomPulse([]() {
|
||||||
static unsigned int pulse = 0;
|
static unsigned int pulse = 0;
|
||||||
EVERY_N_MILLISECONDS(25) {
|
EVERY_N_MILLISECONDS(25) {
|
||||||
@ -158,6 +163,8 @@ InputMapper keyMap([](const InputEvent& evt) {
|
|||||||
case Buttons::Cross:
|
case Buttons::Cross:
|
||||||
return InputEvent::UserInput;
|
return InputEvent::UserInput;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return InputEvent::None;
|
return InputEvent::None;
|
||||||
@ -189,7 +196,6 @@ Renderer renderer{
|
|||||||
&solid,
|
&solid,
|
||||||
&flashlight,
|
&flashlight,
|
||||||
Static<UpdateStatus>::instance(),
|
Static<UpdateStatus>::instance(),
|
||||||
&displayClip,
|
|
||||||
&inputBlip,
|
&inputBlip,
|
||||||
&power,
|
&power,
|
||||||
}
|
}
|
||||||
@ -249,6 +255,8 @@ public:
|
|||||||
//Log.info("Save...");
|
//Log.info("Save...");
|
||||||
setEvent(InputEvent::SaveConfigurationRequest);
|
setEvent(InputEvent::SaveConfigurationRequest);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,6 +273,8 @@ private:
|
|||||||
case InputEvent::SetDisplayOffset:
|
case InputEvent::SetDisplayOffset:
|
||||||
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setEvent(InputEvent{m_currentIntent, current - 1});
|
setEvent(InputEvent{m_currentIntent, current - 1});
|
||||||
}
|
}
|
||||||
@ -278,6 +288,8 @@ private:
|
|||||||
case InputEvent::SetDisplayOffset:
|
case InputEvent::SetDisplayOffset:
|
||||||
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setEvent(InputEvent{m_currentIntent, current + 1});
|
setEvent(InputEvent{m_currentIntent, current + 1});
|
||||||
}
|
}
|
||||||
@ -288,22 +300,12 @@ private:
|
|||||||
return InputEvent::SetDisplayOffset;
|
return InputEvent::SetDisplayOffset;
|
||||||
case InputEvent::SetDisplayOffset:
|
case InputEvent::SetDisplayOffset:
|
||||||
return InputEvent::SetDisplayLength;
|
return InputEvent::SetDisplayLength;
|
||||||
|
default:
|
||||||
|
return InputEvent::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Phase {
|
|
||||||
Null,
|
|
||||||
AstronomicalDay,
|
|
||||||
NauticalDay,
|
|
||||||
CivilDay,
|
|
||||||
CivilNight,
|
|
||||||
NauticalNight,
|
|
||||||
AstronomicalNight,
|
|
||||||
Evening,
|
|
||||||
Bedtime,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScheduleEntry {
|
struct ScheduleEntry {
|
||||||
uint8_t hour;
|
uint8_t hour;
|
||||||
uint8_t brightness;
|
uint8_t brightness;
|
||||||
@ -358,28 +360,32 @@ uint8_t brightnessForTime(uint8_t hour, uint8_t minute) {
|
|||||||
return lerp8by8(start.brightness, end.brightness, frac);
|
return lerp8by8(start.brightness, end.brightness, frac);
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFiUDP wifiUdp;
|
class CircadianRhythm : public InputSource {
|
||||||
NTP ntp(wifiUdp);
|
private:
|
||||||
|
bool needsUpdate = true;
|
||||||
|
public:
|
||||||
|
CircadianRhythm() : InputSource("CircadianRhythm") {}
|
||||||
|
|
||||||
InputFunc circadianRhythm([]() {
|
InputEvent read() {
|
||||||
static bool needsUpdate = true;
|
|
||||||
EVERY_N_SECONDS(60) {
|
EVERY_N_SECONDS(60) {
|
||||||
needsUpdate = true;
|
needsUpdate = true;
|
||||||
}
|
}
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
if (Time.isValid() && needsUpdate) {
|
|
||||||
needsUpdate = false;
|
|
||||||
return InputEvent{InputEvent::SetBrightness, brightnessForTime(Time.hour(), Time.minute())};
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
ntp.update();
|
|
||||||
if (needsUpdate) {
|
if (needsUpdate) {
|
||||||
|
uint8_t hour = 0;
|
||||||
|
uint8_t minute = 0;
|
||||||
needsUpdate = false;
|
needsUpdate = false;
|
||||||
return InputEvent{InputEvent::SetBrightness, brightnessForTime(ntp.hours(), ntp.minutes())};
|
struct tm timeinfo;
|
||||||
|
Platform::getLocalTime(&timeinfo);
|
||||||
|
hour = timeinfo.tm_hour;
|
||||||
|
minute = timeinfo.tm_min;
|
||||||
|
Log.notice("Current time: %d:%d", hour, minute);
|
||||||
|
return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return InputEvent{};
|
return InputEvent{};
|
||||||
}, "CircadianRhythm", Task::Running);
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC_ALLOC(CircadianRhythm);
|
||||||
|
|
||||||
// A special mainloop app for configuring hardware settings that reboots the
|
// A special mainloop app for configuring hardware settings that reboots the
|
||||||
// device when the user is finished.
|
// device when the user is finished.
|
||||||
@ -393,11 +399,14 @@ MainLoop configApp{{
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read hardware inputs
|
// Read hardware inputs
|
||||||
new Buttons(),
|
Static<Buttons>::instance(),
|
||||||
|
|
||||||
// Map input buttons to configuration commands
|
// Map input buttons to configuration commands
|
||||||
new ConfigInputTask(),
|
new ConfigInputTask(),
|
||||||
|
|
||||||
|
// System logging
|
||||||
|
Static<LogService>::instance(),
|
||||||
|
|
||||||
// Fill the entire display with a color, to see size
|
// Fill the entire display with a color, to see size
|
||||||
&configDisplay,
|
&configDisplay,
|
||||||
// Render some basic input feedback
|
// Render some basic input feedback
|
||||||
@ -409,10 +418,11 @@ MainLoop configApp{{
|
|||||||
// Turn on,
|
// Turn on,
|
||||||
MainLoop renderbugApp{{
|
MainLoop renderbugApp{{
|
||||||
|
|
||||||
// Load/update graphics configuration from EEPROM and Particle
|
// Load/update graphics configuration from EEPROM
|
||||||
Static<ConfigService>::instance(),
|
Static<ConfigService>::instance(),
|
||||||
|
|
||||||
// Platform inputs
|
// Platform inputs
|
||||||
|
// TODO: Merge cloud and esp wifi tasks into a common networking base
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
// Particle cloud status
|
// Particle cloud status
|
||||||
Static<CloudStatus>::instance(),
|
Static<CloudStatus>::instance(),
|
||||||
@ -424,9 +434,12 @@ MainLoop renderbugApp{{
|
|||||||
Static<WiFiTask>::instance(),
|
Static<WiFiTask>::instance(),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// System logging
|
||||||
|
Static<LogService>::instance(),
|
||||||
|
|
||||||
// Hardware drivers
|
// Hardware drivers
|
||||||
//new MPU5060(),
|
Static<MPU5060>::instance(),
|
||||||
//new Buttons(),
|
Static<Buttons>::instance(),
|
||||||
|
|
||||||
// Map buttons to events
|
// Map buttons to events
|
||||||
&keyMap,
|
&keyMap,
|
||||||
@ -435,7 +448,7 @@ MainLoop renderbugApp{{
|
|||||||
&sequencer,
|
&sequencer,
|
||||||
|
|
||||||
// Daily rhythm activities
|
// Daily rhythm activities
|
||||||
&circadianRhythm,
|
Static<CircadianRhythm>::instance(),
|
||||||
|
|
||||||
// Periodic motion input
|
// Periodic motion input
|
||||||
&randomPulse,
|
&randomPulse,
|
||||||
@ -454,7 +467,6 @@ MainLoop renderbugApp{{
|
|||||||
|
|
||||||
// Update UI layer
|
// Update UI layer
|
||||||
&power,
|
&power,
|
||||||
&displayClip,
|
|
||||||
Static<UpdateStatus>::instance(),
|
Static<UpdateStatus>::instance(),
|
||||||
&inputBlip,
|
&inputBlip,
|
||||||
|
|
||||||
@ -462,6 +474,9 @@ MainLoop renderbugApp{{
|
|||||||
&renderer,
|
&renderer,
|
||||||
|
|
||||||
// Platform telemetry
|
// Platform telemetry
|
||||||
|
// TODO: Combine some of these services into a unified telemetry API with
|
||||||
|
// platform-specific backends?
|
||||||
|
// Or at least, just the MQTT and watchdog ones.
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
// Update photon telemetry
|
// Update photon telemetry
|
||||||
Static<PhotonTelemetry>::instance(),
|
Static<PhotonTelemetry>::instance(),
|
||||||
@ -480,63 +495,33 @@ MainLoop renderbugApp{{
|
|||||||
#else
|
#else
|
||||||
// MQTT
|
// MQTT
|
||||||
Static<MQTTTelemetry>::instance(),
|
Static<MQTTTelemetry>::instance(),
|
||||||
|
|
||||||
|
// OTA Updates
|
||||||
|
Static<ArduinoOTAUpdater>::instance(),
|
||||||
#endif
|
#endif
|
||||||
}};
|
}};
|
||||||
|
|
||||||
MainLoop &runner = renderbugApp;
|
MainLoop &runner = renderbugApp;
|
||||||
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
STARTUP(BootOptions::initPins());
|
|
||||||
retained BootOptions bootopts;
|
|
||||||
#else
|
|
||||||
BootOptions bootopts;
|
|
||||||
|
|
||||||
void printNewline(Print* logOutput) {
|
|
||||||
logOutput->print("\r\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Tune in,
|
// Tune in,
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Platform::preSetup();
|
||||||
#ifdef PLATFORM_PHOTON
|
Static<MQTTTelemetry>::instance()->setSequencer(&sequencer);
|
||||||
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
|
||||||
if (bootopts.isFlash) {
|
|
||||||
System.dfu();
|
|
||||||
}
|
|
||||||
if (bootopts.isSerial) {
|
|
||||||
bootopts.waitForRelease();
|
|
||||||
while(!Serial.isConnected()) {
|
|
||||||
Particle.process();
|
|
||||||
}
|
|
||||||
//Log.info("\xf0\x9f\x94\x8c Serial connected");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Log.begin(LOG_LEVEL_VERBOSE, &Serial, true);
|
|
||||||
Log.setSuffix(printNewline);
|
|
||||||
Log.notice("Test log?");
|
|
||||||
#endif
|
|
||||||
Log.notice(u8"🐛 Booting Renderbug!");
|
Log.notice(u8"🐛 Booting Renderbug!");
|
||||||
Log.notice(u8"🐞 I am built for %d LEDs running on %dmA", HardwareConfig::MAX_LED_NUM, PSU_MILLIAMPS);
|
Log.notice(u8"🐞 I am built for %d LEDs running on %dmA", HardwareConfig::MAX_LED_NUM, PSU_MILLIAMPS);
|
||||||
#ifdef PLATFORM_PHOTON
|
Log.notice(u8"📡 Platform %s version %s", Platform::name(), Platform::version());
|
||||||
Log.notice(u8"📡 Particle version %s", System.version().c_str());
|
Platform::bootSplash();
|
||||||
#endif
|
|
||||||
Log.notice(u8" Boot pin configuration:");
|
|
||||||
Log.notice(u8" 2: Setup - %d", bootopts.isSetup);
|
|
||||||
Log.notice(u8" 3: Serial - %d", bootopts.isSerial);
|
|
||||||
Log.notice(u8" 4: Flash - %d", bootopts.isFlash);
|
|
||||||
|
|
||||||
//Log.info(u8" Setting timezone to UTC-7");
|
Log.notice(u8"Setting timezone to -7 (PST)");
|
||||||
//Time.zone(-7);
|
Platform::setTimezone(-7);
|
||||||
|
|
||||||
|
Log.notice(u8" Setting up platform...");
|
||||||
|
Platform::setup();
|
||||||
|
|
||||||
Log.notice(u8"💡 Starting FastLED...");
|
Log.notice(u8"💡 Starting FastLED...");
|
||||||
#ifdef PLATFORM_PHOTON
|
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
|
||||||
FastLED.addLeds<NEOPIXEL, 6>(leds, HardwareConfig::MAX_LED_NUM);
|
|
||||||
#else
|
|
||||||
FastLED.addLeds<WS2812B, 13, RGB>(leds, HardwareConfig::MAX_LED_NUM);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (bootopts.isSetup) {
|
if (Platform::bootopts.isSetup) {
|
||||||
Log.notice(u8"🌌 Starting Figment in configuration mode...");
|
Log.notice(u8"🌌 Starting Figment in configuration mode...");
|
||||||
runner = configApp;
|
runner = configApp;
|
||||||
} else {
|
} else {
|
||||||
@ -548,8 +533,6 @@ void setup() {
|
|||||||
//Log.info(u8"💽 %lu bytes of free RAM", System.freeMemory());
|
//Log.info(u8"💽 %lu bytes of free RAM", System.freeMemory());
|
||||||
Log.notice(u8"🚀 Setup complete! Ready to rock and roll.");
|
Log.notice(u8"🚀 Setup complete! Ready to rock and roll.");
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
ntp.begin();
|
|
||||||
ArduinoOTA.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop out.
|
// Drop out.
|
||||||
|
230
src/platform/arduino/MQTTTelemetry.cpp
Normal file
230
src/platform/arduino/MQTTTelemetry.cpp
Normal 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);
|
@ -1,163 +1,58 @@
|
|||||||
#include <Input.h>
|
#include <Figments.h>
|
||||||
|
#include <Ringbuf.h>
|
||||||
|
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
#include <ArduinoLog.h>
|
#include <ArduinoLog.h>
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <ArduinoUniqueID.h>
|
|
||||||
|
|
||||||
WiFiClient wifi;
|
#include "../../Sequencer.h"
|
||||||
PubSubClient m_mqtt(wifi);
|
|
||||||
|
|
||||||
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
||||||
public:
|
public:
|
||||||
MQTTTelemetry() : BufferedInputSource("MQTT") {}
|
MQTTTelemetry();
|
||||||
|
void setSequencer(Sequencer* seq) { m_sequencer = seq; }
|
||||||
|
|
||||||
void handleEventOnline(const InputEvent& evt) override {
|
class LogPrinter : public Print {
|
||||||
if (!m_mqtt.connected()) {
|
private:
|
||||||
Log.notice("Connecting to MQTT...");
|
MQTTTelemetry* telemetry;
|
||||||
const IPAddress server(10, 0, 0, 2);
|
Ringbuf<char, 512> buf;
|
||||||
uint64_t chipid = ESP.getEfuseMac();
|
public:
|
||||||
char deviceID[15];
|
LogPrinter(MQTTTelemetry* telemetry) : telemetry(telemetry) {};
|
||||||
snprintf(deviceID, 15, "%08X", (uint32_t)chipid);
|
size_t write(uint8_t byte) {
|
||||||
Log.verbose("Device ID %s", deviceID);
|
char outBuf[512];
|
||||||
m_mqtt.setServer(server, 1883);
|
if (byte == '\n') {
|
||||||
m_mqtt.setBufferSize(512);
|
size_t bufSize = buf.write(outBuf);
|
||||||
m_mqtt.setCallback(&MQTTTelemetry::s_callback);
|
outBuf[std::min(sizeof(outBuf), bufSize)] = 0;
|
||||||
if (m_mqtt.connect(deviceID)) {
|
telemetry->m_mqtt.publish(telemetry->m_logTopic, outBuf);
|
||||||
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 {
|
} else {
|
||||||
Log.warning("Could not connect to MQTT");
|
buf.insert(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);
|
|
||||||
}
|
}
|
||||||
|
return sizeof(byte);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Print* logPrinter() {
|
||||||
|
return &m_logPrinter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() override {
|
void handleEventOnline(const InputEvent& evt) override;
|
||||||
BufferedInputSource::loop();
|
void loop() override;
|
||||||
OnlineTaskMixin::loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loopOnline() 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char m_statTopic[100];
|
char m_statTopic[100];
|
||||||
char m_attrTopic[100];
|
char m_attrTopic[100];
|
||||||
char m_cmdTopic[100];
|
char m_cmdTopic[100];
|
||||||
|
char m_logTopic[100];
|
||||||
|
char m_heartbeatTopic[100];
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
static void s_callback(char* topic, byte* payload, unsigned int length);
|
||||||
char m_patternBuf[48];
|
char m_patternBuf[48];
|
||||||
};
|
bool m_needHeartbeat = false;
|
||||||
|
|
||||||
STATIC_ALLOC(MQTTTelemetry);
|
Sequencer *m_sequencer = 0;
|
||||||
|
PubSubClient m_mqtt;
|
||||||
|
LogPrinter m_logPrinter;
|
||||||
|
};
|
||||||
|
@ -57,101 +57,6 @@ PhotonTelemetry::loop()
|
|||||||
void
|
void
|
||||||
PhotonTelemetry::handleEvent(const InputEvent &evt)
|
PhotonTelemetry::handleEvent(const InputEvent &evt)
|
||||||
{
|
{
|
||||||
Serial.flush();
|
|
||||||
if (evt.intent == InputEvent::NetworkStatus) {
|
|
||||||
onConnected();
|
|
||||||
}
|
|
||||||
if (evt.intent != InputEvent::None) {
|
|
||||||
const char* sourceName;
|
|
||||||
switch(evt.intent) {
|
|
||||||
case InputEvent::PowerToggle:
|
|
||||||
sourceName = "power-toggle";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetPower:
|
|
||||||
sourceName = "set-power";
|
|
||||||
break;
|
|
||||||
case InputEvent::PreviousPattern:
|
|
||||||
sourceName = "previous-pattern";
|
|
||||||
break;
|
|
||||||
case InputEvent::NextPattern:
|
|
||||||
sourceName = "next-pattern";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetPattern:
|
|
||||||
sourceName = "set-pattern";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetColor:
|
|
||||||
sourceName = "set-color";
|
|
||||||
break;
|
|
||||||
case InputEvent::Acceleration:
|
|
||||||
sourceName = "acceleration";
|
|
||||||
break;
|
|
||||||
case InputEvent::UserInput:
|
|
||||||
sourceName = "user";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetBrightness:
|
|
||||||
sourceName = "set-brightness";
|
|
||||||
break;
|
|
||||||
case InputEvent::FirmwareUpdate:
|
|
||||||
sourceName = "firmware-update";
|
|
||||||
break;
|
|
||||||
case InputEvent::NetworkStatus:
|
|
||||||
sourceName = "network-status";
|
|
||||||
break;
|
|
||||||
case InputEvent::NetworkActivity:
|
|
||||||
sourceName = "network-activity";
|
|
||||||
break;
|
|
||||||
case InputEvent::StartThing:
|
|
||||||
sourceName = "start-thing";
|
|
||||||
break;
|
|
||||||
case InputEvent::StopThing:
|
|
||||||
sourceName = "stop-thing";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayOffset:
|
|
||||||
sourceName = "set-display-offset";
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayLength:
|
|
||||||
sourceName = "set-display-length";
|
|
||||||
break;
|
|
||||||
case InputEvent::SaveConfigurationRequest:
|
|
||||||
sourceName = "save-configuration";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sourceName = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
char valueBuf[255];
|
|
||||||
switch(evt.type) {
|
|
||||||
case InputEvent::Null:
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "null");break;
|
|
||||||
case InputEvent::Integer:
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "%d %02x", evt.asInt(), evt.asInt());break;
|
|
||||||
case InputEvent::String:
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "\"%s\"", evt.asString());break;
|
|
||||||
case InputEvent::Color:
|
|
||||||
snprintf(valueBuf, sizeof(valueBuf), "[%d, %d, %d]", evt.asRGB().r, evt.asRGB().g, evt.asRGB().b);break;
|
|
||||||
}
|
|
||||||
char buf[255 * 2];
|
|
||||||
if (sourceName == 0) {
|
|
||||||
snprintf(buf, sizeof(buf), "{\"intent\": %d, \"value\": %s}", evt.intent, valueBuf);
|
|
||||||
} else {
|
|
||||||
snprintf(buf, sizeof(buf), "{\"intent\": \"%s\", \"value\": %s}", sourceName, valueBuf);
|
|
||||||
}
|
|
||||||
if (m_online) {
|
|
||||||
if (evt.intent != m_lastEvent.intent) {
|
|
||||||
if (m_duplicateEvents > 0) {
|
|
||||||
Log.info("Suppressed reporting %ld duplicate events.", m_duplicateEvents);
|
|
||||||
}
|
|
||||||
Log.info("Event: %s", buf);
|
|
||||||
m_duplicateEvents = 0;
|
|
||||||
m_lastEvent = evt;
|
|
||||||
Particle.publish("renderbug/event", buf, PRIVATE);
|
|
||||||
} else {
|
|
||||||
m_duplicateEvents++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("[offline] Event: %s", buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt.intent == InputEvent::SetColor) {
|
if (evt.intent == InputEvent::SetColor) {
|
||||||
NSFastLED::CRGB rgb {evt.asRGB()};
|
NSFastLED::CRGB rgb {evt.asRGB()};
|
||||||
uint32_t color = (rgb.r << 16) + (rgb.g << 8) + (rgb.b << 0);
|
uint32_t color = (rgb.r << 16) + (rgb.g << 8) + (rgb.b << 0);
|
||||||
@ -160,6 +65,5 @@ PhotonTelemetry::handleEvent(const InputEvent &evt)
|
|||||||
m_rgbPulseFrame = 1000;
|
m_rgbPulseFrame = 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_ALLOC(PhotonTelemetry);
|
STATIC_ALLOC(PhotonTelemetry);
|
||||||
|
Loading…
Reference in New Issue
Block a user