build for esp32 mask project
This commit is contained in:
parent
439a456d1a
commit
75bf48756b
@ -34,6 +34,12 @@ struct Task : public virtual Loopable {
|
|||||||
State state = Running;
|
State state = Running;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TaskFunc: public Task {
|
||||||
|
TaskFunc(std::function<void()> func) : Task("lambda"), func(func) {}
|
||||||
|
void loop() override {func();}
|
||||||
|
std::function<void()> func;
|
||||||
|
};
|
||||||
|
|
||||||
struct Figment: public Task {
|
struct Figment: public Task {
|
||||||
Figment() : Task() {}
|
Figment() : Task() {}
|
||||||
Figment(State initialState) : Task(initialState) {}
|
Figment(State initialState) : Task(initialState) {}
|
||||||
|
@ -137,7 +137,7 @@ protected:
|
|||||||
void setEvent(InputEvent::Intent intent, Variant &&v);
|
void setEvent(InputEvent::Intent intent, Variant &&v);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ringbuf<InputEvent, 5> m_eventQueue;
|
Ringbuf<InputEvent, 12> m_eventQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputMapper: public BufferedInputSource {
|
class InputMapper: public BufferedInputSource {
|
||||||
@ -157,12 +157,20 @@ class OnlineTaskMixin : public virtual Loopable {
|
|||||||
void handleEvent(const InputEvent &evt) override {
|
void handleEvent(const InputEvent &evt) override {
|
||||||
if (evt.intent == InputEvent::NetworkStatus) {
|
if (evt.intent == InputEvent::NetworkStatus) {
|
||||||
m_online = evt.asInt();
|
m_online = evt.asInt();
|
||||||
|
if (m_online) {
|
||||||
|
onOnline();
|
||||||
|
} else {
|
||||||
|
onOffline();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_online) {
|
if (m_online) {
|
||||||
handleEventOnline(evt);
|
handleEventOnline(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onOnline() {}
|
||||||
|
virtual void onOffline() {}
|
||||||
|
|
||||||
virtual void handleEventOnline(const InputEvent &evt) {}
|
virtual void handleEventOnline(const InputEvent &evt) {}
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
|
@ -37,10 +37,27 @@ MainLoop::loop()
|
|||||||
task->handleEvent(evt);
|
task->handleEvent(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unsigned int slowest = 0;
|
||||||
|
unsigned int frameSpeed = 0;
|
||||||
|
unsigned int frameStart = millis();
|
||||||
|
unsigned int taskCount = 0;
|
||||||
|
Task* slowestTask = NULL;
|
||||||
for(Task* task : scheduler) {
|
for(Task* task : scheduler) {
|
||||||
//Log.notice("Running %s", task->name);
|
//unsigned int start = millis();
|
||||||
|
unsigned int start = ESP.getCycleCount();
|
||||||
task->loop();
|
task->loop();
|
||||||
//Log.notice("next");
|
//unsigned int runtime = millis() - start;
|
||||||
|
unsigned int runtime = ESP.getCycleCount() - start;
|
||||||
|
frameSpeed += runtime;
|
||||||
|
taskCount++;
|
||||||
|
if (runtime > slowest) {
|
||||||
|
slowest = runtime;
|
||||||
|
slowestTask = task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frameSpeed = millis() - frameStart;
|
||||||
|
if (frameSpeed >= 23) {
|
||||||
|
Log.notice("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestTask->name, slowest/160000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,12 @@ Renderer::loop()
|
|||||||
for(Display* dpy : m_displays) {
|
for(Display* dpy : m_displays) {
|
||||||
for(Figment* figment : m_figments) {
|
for(Figment* figment : m_figments) {
|
||||||
if (figment->state == Task::Running) {
|
if (figment->state == Task::Running) {
|
||||||
//Log.notice("Rendering %s", figment->name);
|
unsigned int frameStart = ESP.getCycleCount();
|
||||||
figment->render(dpy);
|
figment->render(dpy);
|
||||||
//Log.notice("next");
|
unsigned int runtime = (ESP.getCycleCount() - frameStart) / 160000;
|
||||||
} else {
|
if (runtime >= 8) {
|
||||||
//Log.notice("Not rendering %s", figment->name);
|
Log.notice("SLOW RENDER: %s took %dms!", figment->name, runtime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
46
lib/README
46
lib/README
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project specific (private) libraries.
|
|
||||||
PlatformIO will compile them to static libraries and link into executable file.
|
|
||||||
|
|
||||||
The source code of each library should be placed in a an own separate directory
|
|
||||||
("lib/your_library_name/[here are source files]").
|
|
||||||
|
|
||||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
|
||||||
|
|
||||||
|--lib
|
|
||||||
| |
|
|
||||||
| |--Bar
|
|
||||||
| | |--docs
|
|
||||||
| | |--examples
|
|
||||||
| | |--src
|
|
||||||
| | |- Bar.c
|
|
||||||
| | |- Bar.h
|
|
||||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
|
||||||
| |
|
|
||||||
| |--Foo
|
|
||||||
| | |- Foo.c
|
|
||||||
| | |- Foo.h
|
|
||||||
| |
|
|
||||||
| |- README --> THIS FILE
|
|
||||||
|
|
|
||||||
|- platformio.ini
|
|
||||||
|--src
|
|
||||||
|- main.c
|
|
||||||
|
|
||||||
and a contents of `src/main.c`:
|
|
||||||
```
|
|
||||||
#include <Foo.h>
|
|
||||||
#include <Bar.h>
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
PlatformIO Library Dependency Finder will find automatically dependent
|
|
||||||
libraries scanning project source files.
|
|
||||||
|
|
||||||
More information about PlatformIO Library Dependency Finder
|
|
||||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
4
no_ota.csv
Normal file
4
no_ota.csv
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x1000,
|
||||||
|
app, app, factory, 0x10000, 2M,
|
|
33
out.log
33
out.log
@ -1,33 +0,0 @@
|
|||||||
ets Jun 8 2016 00:22:57
|
|
||||||
|
|
||||||
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
|
|
||||||
configsip: 0, SPIWP:0xee
|
|
||||||
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
|
|
||||||
mode:DIO, clock div:2
|
|
||||||
load:0x3fff0018,len:4
|
|
||||||
load:0x3fff001c,len:1044
|
|
||||||
load:0x40078000,len:10124
|
|
||||||
load:0x40080400,len:5828
|
|
||||||
entry 0x400806a8
|
|
||||||
N: 🐛 Booting Renderbug!
|
|
||||||
N: 🐞 I am built for 255 LEDs running on 2000mA
|
|
||||||
N: Boot pin configuration:
|
|
||||||
N: 2: Setup - 0
|
|
||||||
N: 3: Serial - 0
|
|
||||||
N: 4: Flash - 0
|
|
||||||
N: 💡 Starting FastLED...
|
|
||||||
N: 🌌 Starting Figment...
|
|
||||||
N: *** Starting 20 tasks...
|
|
||||||
N: * Starting Configuration...
|
|
||||||
N: * Starting MPU5060...
|
|
||||||
N: * Starting Buttons...
|
|
||||||
N: * Starting ...
|
|
||||||
N: * Starting SceneSequencer...
|
|
||||||
N: * Starting CircadianRhythm...
|
|
||||||
N: * Starting Pulse...
|
|
||||||
N: * Starting Solid...
|
|
||||||
N: * Starting Power...
|
|
||||||
N: * Starting lambda...
|
|
||||||
N: * Starting UpdateStatusAnimation...
|
|
||||||
N: * Starting Renderer...
|
|
||||||
N: 🚀 Setup complete! Ready to rock and roll.
|
|
@ -11,13 +11,37 @@
|
|||||||
[common_env_data]
|
[common_env_data]
|
||||||
src_filter = "+<*> -<.git/> -<.svn/> -<platform/>"
|
src_filter = "+<*> -<.git/> -<.svn/> -<platform/>"
|
||||||
|
|
||||||
[env:featheresp32]
|
[env:esp32]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = featheresp32
|
board = featheresp32
|
||||||
framework = arduino
|
framework = arduino
|
||||||
build_flags =
|
build_flags =
|
||||||
-D PLATFORM_ARDUINO
|
-DPLATFORM_ARDUINO
|
||||||
-D BOARD_ESP32
|
-DBOARD_ESP32
|
||||||
|
-DCONFIG_NO_COLORDATA
|
||||||
|
; -DCORE_DEBUG_LEVEL=5
|
||||||
|
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/>"
|
||||||
|
board_build.partitions = no_ota.csv
|
||||||
|
;build_type = debug
|
||||||
|
|
||||||
|
[env:cyberplague]
|
||||||
|
extends = env:esp32
|
||||||
|
board_build.partitions = no_ota.csv
|
||||||
|
|
||||||
|
[env:esp8266]
|
||||||
|
platform = espressif8266
|
||||||
|
board = huzzah
|
||||||
|
framework = arduino
|
||||||
|
build_flags =
|
||||||
|
-DPLATFORM_ARDUINO
|
||||||
|
-DBOARD_ESP8266
|
||||||
lib_deps =
|
lib_deps =
|
||||||
fastled/FastLED@^3.4.0
|
fastled/FastLED@^3.4.0
|
||||||
thijse/ArduinoLog@^1.0.3
|
thijse/ArduinoLog@^1.0.3
|
||||||
@ -27,18 +51,7 @@ lib_deps =
|
|||||||
arduino-libraries/NTPClient@^3.1.0
|
arduino-libraries/NTPClient@^3.1.0
|
||||||
src_filter = "${common_env_data.src_filter} +<platform/arduino/>"
|
src_filter = "${common_env_data.src_filter} +<platform/arduino/>"
|
||||||
|
|
||||||
[env:huzzah]
|
;[env:photon]
|
||||||
platform = espressif8266
|
;platform = particlephoton
|
||||||
board = huzzah
|
;board = photon
|
||||||
framework = arduino
|
;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,4 +1,9 @@
|
|||||||
#include "BootOptions.h"
|
#include "BootOptions.h"
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#endif
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
LEDStatus serialStatus = LEDStatus(RGB_COLOR_ORANGE, LED_PATTERN_FADE, LED_SPEED_FAST, LED_PRIORITY_BACKGROUND);
|
LEDStatus serialStatus = LEDStatus(RGB_COLOR_ORANGE, LED_PATTERN_FADE, LED_SPEED_FAST, LED_PRIORITY_BACKGROUND);
|
||||||
@ -31,6 +36,28 @@ BootOptions::BootOptions()
|
|||||||
configStatus.setActive(isSetup);
|
configStatus.setActive(isSetup);
|
||||||
serialStatus.setActive(isSerial);
|
serialStatus.setActive(isSerial);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
struct rst_info resetInfo = *ESP.getResetInfoPtr();
|
||||||
|
uint8_t crashCount;
|
||||||
|
EEPROM.begin(sizeof(crashCount));
|
||||||
|
EEPROM.get(sizeof(HardwareConfig) + 32, crashCount);
|
||||||
|
EEPROM.end();
|
||||||
|
if (resetInfo.reason == REASON_WDT_RST) {
|
||||||
|
if (crashCount++ >= 3) {
|
||||||
|
// Boot into safe mode if the watchdog reset us three times in a row.
|
||||||
|
isSafeMode = true;
|
||||||
|
} else {
|
||||||
|
EEPROM.begin(sizeof(crashCount));
|
||||||
|
EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
} else if (crashCount != 0) {
|
||||||
|
crashCount = 0;
|
||||||
|
EEPROM.begin(sizeof(crashCount));
|
||||||
|
EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -11,4 +11,5 @@ struct BootOptions {
|
|||||||
bool isSerial = false;
|
bool isSerial = false;
|
||||||
bool isFlash = false;
|
bool isFlash = false;
|
||||||
bool lastBootWasFlash = false;
|
bool lastBootWasFlash = false;
|
||||||
|
bool isSafeMode = false;
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,10 @@ constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
|||||||
HardwareConfig
|
HardwareConfig
|
||||||
HardwareConfig::load() {
|
HardwareConfig::load() {
|
||||||
HardwareConfig ret;
|
HardwareConfig ret;
|
||||||
|
EEPROM.begin(sizeof(ret));
|
||||||
EEPROM.get(0, ret);
|
EEPROM.get(0, ret);
|
||||||
|
EEPROM.end();
|
||||||
|
Log.notice("Loaded config version %d, CRC %d", ret.version, ret.checksum);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +19,10 @@ void
|
|||||||
HardwareConfig::save() {
|
HardwareConfig::save() {
|
||||||
HardwareConfig dataCopy{*this};
|
HardwareConfig dataCopy{*this};
|
||||||
dataCopy.checksum = getCRC();
|
dataCopy.checksum = getCRC();
|
||||||
|
EEPROM.begin(sizeof(dataCopy));
|
||||||
EEPROM.put(0, dataCopy);
|
EEPROM.put(0, dataCopy);
|
||||||
|
EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearCoordinateMapping
|
LinearCoordinateMapping
|
||||||
@ -66,13 +72,13 @@ ConfigService::onStart()
|
|||||||
m_coordMap = m_config.toCoordMap();
|
m_coordMap = m_config.toCoordMap();
|
||||||
|
|
||||||
Log.notice("Configured to use %d pixels, starting at %d", m_config.data.pixelCount, m_config.data.startPixel);
|
Log.notice("Configured to use %d pixels, starting at %d", m_config.data.pixelCount, m_config.data.startPixel);
|
||||||
Log.notice("Loading task states...");
|
/*Log.notice("Loading task states...");
|
||||||
for(int i = 0; i < 32; i++) {
|
for(int i = 0; i < 32; i++) {
|
||||||
auto svc = m_config.data.serviceStates[i];
|
auto svc = m_config.data.serviceStates[i];
|
||||||
if (strlen(svc.name) > 0) {
|
if (strnlen(svc.name, 16) > 0) {
|
||||||
Log.notice("* %s: %s", svc.name, svc.isDisabled? "DISABLED" : "ENABLED");
|
Log.notice("* %s: %s", svc.name, svc.isDisabled? "DISABLED" : "ENABLED");
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -81,7 +81,7 @@ LogService::handleEvent(const InputEvent& evt) {
|
|||||||
}
|
}
|
||||||
if (evt.intent != m_lastEvent.intent) {
|
if (evt.intent != m_lastEvent.intent) {
|
||||||
if (m_duplicateEvents > 0) {
|
if (m_duplicateEvents > 0) {
|
||||||
Log.notice("Suppressed reporting %u duplicate events.", m_duplicateEvents);
|
Log.notice("Suppressed reporting %d duplicate events.", m_duplicateEvents);
|
||||||
}
|
}
|
||||||
Log.verbose("Event: %s", buf);
|
Log.verbose("Event: %s", buf);
|
||||||
m_duplicateEvents = 0;
|
m_duplicateEvents = 0;
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#ifdef BOARD_ESP32
|
#ifdef BOARD_ESP32
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
#include <time.h>
|
||||||
#elif defined(BOARD_ESP8266)
|
#elif defined(BOARD_ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
@ -11,16 +13,23 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
WiFiUDP wifiUdp;
|
WiFiUDP wifiUdp;
|
||||||
NTPClient timeClient(wifiUdp, "pool.ntp.org", 3600 * -7);
|
//NTPClient timeClient(wifiUdp, "pool.ntp.org", 3600 * -7);
|
||||||
|
NTPClient timeClient(wifiUdp, "10.0.0.1", 3600 * -7);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
STARTUP(BootOptions::initPins());
|
STARTUP(BootOptions::initPins());
|
||||||
#else
|
#else
|
||||||
#include "platform/arduino/MQTTTelemetry.h"
|
#include "platform/arduino/MQTTTelemetry.h"
|
||||||
void printNewline(Print* logOutput) {
|
void printNewline(Print* logOutput)
|
||||||
|
{
|
||||||
logOutput->print("\r\n");
|
logOutput->print("\r\n");
|
||||||
}
|
}
|
||||||
|
int printEspLog(const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
Log.notice(fmt, args);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int Platform::s_timezone = 0;
|
int Platform::s_timezone = 0;
|
||||||
@ -30,6 +39,10 @@ Platform::name()
|
|||||||
{
|
{
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
return "Photon";
|
return "Photon";
|
||||||
|
#elif defined(BOARD_ESP8266)
|
||||||
|
return "ESP8266";
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
return "ESP32";
|
||||||
#else
|
#else
|
||||||
return "Unknown!";
|
return "Unknown!";
|
||||||
#endif
|
#endif
|
||||||
@ -40,6 +53,8 @@ Platform::version()
|
|||||||
{
|
{
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
return System.version().c_str();
|
return System.version().c_str();
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
return ESP.getSdkVersion();
|
||||||
#else
|
#else
|
||||||
return "Unknown!";
|
return "Unknown!";
|
||||||
#endif
|
#endif
|
||||||
@ -49,6 +64,7 @@ void
|
|||||||
Platform::preSetup()
|
Platform::preSetup()
|
||||||
{
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
delay(5000);
|
||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
||||||
if (bootopts.isFlash) {
|
if (bootopts.isFlash) {
|
||||||
@ -65,6 +81,15 @@ Platform::preSetup()
|
|||||||
Log.begin(LOG_LEVEL_VERBOSE, Static<MQTTTelemetry>::instance()->logPrinter());
|
Log.begin(LOG_LEVEL_VERBOSE, Static<MQTTTelemetry>::instance()->logPrinter());
|
||||||
Log.setSuffix(printNewline);
|
Log.setSuffix(printNewline);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef BOARD_ESP32
|
||||||
|
esp_task_wdt_init(10, true);
|
||||||
|
esp_task_wdt_add(NULL);
|
||||||
|
esp_log_set_vprintf(printEspLog);
|
||||||
|
#endif
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
ESP.wdtEnable(0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -95,7 +120,12 @@ void
|
|||||||
Platform::loop()
|
Platform::loop()
|
||||||
{
|
{
|
||||||
#ifdef BOARD_ESP8266
|
#ifdef BOARD_ESP8266
|
||||||
timeClient.update();
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
timeClient.update();
|
||||||
|
}
|
||||||
|
ESP.wdtFeed();
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
esp_task_wdt_reset();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +140,12 @@ Platform::getLocalTime(struct tm* timedata)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
#elif defined(BOARD_ESP32)
|
#elif defined(BOARD_ESP32)
|
||||||
return getLocalTime(timedata);
|
time_t rawtime;
|
||||||
|
time(&rawtime);
|
||||||
|
(*timedata) = (*localtime(&rawtime));
|
||||||
|
return (timedata->tm_year > (2016-1990));
|
||||||
|
//return getLocalTime(timedata);
|
||||||
#else
|
#else
|
||||||
timeClient.update();
|
|
||||||
timedata->tm_hour = timeClient.getHours();
|
timedata->tm_hour = timeClient.getHours();
|
||||||
timedata->tm_min = timeClient.getMinutes();
|
timedata->tm_min = timeClient.getMinutes();
|
||||||
return true;
|
return true;
|
||||||
@ -137,3 +170,5 @@ Platform::bootopts;
|
|||||||
|
|
||||||
char
|
char
|
||||||
Platform::s_deviceID[15];
|
Platform::s_deviceID[15];
|
||||||
|
|
||||||
|
STATIC_ALLOC(Platform);
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
|
#include <Figments.h>
|
||||||
#include "BootOptions.h"
|
#include "BootOptions.h"
|
||||||
|
|
||||||
class Platform {
|
class Platform : public Task {
|
||||||
static int s_timezone;
|
static int s_timezone;
|
||||||
static char s_deviceID[15];
|
static char s_deviceID[15];
|
||||||
public:
|
public:
|
||||||
|
Platform() : Task("Platform") {}
|
||||||
static BootOptions bootopts;
|
static BootOptions bootopts;
|
||||||
static void setTimezone(int tz) { s_timezone = tz; }
|
static void setTimezone(int tz) { s_timezone = tz; }
|
||||||
static int getTimezone() { return s_timezone; }
|
static int getTimezone() { return s_timezone; }
|
||||||
@ -14,18 +16,28 @@ class Platform {
|
|||||||
#ifdef PLATFORM_PHOTON
|
#ifdef PLATFORM_PHOTON
|
||||||
FastLED.addLeds<NEOPIXEL, 6>(leds, ledCount);
|
FastLED.addLeds<NEOPIXEL, 6>(leds, ledCount);
|
||||||
#elif defined(BOARD_ESP32)
|
#elif defined(BOARD_ESP32)
|
||||||
FastLED.addLeds<WS2812B, 13, RGB>(leds, ledCount);
|
FastLED.addLeds<WS2812B, 13, GRB>(leds, ledCount);
|
||||||
#else
|
#else
|
||||||
FastLED.addLeds<WS2812B, 14, GRB>(leds, ledCount);
|
//FastLED.addLeds<WS2812B, 14, GRB>(leds, ledCount);
|
||||||
|
FastLED.addLeds<WS2812B, 14, RGB>(leds, ledCount);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* name();
|
static const char* name();
|
||||||
static const char* version();
|
static const char* version();
|
||||||
|
static const String model() {
|
||||||
|
static String modelName = String("Renderbug " ) + Platform::name();
|
||||||
|
return modelName;
|
||||||
|
}
|
||||||
|
static const String deviceName() {
|
||||||
|
static String devName = model() + " " + Platform::deviceID();
|
||||||
|
return devName;
|
||||||
|
}
|
||||||
static void preSetup();
|
static void preSetup();
|
||||||
static void bootSplash();
|
static void bootSplash();
|
||||||
static void setup();
|
static void setup();
|
||||||
static void loop();
|
void loop() override;
|
||||||
static bool getLocalTime(struct tm* timedata);
|
static bool getLocalTime(struct tm* timedata);
|
||||||
static const char* deviceID();
|
static const char* deviceID();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,10 +25,13 @@ Sequencer::scenes() const
|
|||||||
void
|
void
|
||||||
Sequencer::handleEvent(const InputEvent& evt)
|
Sequencer::handleEvent(const InputEvent& evt)
|
||||||
{
|
{
|
||||||
|
if (evt.intent == InputEvent::SetPattern && evt.asString() == m_scenes[m_idx].name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (evt.intent == InputEvent::SetPattern || evt.intent == InputEvent::NextPattern || evt.intent == InputEvent::PreviousPattern) {
|
if (evt.intent == InputEvent::SetPattern || evt.intent == InputEvent::NextPattern || evt.intent == InputEvent::PreviousPattern) {
|
||||||
Log.notice("Switching pattern!");
|
Log.notice("Switching pattern!");
|
||||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||||
Log.notice("Stopping %s", pattern);
|
//Log.notice("Stopping %s", pattern);
|
||||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +57,7 @@ Sequencer::handleEvent(const InputEvent& evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||||
Log.notice("Starting %s", pattern);
|
//Log.notice("Starting %s", pattern);
|
||||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,7 @@ WiFiTask::onStart()
|
|||||||
{
|
{
|
||||||
Log.notice("Starting wifi...");
|
Log.notice("Starting wifi...");
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
int n = WiFi.scanNetworks();
|
|
||||||
if (n == 0) {
|
|
||||||
Log.notice("No wifi found");
|
|
||||||
} else {
|
|
||||||
for(int i = 0; i < n; ++i) {
|
|
||||||
Serial.print("WiFi: ");
|
|
||||||
Serial.println(WiFi.SSID(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.begin("The Frequency", "thepasswordkenneth");
|
WiFi.begin("The Frequency", "thepasswordkenneth");
|
||||||
while(WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.print('.');
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputEvent
|
InputEvent
|
||||||
@ -42,7 +28,7 @@ WiFiTask::read()
|
|||||||
m_lastStatus = curStatus;
|
m_lastStatus = curStatus;
|
||||||
Log.verbose("WiFi Status: %d", curStatus);
|
Log.verbose("WiFi Status: %d", curStatus);
|
||||||
if (curStatus == WL_CONNECTED) {
|
if (curStatus == WL_CONNECTED) {
|
||||||
Log.notice("Connected!");
|
Log.notice("Connected! IP address is %s", WiFi.localIP().toString().c_str());
|
||||||
return InputEvent{InputEvent::NetworkStatus, true};
|
return InputEvent{InputEvent::NetworkStatus, true};
|
||||||
} else if (curStatus == WL_CONNECTION_LOST || curStatus == WL_DISCONNECTED) {
|
} else if (curStatus == WL_CONNECTION_LOST || curStatus == WL_DISCONNECTED) {
|
||||||
Log.notice("Lost wifi connection!");
|
Log.notice("Lost wifi connection!");
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "colors.h"
|
#include "colors.h"
|
||||||
|
|
||||||
const ColorInfo color_data[] = {
|
const ColorInfo color_data[] = {
|
||||||
|
#ifdef CONFIG_NO_COLORDATA
|
||||||
|
#else
|
||||||
{ "Air Superiority Blue", { 114, 160, 193 } },
|
{ "Air Superiority Blue", { 114, 160, 193 } },
|
||||||
{ "Alabama Crimson", { 163, 38, 56 } },
|
{ "Alabama Crimson", { 163, 38, 56 } },
|
||||||
{ "Alice Blue", { 240, 248, 255 } },
|
{ "Alice Blue", { 240, 248, 255 } },
|
||||||
@ -792,6 +794,7 @@ const ColorInfo color_data[] = {
|
|||||||
{ "Yellow Orange", { 255, 174, 66 } },
|
{ "Yellow Orange", { 255, 174, 66 } },
|
||||||
{ "Zaffre", { 0, 20, 168 } },
|
{ "Zaffre", { 0, 20, 168 } },
|
||||||
{ "Zinnwaldite Brown", { 44, 22, 8 } },
|
{ "Zinnwaldite Brown", { 44, 22, 8 } },
|
||||||
|
#endif
|
||||||
{0, {0, 0, 0}},
|
{0, {0, 0, 0}},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
firmware
|
|
187
src/main.cpp
187
src/main.cpp
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include "Static.h"
|
#include "Static.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "colors.h"
|
|
||||||
|
|
||||||
#include "Sequencer.h"
|
#include "Sequencer.h"
|
||||||
#include "LogService.h"
|
#include "LogService.h"
|
||||||
@ -36,6 +35,7 @@
|
|||||||
#include "platform/particle/MDNSService.cpp"
|
#include "platform/particle/MDNSService.cpp"
|
||||||
#else
|
#else
|
||||||
#include "WiFiTask.h"
|
#include "WiFiTask.h"
|
||||||
|
#include "platform/arduino/BluetoothSerialTelemetry.h"
|
||||||
#include "platform/arduino/MQTTTelemetry.h"
|
#include "platform/arduino/MQTTTelemetry.h"
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#endif
|
#endif
|
||||||
@ -55,9 +55,6 @@
|
|||||||
CRGB leds[HardwareConfig::MAX_LED_NUM];
|
CRGB leds[HardwareConfig::MAX_LED_NUM];
|
||||||
Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static<ConfigService>::instance()->coordMap());
|
Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static<ConfigService>::instance()->coordMap());
|
||||||
|
|
||||||
LinearCoordinateMapping neckMap{60, 0};
|
|
||||||
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;
|
||||||
|
|
||||||
@ -113,7 +110,7 @@ class ArduinoOTAUpdater : public BufferedInputSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleEvent(const InputEvent& evt) {
|
void handleEvent(const InputEvent& evt) {
|
||||||
if (evt.intent == InputEvent::NetworkStatus) {
|
if (evt.intent == InputEvent::NetworkStatus && evt.asInt()) {
|
||||||
Log.notice("Booting OTA");
|
Log.notice("Booting OTA");
|
||||||
m_online = true;
|
m_online = true;
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
@ -176,19 +173,17 @@ DrainAnimation drain{Task::Stopped};
|
|||||||
Flashlight flashlight{Task::Stopped};
|
Flashlight flashlight{Task::Stopped};
|
||||||
|
|
||||||
Sequencer sequencer{{
|
Sequencer sequencer{{
|
||||||
{"Idle", {"Solid", "MPU5060", "Pulse", "Hackerbots", "Kieryn", "CircadianRhythm"}},
|
{"Idle", {"Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"}},
|
||||||
{"Solid", {"Solid", "MPU5060", "Pulse", "CircadianRhythm"}},
|
{"Solid", {"Solid", "MPU5060", "Pulse", "CircadianRhythm"}},
|
||||||
{"Interactive", {"Drain", "CircadianRhythm"}},
|
{"Interactive", {"Drain", "MPU5060", "CircadianRhythm"}},
|
||||||
{"Flashlight", {"Flashlight"}},
|
{"Flashlight", {"Flashlight"}},
|
||||||
{"Nightlight", {"Drain", "Pulse", "Noisebridge"}},
|
{"Gay", {"Solid", "Pulse", "Rainbow", "Rainbow"}},
|
||||||
{"Gay", {"Solid", "Pulse", "Rainbow", "Hackerbots", "Kieryn"}},
|
{"Acid", {"Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"}},
|
||||||
{"Acid", {"Chimes", "Pulse", "MPU5060", "Hackerbots", "Rainbow"}},
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
// Render all layers to the displays
|
// Render all layers to the displays
|
||||||
Renderer renderer{
|
Renderer renderer{
|
||||||
//{&dpy, &neckDisplay},
|
|
||||||
{&dpy},
|
{&dpy},
|
||||||
{
|
{
|
||||||
&chimes,
|
&chimes,
|
||||||
@ -207,29 +202,22 @@ Renderer configRenderer{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Cycle some random colors
|
// Cycle some random colors
|
||||||
ColorSequenceInput<7> noisebridgeCycle{{colorForName("Red").rgb}, "Noisebridge", Task::Stopped};
|
ColorSequenceInput<9> idleCycle{{
|
||||||
|
|
||||||
ColorSequenceInput<13> kierynCycle{{
|
|
||||||
CRGB(0, 123, 167), // Cerulean
|
CRGB(0, 123, 167), // Cerulean
|
||||||
CRGB(80, 200, 120), // Emerald
|
CRGB(80, 200, 120), // Emerald
|
||||||
CRGB(207, 113, 175), // Sky Magenta
|
CRGB(207, 113, 175), // Sky Magenta
|
||||||
}, "Kieryn", Task::Running};
|
|
||||||
|
|
||||||
ColorSequenceInput<7> rainbowCycle{{
|
|
||||||
colorForName("Red").rgb,
|
|
||||||
colorForName("Orange").rgb,
|
|
||||||
colorForName("Yellow").rgb,
|
|
||||||
colorForName("Green").rgb,
|
|
||||||
colorForName("Blue").rgb,
|
|
||||||
colorForName("Purple").rgb,
|
|
||||||
colorForName("White").rgb,
|
|
||||||
}, "Rainbow", Task::Stopped};
|
|
||||||
|
|
||||||
ColorSequenceInput<7> hackerbotsCycle{{
|
|
||||||
CRGB(128, 0, 128), // Purple
|
CRGB(128, 0, 128), // Purple
|
||||||
CRGB(255, 255, 255), // White
|
CRGB(255, 255, 255), // White
|
||||||
CRGB(0, 255, 255), // Cyan
|
CRGB(0, 255, 255), // Cyan
|
||||||
}, "Hackerbots", Task::Running};
|
}, "IdleColors", Task::Running};
|
||||||
|
|
||||||
|
ColorSequenceInput<7> rainbowCycle{{
|
||||||
|
CRGB(255, 0, 0), // Red
|
||||||
|
CRGB(255, 127, 0), // Yellow
|
||||||
|
CRGB(0, 255, 0), // Green
|
||||||
|
CRGB(0, 0, 255), // Blue
|
||||||
|
CRGB(128, 0, 128), // Purple
|
||||||
|
}, "Rainbow", Task::Stopped};
|
||||||
|
|
||||||
struct ConfigInputTask: public BufferedInputSource {
|
struct ConfigInputTask: public BufferedInputSource {
|
||||||
public:
|
public:
|
||||||
@ -324,48 +312,53 @@ std::array<ScheduleEntry, 10> schedule{{
|
|||||||
{23, 20}
|
{23, 20}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
uint8_t brightnessForTime(uint8_t hour, uint8_t minute) {
|
|
||||||
ScheduleEntry start = schedule.back();
|
|
||||||
ScheduleEntry end = schedule.front();
|
|
||||||
for(ScheduleEntry cur : schedule) {
|
|
||||||
// Find the last hour that is prior to or equal to now
|
|
||||||
if (cur.hour <= hour) {
|
|
||||||
start = cur;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(ScheduleEntry cur : schedule) {
|
|
||||||
// Find the first hour that is after now
|
|
||||||
// If no such hour exists, we should automatically wrap back to hour 0
|
|
||||||
if (cur.hour > hour) {
|
|
||||||
end = cur;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start.hour > end.hour) {
|
|
||||||
end.hour += 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t startTime = start.hour * 60;
|
|
||||||
uint16_t endTime = end.hour * 60;
|
|
||||||
uint16_t nowTime = hour * 60 + minute;
|
|
||||||
|
|
||||||
uint16_t duration = endTime - startTime;
|
|
||||||
uint16_t curDuration = nowTime - startTime;
|
|
||||||
|
|
||||||
uint8_t frac = ((double)curDuration / (double)duration) * 255.0;
|
|
||||||
|
|
||||||
return lerp8by8(start.brightness, end.brightness, frac);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CircadianRhythm : public InputSource {
|
class CircadianRhythm : public InputSource {
|
||||||
private:
|
private:
|
||||||
bool needsUpdate = true;
|
bool needsUpdate = true;
|
||||||
public:
|
public:
|
||||||
CircadianRhythm() : InputSource("CircadianRhythm") {}
|
CircadianRhythm() : InputSource("CircadianRhythm") {}
|
||||||
|
|
||||||
|
void onStart() {
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t brightnessForTime(uint8_t hour, uint8_t minute) const {
|
||||||
|
ScheduleEntry start = schedule.back();
|
||||||
|
ScheduleEntry end = schedule.front();
|
||||||
|
for(ScheduleEntry cur : schedule) {
|
||||||
|
// Find the last hour that is prior to or equal to now
|
||||||
|
if (cur.hour <= hour) {
|
||||||
|
start = cur;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(ScheduleEntry cur : schedule) {
|
||||||
|
// Find the first hour that is after now
|
||||||
|
// If no such hour exists, we should automatically wrap back to hour 0
|
||||||
|
if (cur.hour > hour) {
|
||||||
|
end = cur;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start.hour > end.hour) {
|
||||||
|
end.hour += 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t startTime = start.hour * 60;
|
||||||
|
uint16_t endTime = end.hour * 60;
|
||||||
|
uint16_t nowTime = hour * 60 + minute;
|
||||||
|
|
||||||
|
uint16_t duration = endTime - startTime;
|
||||||
|
uint16_t curDuration = nowTime - startTime;
|
||||||
|
|
||||||
|
uint8_t frac = map8(curDuration, 0, duration);
|
||||||
|
|
||||||
|
return lerp8by8(start.brightness, end.brightness, frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
InputEvent read() {
|
InputEvent read() {
|
||||||
EVERY_N_SECONDS(60) {
|
EVERY_N_SECONDS(60) {
|
||||||
needsUpdate = true;
|
needsUpdate = true;
|
||||||
@ -390,6 +383,8 @@ 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.
|
||||||
MainLoop configApp{{
|
MainLoop configApp{{
|
||||||
|
Static<Platform>::instance(),
|
||||||
|
|
||||||
// Manage read/write of configuration data
|
// Manage read/write of configuration data
|
||||||
Static<ConfigService>::instance(),
|
Static<ConfigService>::instance(),
|
||||||
|
|
||||||
@ -415,9 +410,45 @@ MainLoop configApp{{
|
|||||||
&configRenderer,
|
&configRenderer,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
TaskFunc safeModeNag([]{
|
||||||
|
static uint8_t frame = 0;
|
||||||
|
EVERY_N_SECONDS(30) {
|
||||||
|
Log.notice("I am running in safe mode!");
|
||||||
|
}
|
||||||
|
EVERY_N_MILLISECONDS(16) {
|
||||||
|
frame++;
|
||||||
|
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
|
||||||
|
leds[i] = CRGB(0, 0, 0);
|
||||||
|
}
|
||||||
|
for(int idx = 0; idx < 3; idx++) {
|
||||||
|
uint8_t length = beatsin8(5, 3, HardwareConfig::MAX_LED_NUM, 0, idx * 5);
|
||||||
|
for(int i = 0; i < length; i++) {
|
||||||
|
leds[i] += CRGB(scale8(5, beatsin8(5 + i * 7, 0, 255, 0, i*3)), 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
MainLoop safeModeApp({
|
||||||
|
Static<Platform>::instance(),
|
||||||
|
// ESP Wifi
|
||||||
|
Static<WiFiTask>::instance(),
|
||||||
|
// System logging
|
||||||
|
Static<LogService>::instance(),
|
||||||
|
// MQTT
|
||||||
|
Static<MQTTTelemetry>::instance(),
|
||||||
|
// OTA Updates
|
||||||
|
Static<ArduinoOTAUpdater>::instance(),
|
||||||
|
|
||||||
|
&safeModeNag,
|
||||||
|
});
|
||||||
|
|
||||||
// Turn on,
|
// Turn on,
|
||||||
MainLoop renderbugApp{{
|
MainLoop renderbugApp{{
|
||||||
|
|
||||||
|
Static<Platform>::instance(),
|
||||||
|
|
||||||
// Load/update graphics configuration from EEPROM
|
// Load/update graphics configuration from EEPROM
|
||||||
Static<ConfigService>::instance(),
|
Static<ConfigService>::instance(),
|
||||||
|
|
||||||
@ -431,18 +462,28 @@ MainLoop renderbugApp{{
|
|||||||
Static<PhotonInput>::instance(),
|
Static<PhotonInput>::instance(),
|
||||||
#else
|
#else
|
||||||
// ESP Wifi
|
// ESP Wifi
|
||||||
Static<WiFiTask>::instance(),
|
//Static<WiFiTask>::instance(),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BOARD_ESP32
|
||||||
|
// ESP32 Bluetooth
|
||||||
|
Static<BluetoothSerialTelemetry>::instance(),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// System logging
|
// System logging
|
||||||
Static<LogService>::instance(),
|
Static<LogService>::instance(),
|
||||||
|
|
||||||
|
#ifdef CONFIG_MPU5060
|
||||||
// Hardware drivers
|
// Hardware drivers
|
||||||
Static<MPU5060>::instance(),
|
Static<MPU5060>::instance(),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_BUTTONS
|
||||||
Static<Buttons>::instance(),
|
Static<Buttons>::instance(),
|
||||||
|
|
||||||
// Map buttons to events
|
// Map buttons to events
|
||||||
&keyMap,
|
&keyMap,
|
||||||
|
#endif
|
||||||
|
|
||||||
// Pattern sequencer
|
// Pattern sequencer
|
||||||
&sequencer,
|
&sequencer,
|
||||||
@ -451,13 +492,11 @@ MainLoop renderbugApp{{
|
|||||||
Static<CircadianRhythm>::instance(),
|
Static<CircadianRhythm>::instance(),
|
||||||
|
|
||||||
// Periodic motion input
|
// Periodic motion input
|
||||||
&randomPulse,
|
//&randomPulse,
|
||||||
|
|
||||||
// Periodic color inputs
|
// Periodic color inputs
|
||||||
&noisebridgeCycle,
|
&idleCycle,
|
||||||
&kierynCycle,
|
|
||||||
&rainbowCycle,
|
&rainbowCycle,
|
||||||
&hackerbotsCycle,
|
|
||||||
|
|
||||||
// Animations
|
// Animations
|
||||||
&chimes,
|
&chimes,
|
||||||
@ -521,8 +560,13 @@ void setup() {
|
|||||||
Log.notice(u8"💡 Starting FastLED...");
|
Log.notice(u8"💡 Starting FastLED...");
|
||||||
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
|
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
|
||||||
|
|
||||||
if (Platform::bootopts.isSetup) {
|
if (Platform::bootopts.isSafeMode) {
|
||||||
Log.notice(u8"🌌 Starting Figment in configuration mode...");
|
Log.notice(u8"⚠️ Starting Figment in safe mode!!!");
|
||||||
|
runner = safeModeApp;
|
||||||
|
FastLED.showColor(CRGB(5, 0, 0));
|
||||||
|
FastLED.show();
|
||||||
|
} else if (Platform::bootopts.isSetup) {
|
||||||
|
Log.notice(u8"🔧 Starting Figment in configuration mode...");
|
||||||
runner = configApp;
|
runner = configApp;
|
||||||
} else {
|
} else {
|
||||||
Log.notice(u8"🌌 Starting Figment...");
|
Log.notice(u8"🌌 Starting Figment...");
|
||||||
@ -537,5 +581,6 @@ void setup() {
|
|||||||
|
|
||||||
// Drop out.
|
// Drop out.
|
||||||
void loop() {
|
void loop() {
|
||||||
|
//Platform::loop();
|
||||||
runner.loop();
|
runner.loop();
|
||||||
}
|
}
|
||||||
|
86
src/platform/arduino/BluetoothSerialTelemetry.cpp
Normal file
86
src/platform/arduino/BluetoothSerialTelemetry.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "BluetoothSerialTelemetry.h"
|
||||||
|
#include "../../Static.h"
|
||||||
|
#include "../../Platform.h"
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
#include "../../inputs/Buttons.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
BluetoothSerialTelemetry::BluetoothSerialTelemetry() : InputSource("Bluetooth")
|
||||||
|
{
|
||||||
|
//m_serial.setPin("0000");
|
||||||
|
m_serial.enableSSP();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputEvent
|
||||||
|
BluetoothSerialTelemetry::read()
|
||||||
|
{
|
||||||
|
bool didRead = false;
|
||||||
|
while (m_serial.available()) {
|
||||||
|
didRead = true;
|
||||||
|
char charRead = m_serial.read();
|
||||||
|
m_ringbuf.insert(charRead);
|
||||||
|
if (charRead == '*') {
|
||||||
|
static char commandBuf[32];
|
||||||
|
size_t cmdSize = m_ringbuf.write(commandBuf);
|
||||||
|
// Overwrite the '*' character, to leave us with a complete command
|
||||||
|
commandBuf[cmdSize-1] = 0;
|
||||||
|
|
||||||
|
//Log.notice("Bluetooth read %s", commandBuf);
|
||||||
|
|
||||||
|
if (commandBuf[0] == 'R') {
|
||||||
|
m_color = CRGB(std::atoi(&commandBuf[1]), m_color.g, m_color.b);
|
||||||
|
return InputEvent{InputEvent::SetColor, m_color};
|
||||||
|
} else if (commandBuf[0] == 'G') {
|
||||||
|
m_color = CRGB(m_color.r, std::atoi(&commandBuf[1]), m_color.b);
|
||||||
|
return InputEvent{InputEvent::SetColor, m_color};
|
||||||
|
} else if (commandBuf[0] == 'B') {
|
||||||
|
m_color = CRGB(m_color.r, m_color.g, std::atoi(&commandBuf[1]));
|
||||||
|
return InputEvent{InputEvent::SetColor, m_color};
|
||||||
|
} else if (commandBuf[0] == 'O') {
|
||||||
|
return InputEvent{InputEvent::UserInput, Buttons::Circle};
|
||||||
|
} else if (commandBuf[0] == 'S') {
|
||||||
|
return InputEvent{InputEvent::UserInput, Buttons::Triangle};
|
||||||
|
} else if (commandBuf[0] == 'X') {
|
||||||
|
return InputEvent{InputEvent::UserInput, Buttons::Cross};
|
||||||
|
} else if (commandBuf[0] == '+') {
|
||||||
|
return InputEvent{InputEvent::SetPower, 1};
|
||||||
|
} else if (commandBuf[0] == '-') {
|
||||||
|
return InputEvent{InputEvent::SetPower, 0};
|
||||||
|
} else if (commandBuf[0] == 'p') {
|
||||||
|
return InputEvent{InputEvent::SetPattern, &commandBuf[1]};
|
||||||
|
} else if (commandBuf[0] == 'A') {
|
||||||
|
char* axisVal = strtok(&commandBuf[1], ",");
|
||||||
|
const uint8_t accelX = std::atof(axisVal) * 10;
|
||||||
|
axisVal = strtok(NULL, ",");
|
||||||
|
const uint8_t accelY = std::atof(axisVal) * 10;
|
||||||
|
axisVal = strtok(NULL, ",");
|
||||||
|
const uint8_t accelZ = std::atof(axisVal) * 10;
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (didRead) {
|
||||||
|
return InputEvent::NetworkActivity;
|
||||||
|
} else {
|
||||||
|
return InputEvent{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BluetoothSerialTelemetry::onStart()
|
||||||
|
{
|
||||||
|
Log.notice("Starting up Bluetooth...");
|
||||||
|
if (m_serial.begin(Platform::deviceName())) {
|
||||||
|
Log.notice("Bluetooth started!");
|
||||||
|
} else {
|
||||||
|
Log.warning("Bluetooth could not be started!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(BluetoothSerialTelemetry);
|
41
src/platform/arduino/BluetoothSerialTelemetry.h
Normal file
41
src/platform/arduino/BluetoothSerialTelemetry.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <Figments.h>
|
||||||
|
#include <BluetoothSerial.h>
|
||||||
|
#include <Ringbuf.h>
|
||||||
|
|
||||||
|
class BluetoothSerialTelemetry : public InputSource {
|
||||||
|
public:
|
||||||
|
BluetoothSerialTelemetry();
|
||||||
|
void onStart() override;
|
||||||
|
InputEvent read() override;
|
||||||
|
|
||||||
|
template<typename T, uint8_t Size = 8>
|
||||||
|
struct Averager {
|
||||||
|
std::array<T, Size> buf;
|
||||||
|
unsigned int idx = 0;
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
void add(const T &value) {
|
||||||
|
buf[idx] = value;
|
||||||
|
idx = (idx + 1) % Size;
|
||||||
|
if (count < Size) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T value() const {
|
||||||
|
if (count == 0) {
|
||||||
|
return T{};
|
||||||
|
}
|
||||||
|
long long int sum = 0;
|
||||||
|
for(unsigned int i = 0; i < count; i++) {
|
||||||
|
sum += buf[i];
|
||||||
|
}
|
||||||
|
return sum / count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
BluetoothSerial m_serial;
|
||||||
|
Ringbuf<char, 32> m_ringbuf;
|
||||||
|
CRGB m_color;
|
||||||
|
Averager<int16_t, 32> m_value;
|
||||||
|
};
|
@ -1,107 +1,190 @@
|
|||||||
#include "MQTTTelemetry.h"
|
#include "MQTTTelemetry.h"
|
||||||
|
|
||||||
#ifdef BOARD_ESP8266
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#elif defined(BOARD_ESP32)
|
|
||||||
#include <WiFi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
#include "../../Static.h"
|
#include "../../Static.h"
|
||||||
#include "../../Config.h"
|
#include "../../Config.h"
|
||||||
#include "../../Platform.h"
|
#include "../../Platform.h"
|
||||||
|
|
||||||
WiFiClient wifiClient;
|
struct MQTTDevice {
|
||||||
|
const String id;
|
||||||
|
const String name;
|
||||||
|
const String model;
|
||||||
|
const String softwareVersion;
|
||||||
|
const String manufacturer;
|
||||||
|
const String availabilityTopic;
|
||||||
|
|
||||||
|
void toJson(const JsonObject& json) const {
|
||||||
|
json["name"] = name;
|
||||||
|
json["mdl"] = model;
|
||||||
|
json["sw"] = softwareVersion;
|
||||||
|
json["mf"] = manufacturer;
|
||||||
|
json["ids"][0] = id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const String availTopic = String("renderbug/") + Platform::deviceID() + "/availability";
|
||||||
|
|
||||||
|
const MQTTDevice Device{
|
||||||
|
Platform::deviceID(),
|
||||||
|
Platform::deviceName(),
|
||||||
|
Platform::model(),
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
ESP.getSketchMD5(),
|
||||||
|
#else
|
||||||
|
"",
|
||||||
|
#endif
|
||||||
|
"Phong Robotics",
|
||||||
|
availTopic
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MQTTEntity {
|
||||||
|
const MQTTDevice& device;
|
||||||
|
String name;
|
||||||
|
String entityId;
|
||||||
|
String rootTopic;
|
||||||
|
|
||||||
|
MQTTEntity(const String& domain, const MQTTDevice& device, const String& name) : device(device), name(Platform::deviceName() + " " + name) {
|
||||||
|
entityId = String(device.id) + "-" + name;
|
||||||
|
rootTopic = String("homeassistant/") + domain + String("/renderbug/") + entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String configTopic() const {
|
||||||
|
return rootTopic + "/config";
|
||||||
|
}
|
||||||
|
|
||||||
|
String commandTopic() const {
|
||||||
|
return rootTopic + "/set";
|
||||||
|
}
|
||||||
|
|
||||||
|
String heartbeatTopic() const {
|
||||||
|
return String("renderbug/") + Device.id + "/heartbeat";
|
||||||
|
}
|
||||||
|
|
||||||
|
String statTopic() const {
|
||||||
|
return rootTopic + "/state";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCommandTopic(const char* topic) const {
|
||||||
|
if (strncmp(topic, rootTopic.c_str(), rootTopic.length()) == 0) {
|
||||||
|
return strncmp(&topic[rootTopic.length()], "/set", sizeof("/set")) == 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toJson(JsonDocument& jsonBuf, bool isInteractive = true) const {
|
||||||
|
jsonBuf["~"] = rootTopic.c_str();
|
||||||
|
jsonBuf["name"] = name;
|
||||||
|
jsonBuf["unique_id"] = entityId;
|
||||||
|
if (isInteractive) {
|
||||||
|
jsonBuf["cmd_t"] = "~/set";
|
||||||
|
jsonBuf["ret"] = true;
|
||||||
|
jsonBuf["schema"] = "json";
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
jsonBuf["stat_t"] = "~/state";
|
||||||
|
jsonBuf["json_attr_t"] = heartbeatTopic();
|
||||||
|
jsonBuf["avty_t"] = device.availabilityTopic;
|
||||||
|
device.toJson(jsonBuf.createNestedObject("dev"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MQTTEntity Lightswitch {
|
||||||
|
"light", Device, "lightswitch"
|
||||||
|
};
|
||||||
|
|
||||||
|
const MQTTEntity flashlightSwitch {
|
||||||
|
"switch", Device, "flashlight"
|
||||||
|
};
|
||||||
|
|
||||||
|
const MQTTEntity FPSSensor {
|
||||||
|
"sensor", Device, "fps"
|
||||||
|
};
|
||||||
|
|
||||||
MQTTTelemetry::MQTTTelemetry() : BufferedInputSource("MQTT"),
|
MQTTTelemetry::MQTTTelemetry() : BufferedInputSource("MQTT"),
|
||||||
m_mqtt(PubSubClient(wifiClient)),
|
m_mqtt(m_wifi),
|
||||||
m_logPrinter(this)
|
m_logPrinter(this)
|
||||||
{}
|
{
|
||||||
|
m_debugTopic = String("renderbug/") + Platform::deviceID();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MQTTTelemetry::handleEventOnline(const InputEvent& evt)
|
MQTTTelemetry::handleEventOnline(const InputEvent& evt)
|
||||||
{
|
{
|
||||||
if (!m_mqtt.connected()) {
|
if (!m_mqtt.connected()) {
|
||||||
Log.notice("Connecting to MQTT...");
|
Log.notice("Connecting to MQTT as %s on %s...", Platform::deviceID(), Device.availabilityTopic.c_str());
|
||||||
const IPAddress server(10, 0, 0, 2);
|
if (m_mqtt.connect(Platform::deviceID(), NULL, NULL, Device.availabilityTopic.c_str(), 0, true, "offline")) {
|
||||||
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");
|
Log.notice("Connected to MQTT");
|
||||||
m_needHeartbeat = true;
|
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;
|
StaticJsonDocument<1024> configJson;
|
||||||
configJson["~"] = rootTopic;
|
|
||||||
configJson["name"] = deviceName;
|
Lightswitch.toJson(configJson);
|
||||||
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;
|
int i = 0;
|
||||||
for(const Sequencer::Scene& scene : m_sequencer->scenes()) {
|
for(const Sequencer::Scene& scene : m_sequencer->scenes()) {
|
||||||
configJson["fx_list"][i++] = scene.name;
|
configJson["fx_list"][i++] = scene.name;
|
||||||
}
|
}
|
||||||
|
configJson["brightness"] = true;
|
||||||
|
configJson["rgb"] = true;
|
||||||
|
|
||||||
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];
|
char buf[1024];
|
||||||
serializeJson(configJson, buf, sizeof(buf));
|
serializeJson(configJson, buf, sizeof(buf));
|
||||||
Log.verbose("Publish %s %s", configTopic.c_str(), buf);
|
|
||||||
m_mqtt.publish(configTopic.c_str(), buf, true);
|
Log.verbose("Publish %s %s", Lightswitch.configTopic().c_str(), buf);
|
||||||
m_mqtt.subscribe(m_cmdTopic);
|
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_mqtt.subscribe(flashlightSwitch.commandTopic().c_str());
|
||||||
|
|
||||||
|
configJson.clear();
|
||||||
|
FPSSensor.toJson(configJson, false);
|
||||||
|
configJson["unit_of_meas"] = "Frames/s";
|
||||||
|
serializeJson(configJson, buf, sizeof(buf));
|
||||||
|
|
||||||
|
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
|
||||||
|
struct rst_info resetInfo = *ESP.getResetInfoPtr();
|
||||||
|
if (resetInfo.reason != 0) {
|
||||||
|
char buff[200];
|
||||||
|
sprintf(&buff[0], "Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x", resetInfo.exccause, resetInfo.reason, (resetInfo.reason == 0 ? "DEFAULT" : resetInfo.reason == 1 ? "WDT" : resetInfo.reason == 2 ? "EXCEPTION" : resetInfo.reason == 3 ? "SOFT_WDT" : resetInfo.reason == 4 ? "SOFT_RESTART" : resetInfo.reason == 5 ? "DEEP_SLEEP_AWAKE" : resetInfo.reason == 6 ? "EXT_SYS_RST" : "???"), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3, resetInfo.excvaddr, resetInfo.depc);
|
||||||
|
Log.warning("Previous crash detected! %s", buff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
Log.warning("Could not connect to MQTT");
|
Log.warning("Could not connect to MQTT");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (evt.intent == InputEvent::SetPower) {
|
String statTopic = Lightswitch.statTopic();
|
||||||
|
if (evt.intent == InputEvent::StopThing && String(evt.asString()) == "Flashlight") {
|
||||||
|
String flashlightStatTopic = flashlightSwitch.statTopic();
|
||||||
|
m_mqtt.publish(flashlightStatTopic.c_str(), "OFF");
|
||||||
|
} else if (evt.intent == InputEvent::StartThing && String(evt.asString()) == "Flashlight") {
|
||||||
|
String flashlightStatTopic = flashlightSwitch.statTopic();
|
||||||
|
m_mqtt.publish(flashlightStatTopic.c_str(), "ON");
|
||||||
|
} else if (evt.intent == InputEvent::SetPower) {
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
doc["state"] = evt.asInt() ? "ON" : "OFF";
|
m_isOn = evt.asInt() ? true : false;
|
||||||
|
doc["state"] = m_isOn ? "ON" : "OFF";
|
||||||
serializeJson(doc, buf, sizeof(buf));
|
serializeJson(doc, buf, sizeof(buf));
|
||||||
m_mqtt.publish(m_statTopic, buf);
|
m_mqtt.publish(statTopic.c_str(), buf);
|
||||||
} else if (evt.intent == InputEvent::SetBrightness) {
|
} else if (evt.intent == InputEvent::SetBrightness) {
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
doc["brightness"] = evt.asInt();
|
doc["brightness"] = evt.asInt();
|
||||||
doc["state"] = "ON";
|
doc["state"] = m_isOn ? "ON" : "OFF";
|
||||||
serializeJson(doc, buf, sizeof(buf));
|
serializeJson(doc, buf, sizeof(buf));
|
||||||
m_mqtt.publish(m_statTopic, buf);
|
m_mqtt.publish(statTopic.c_str(), buf);
|
||||||
} else if (evt.intent == InputEvent::SetColor) {
|
} else if (evt.intent == InputEvent::SetColor) {
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
@ -109,18 +192,19 @@ MQTTTelemetry::handleEventOnline(const InputEvent& evt)
|
|||||||
doc["color"]["r"] = color.r;
|
doc["color"]["r"] = color.r;
|
||||||
doc["color"]["g"] = color.g;
|
doc["color"]["g"] = color.g;
|
||||||
doc["color"]["b"] = color.b;
|
doc["color"]["b"] = color.b;
|
||||||
doc["state"] = "ON";
|
doc["state"] = m_isOn ? "ON" : "OFF";
|
||||||
serializeJson(doc, buf, sizeof(buf));
|
serializeJson(doc, buf, sizeof(buf));
|
||||||
m_mqtt.publish(m_statTopic, buf);
|
m_mqtt.publish(statTopic.c_str(), buf);
|
||||||
} else if (evt.intent == InputEvent::SetPattern) {
|
} else if (evt.intent == InputEvent::SetPattern) {
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
doc["effect"] = evt.asString();
|
doc["effect"] = evt.asString();
|
||||||
doc["state"] = "ON";
|
doc["state"] = m_isOn ? "ON" : "OFF";
|
||||||
serializeJson(doc, buf, sizeof(buf));
|
serializeJson(doc, buf, sizeof(buf));
|
||||||
m_mqtt.publish(m_statTopic, buf);
|
m_mqtt.publish(statTopic.c_str(), buf);
|
||||||
} else if (evt.intent == InputEvent::FirmwareUpdate) {
|
} else if (evt.intent == InputEvent::FirmwareUpdate) {
|
||||||
m_mqtt.publish("renderbug/debug/firmware", "firmware update!");
|
String updateTopic = m_debugTopic + "/firmware";
|
||||||
|
m_mqtt.publish(updateTopic.c_str(), "firmware update!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +216,23 @@ MQTTTelemetry::loop()
|
|||||||
OnlineTaskMixin::loop();
|
OnlineTaskMixin::loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MQTTTelemetry::onOnline()
|
||||||
|
{
|
||||||
|
const IPAddress server(10, 0, 0, 2);
|
||||||
|
|
||||||
|
m_needHeartbeat = true;
|
||||||
|
m_mqtt.setServer(server, 1883);
|
||||||
|
m_mqtt.setBufferSize(1024);
|
||||||
|
m_mqtt.setCallback(&MQTTTelemetry::s_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MQTTTelemetry::onOffline()
|
||||||
|
{
|
||||||
|
m_mqtt.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MQTTTelemetry::loopOnline()
|
MQTTTelemetry::loopOnline()
|
||||||
{
|
{
|
||||||
@ -142,89 +243,126 @@ MQTTTelemetry::loopOnline()
|
|||||||
if (m_needHeartbeat) {
|
if (m_needHeartbeat) {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
StaticJsonDocument<512> response;
|
StaticJsonDocument<512> response;
|
||||||
response["fps"] = FastLED.getFPS();
|
response["device_id"] = Platform::deviceID();
|
||||||
response["RSSI"] = WiFi.RSSI();
|
|
||||||
response["localip"] = WiFi.localIP().toString();
|
|
||||||
response["free_ram"] = ESP.getFreeHeap();
|
|
||||||
response["os_version"] = ESP.getSdkVersion();
|
|
||||||
response["sketch_version"] = ESP.getSketchMD5();
|
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));
|
serializeJson(response, buf, sizeof(buf));
|
||||||
m_mqtt.publish(m_attrTopic, buf);
|
String availTopic = m_rootTopic + "/available";
|
||||||
m_mqtt.publish(m_heartbeatTopic, buf);
|
m_mqtt.publish(Lightswitch.heartbeatTopic().c_str(), buf);
|
||||||
Log.notice("Heartbeat: %s", buf);
|
m_mqtt.publish(Device.availabilityTopic.c_str(), "online");
|
||||||
|
//Log.notice("Heartbeat: %s", buf);
|
||||||
|
|
||||||
|
String fpsCounter = String(FastLED.getFPS());
|
||||||
|
m_mqtt.publish(FPSSensor.statTopic().c_str(), fpsCounter.c_str());
|
||||||
|
|
||||||
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;
|
m_needHeartbeat = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MQTTTelemetry::callback(char* topic, byte* payload, unsigned int length)
|
MQTTTelemetry::callback(char* topic, const char* payload)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument doc(1024);
|
setEvent(InputEvent::NetworkActivity);
|
||||||
deserializeJson(doc, payload, length);
|
if (flashlightSwitch.isCommandTopic(topic)) {
|
||||||
|
if (!strncmp((char*)payload, "ON", sizeof("ON"))) {
|
||||||
if (doc.containsKey("state")) {
|
Log.notice("Turning on flashlight");
|
||||||
if (doc["state"] == "ON") {
|
setEvent(InputEvent{InputEvent::SetPower, true});
|
||||||
setEvent(InputEvent{InputEvent::SetPower, true});
|
setEvent(InputEvent{InputEvent::SetPattern, "Flashlight"});
|
||||||
} else if (doc["state"] == "OFF") {
|
setEvent(InputEvent{InputEvent::SetBrightness, 255});
|
||||||
setEvent(InputEvent{InputEvent::SetPower, false});
|
} else if (!strncmp((char*)payload, "OFF", sizeof("OFF"))) {
|
||||||
}
|
Log.notice("Turning off flashlight");
|
||||||
}
|
setEvent(InputEvent{InputEvent::SetPattern, "Idle"});
|
||||||
|
|
||||||
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});
|
|
||||||
}
|
}
|
||||||
}
|
} else if (Lightswitch.isCommandTopic(topic)) {
|
||||||
|
StaticJsonDocument<512> doc;
|
||||||
|
deserializeJson(doc, payload);
|
||||||
|
|
||||||
if (doc.containsKey("pixelCount")) {
|
if (doc.containsKey("state")) {
|
||||||
setEvent(InputEvent{InputEvent::SetDisplayLength, (int)doc["pixelCount"]});
|
if (doc["state"] == "ON") {
|
||||||
}
|
Log.notice("Turning on power");
|
||||||
|
setEvent(InputEvent{InputEvent::SetPower, true});
|
||||||
|
} else if (doc["state"] == "OFF") {
|
||||||
|
Log.notice("Turning off power");
|
||||||
|
setEvent(InputEvent{InputEvent::SetPattern, "Idle"});
|
||||||
|
setEvent(InputEvent{InputEvent::SetPower, false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (doc.containsKey("startPixel")) {
|
if (doc.containsKey("start")) {
|
||||||
setEvent(InputEvent{InputEvent::SetDisplayOffset, (int)doc["startPixel"]});
|
strcpy(m_patternBuf, doc["start"].as<const char*>());
|
||||||
}
|
setEvent(InputEvent{InputEvent::StartThing, m_patternBuf});
|
||||||
|
}
|
||||||
|
|
||||||
if (doc.containsKey("save")) {
|
if (doc.containsKey("stop")) {
|
||||||
setEvent(InputEvent{InputEvent::SaveConfigurationRequest});
|
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("effect")) {
|
if (doc.containsKey("pixelCount")) {
|
||||||
strcpy(m_patternBuf, doc["effect"].as<const char*>());
|
setEvent(InputEvent{InputEvent::SetDisplayLength, (int)doc["pixelCount"]});
|
||||||
setEvent(InputEvent{InputEvent::SetPattern, m_patternBuf});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (doc.containsKey("color")) {
|
if (doc.containsKey("startPixel")) {
|
||||||
uint8_t r = doc["color"]["r"];
|
setEvent(InputEvent{InputEvent::SetDisplayOffset, (int)doc["startPixel"]});
|
||||||
uint8_t g = doc["color"]["g"];
|
}
|
||||||
uint8_t b = doc["color"]["b"];
|
|
||||||
setEvent(InputEvent{InputEvent::SetColor, CRGB(r, g, b)});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doc.containsKey("brightness")) {
|
if (doc.containsKey("save")) {
|
||||||
setEvent(InputEvent{InputEvent::SetBrightness, (int)doc["brightness"]});
|
setEvent(InputEvent{InputEvent::SaveConfigurationRequest});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.containsKey("restart")) {
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
ESP.wdtDisable();
|
||||||
|
ESP.restart();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.containsKey("reconnect")) {
|
||||||
|
m_mqtt.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.containsKey("ping")) {
|
||||||
|
m_needHeartbeat = true;
|
||||||
|
Log.notice("Queuing up heartbeat");
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
MQTTTelemetry::s_callback(char* topic, byte* payload, unsigned int length)
|
MQTTTelemetry::s_callback(char* topic, byte* payload, unsigned int length)
|
||||||
{
|
{
|
||||||
Static<MQTTTelemetry>::instance()->callback(topic, payload, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC_ALLOC(MQTTTelemetry);
|
STATIC_ALLOC(MQTTTelemetry);
|
||||||
|
@ -6,6 +6,13 @@
|
|||||||
|
|
||||||
#include "../../Sequencer.h"
|
#include "../../Sequencer.h"
|
||||||
|
|
||||||
|
#ifdef BOARD_ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#elif defined(BOARD_ESP32)
|
||||||
|
#include <WiFi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
||||||
public:
|
public:
|
||||||
MQTTTelemetry();
|
MQTTTelemetry();
|
||||||
@ -22,7 +29,9 @@ class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
|||||||
if (byte == '\n') {
|
if (byte == '\n') {
|
||||||
size_t bufSize = buf.write(outBuf);
|
size_t bufSize = buf.write(outBuf);
|
||||||
outBuf[std::min(sizeof(outBuf), bufSize)] = 0;
|
outBuf[std::min(sizeof(outBuf), bufSize)] = 0;
|
||||||
telemetry->m_mqtt.publish(telemetry->m_logTopic, outBuf);
|
Serial.println(outBuf);
|
||||||
|
String logTopic = telemetry->m_debugTopic + "/log";
|
||||||
|
telemetry->m_mqtt.publish(logTopic.c_str(), outBuf);
|
||||||
} else {
|
} else {
|
||||||
buf.insert(byte);
|
buf.insert(byte);
|
||||||
}
|
}
|
||||||
@ -39,20 +48,22 @@ class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
|||||||
|
|
||||||
void loopOnline() override;
|
void loopOnline() override;
|
||||||
|
|
||||||
private:
|
void onOnline() override;
|
||||||
char m_statTopic[100];
|
void onOffline() override;
|
||||||
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);
|
private:
|
||||||
|
String m_rootTopic;
|
||||||
|
String m_debugTopic;
|
||||||
|
|
||||||
|
void callback(char* topic, const char* payload);
|
||||||
|
|
||||||
static void s_callback(char* topic, byte* payload, unsigned int length);
|
static void s_callback(char* topic, byte* payload, unsigned int length);
|
||||||
char m_patternBuf[48];
|
char m_patternBuf[48];
|
||||||
bool m_needHeartbeat = false;
|
bool m_needHeartbeat = false;
|
||||||
|
bool m_isOn = true;
|
||||||
|
|
||||||
Sequencer *m_sequencer = 0;
|
Sequencer *m_sequencer = 0;
|
||||||
|
WiFiClient m_wifi;
|
||||||
PubSubClient m_mqtt;
|
PubSubClient m_mqtt;
|
||||||
LogPrinter m_logPrinter;
|
LogPrinter m_logPrinter;
|
||||||
};
|
};
|
||||||
|
@ -55,13 +55,16 @@ public:
|
|||||||
// Grab the physical pixel we'll start with
|
// Grab the physical pixel we'll start with
|
||||||
PhysicalCoordinates startPos = map->virtualToPhysicalCoords({m_pos, 0});
|
PhysicalCoordinates startPos = map->virtualToPhysicalCoords({m_pos, 0});
|
||||||
PhysicalCoordinates endPos = map->virtualToPhysicalCoords({m_pos + width, 0});
|
PhysicalCoordinates endPos = map->virtualToPhysicalCoords({m_pos + width, 0});
|
||||||
int scaledWidth = std::abs(endPos.x - startPos.x);
|
uint8_t scaledWidth = std::abs(endPos.x - startPos.x);
|
||||||
|
|
||||||
//Log.notice("blob w=%d x=%d", scaledWidth, startPos.x);
|
//Log.notice("blob w=%d x=%d", scaledWidth, startPos.x);
|
||||||
for(uint8_t i = 0;i < scaledWidth; i++) {
|
for(uint8_t i = 0;i < scaledWidth; i++) {
|
||||||
// Blobs desaturate towards their tail
|
// Blobs desaturate towards their tail
|
||||||
//Log.notice("blob i=%d w=%d x=%d", i, scaledWidth, startPos.x);
|
//Log.notice("blob i=%d w=%d x=%d", i, scaledWidth, startPos.x);
|
||||||
CHSV blobColor(m_hue, m_saturation, quadwave8((i / (double)scaledWidth) * m_brightness));
|
uint8_t scalePct = map8(i, 0, scaledWidth);
|
||||||
|
uint8_t val = lerp8by8(0, m_brightness, scalePct);
|
||||||
|
//CHSV blobColor(m_hue, m_saturation, quadwave8((i / (double)scaledWidth) * m_brightness));
|
||||||
|
CHSV blobColor(m_hue, m_saturation, quadwave8(val));
|
||||||
|
|
||||||
PhysicalCoordinates pos{startPos.x + (i*m_fadeDir), 0};
|
PhysicalCoordinates pos{startPos.x + (i*m_fadeDir), 0};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user