port to platformio
This commit is contained in:
parent
9a3bf84214
commit
a6534bcb20
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
bin/*
|
bin/*
|
||||||
*.bin
|
*.bin
|
||||||
|
.pio
|
||||||
|
@ -1,164 +0,0 @@
|
|||||||
#include "./Config.h"
|
|
||||||
#include "./Static.h"
|
|
||||||
|
|
||||||
HardwareConfig
|
|
||||||
HardwareConfig::load() {
|
|
||||||
HardwareConfig ret;
|
|
||||||
EEPROM.get(0, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
HardwareConfig::save() {
|
|
||||||
HardwareConfig dataCopy{*this};
|
|
||||||
dataCopy.checksum = getCRC();
|
|
||||||
EEPROM.put(0, dataCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearCoordinateMapping
|
|
||||||
HardwareConfig::toCoordMap() const
|
|
||||||
{
|
|
||||||
auto pixelCount = min(HardwareConfig::MAX_LED_NUM, max(1, data.pixelCount));
|
|
||||||
auto startPixel = min(pixelCount, max(1, data.startPixel));
|
|
||||||
return LinearCoordinateMapping{pixelCount, startPixel};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
HardwareConfig::isValid() const
|
|
||||||
{
|
|
||||||
return version == 1 && checksum == getCRC() && data.pixelCount <= MAX_LED_NUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t
|
|
||||||
HardwareConfig::getCRC() const
|
|
||||||
{
|
|
||||||
const unsigned char* message = reinterpret_cast<const unsigned char*>(&data);
|
|
||||||
constexpr uint8_t length = sizeof(data);
|
|
||||||
unsigned char i, j, crc = 0;
|
|
||||||
for(i = 0; i < length; i++) {
|
|
||||||
crc ^= message[i];
|
|
||||||
for(j = 0; j < 8; j++) {
|
|
||||||
if (crc & 1) {
|
|
||||||
crc ^= CRC7_POLY;
|
|
||||||
}
|
|
||||||
crc >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigService::onStart()
|
|
||||||
{
|
|
||||||
Log.info("Starting configuration service...");
|
|
||||||
m_config = HardwareConfig::load();
|
|
||||||
if (m_config.isValid()) {
|
|
||||||
Log.info("Configuration found!");
|
|
||||||
} else {
|
|
||||||
Log.info("No configuration found. Writing defaults...");
|
|
||||||
m_config = HardwareConfig{};
|
|
||||||
m_config.save();
|
|
||||||
}
|
|
||||||
m_coordMap = m_config.toCoordMap();
|
|
||||||
|
|
||||||
m_pixelCount = AnimatedNumber{max(1, m_coordMap.pixelCount)};
|
|
||||||
m_startPixel = AnimatedNumber{max(0, m_coordMap.startPixel)};
|
|
||||||
Log.info("Configured to use %d pixels, starting at %d", m_pixelCount.value(), m_startPixel.value());
|
|
||||||
Log.info("Loading task states...");
|
|
||||||
for(int i = 0; i < 32; i++) {
|
|
||||||
auto svc = m_config.data.serviceStates[i];
|
|
||||||
if (strlen(svc.name) > 0) {
|
|
||||||
Log.info("* %s: %s", svc.name, svc.isRunning ? "RUNNING" : "STOPPED");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigService::onConnected()
|
|
||||||
{
|
|
||||||
Log.info("Connecting photon configuration...");
|
|
||||||
Particle.function("pixelCount", &ConfigService::setPixelCount, this);
|
|
||||||
Particle.function("startPixel", &ConfigService::setStartPixel, this);
|
|
||||||
|
|
||||||
Particle.function("save", &ConfigService::photonSave, this);
|
|
||||||
Particle.variable("pixelCount", m_pixelCountInt);
|
|
||||||
Particle.variable("startPixel", m_startPixelInt);
|
|
||||||
|
|
||||||
publishConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigService::loop()
|
|
||||||
{
|
|
||||||
//m_startPixel.update();
|
|
||||||
//m_pixelCount.update();
|
|
||||||
//m_coordMap.pixelCount = max(1, m_pixelCount.value());
|
|
||||||
//m_coordMap.startPixel = max(0, m_startPixel.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigService::handleEvent(const InputEvent &evt)
|
|
||||||
{
|
|
||||||
switch(evt.intent) {
|
|
||||||
case InputEvent::NetworkStatus:
|
|
||||||
onConnected();
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayLength:
|
|
||||||
Log.info("Updating pixel count from %d to %d", m_coordMap.pixelCount, evt.asInt());
|
|
||||||
m_config.data.pixelCount = evt.asInt();
|
|
||||||
m_coordMap = m_config.toCoordMap();
|
|
||||||
m_pixelCountInt = evt.asInt();
|
|
||||||
//m_pixelCount = m_config.data.pixelCount;
|
|
||||||
//m_coordMap.pixelCount = evt.asInt();
|
|
||||||
Log.info("Count is now %d", m_coordMap.pixelCount);
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayOffset:
|
|
||||||
Log.info("Updating pixel offset from %d to %d", m_coordMap.startPixel, evt.asInt());
|
|
||||||
m_config.data.startPixel = evt.asInt();
|
|
||||||
m_coordMap = m_config.toCoordMap();
|
|
||||||
m_startPixelInt = evt.asInt();
|
|
||||||
Log.info("Offset is now %d", m_coordMap.startPixel);
|
|
||||||
//m_coordMap.startPixel = evt.asInt();
|
|
||||||
//m_startPixel = m_config.data.startPixel;
|
|
||||||
break;
|
|
||||||
case InputEvent::SaveConfigurationRequest:
|
|
||||||
Log.info("Saving configuration");
|
|
||||||
m_config.save();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigService::publishConfig() const
|
|
||||||
{
|
|
||||||
char buf[255];
|
|
||||||
snprintf(buf, sizeof(buf), "{\"pixels\": \"%d\", \"offset\": \"%d\"}", m_config.data.pixelCount, m_config.data.startPixel);
|
|
||||||
Particle.publish("renderbug/config", buf, PRIVATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ConfigService::photonSave(String command)
|
|
||||||
{
|
|
||||||
m_config.save();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ConfigService::setPixelCount(String command)
|
|
||||||
{
|
|
||||||
m_config.data.pixelCount = command.toInt();
|
|
||||||
m_pixelCount = m_config.data.pixelCount;
|
|
||||||
publishConfig();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
ConfigService::setStartPixel(String command)
|
|
||||||
{
|
|
||||||
m_config.data.startPixel = command.toInt();
|
|
||||||
m_startPixel = m_config.data.startPixel;
|
|
||||||
publishConfig();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC_ALLOC(ConfigService);
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/FastLED.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/FastLED.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/FastSPI_LED2.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/LICENSE
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/PORTING.md
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/README.md
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/bitswap.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/chipsets.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/clockless_arm_stm32.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/color.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/colorpalettes.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/colorpalettes.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/colorutils.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/colorutils.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/controller.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/delay.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/dmx.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/docs
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/examples
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastled_arm_stm32.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastled_config.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastpin.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastpin_arm_stm32.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi_bitbang.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi_dma.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi_nop.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi_ref.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/fastspi_types.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/hsv2rgb.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/hsv2rgb.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/keywords.txt
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/led_sysdefs.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/led_sysdefs_arm_stm32.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/lib8tion.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/lib8tion.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/noise.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/noise.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/pixeltypes.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/platforms
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/platforms.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/power_mgt.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/power_mgt.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/preview_changes.txt
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/release_notes.md
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/FastLED/firmware/wiring.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Buffer.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Buffer.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Label.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Label.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/MDNS
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/MDNS.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/MDNS.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Record.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MDNS/src/Record.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MQTT/src/MQTT
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MQTT/src/MQTT.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/MQTT/src/MQTT.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/ParticleWebLog/src/ParticleWebLog.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/ParticleWebLog/src/ParticleWebLog.h
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/WebDuino/src/WebDuino
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/WebDuino/src/WebDuino.cpp
|
|
@ -1 +0,0 @@
|
|||||||
/home/tdfischer/.po-util/lib/WebDuino/src/WebDuino.h
|
|
@ -1,780 +0,0 @@
|
|||||||
#define RENDERBUG_VERSION 1
|
|
||||||
#define PLATFORM_PHOTON
|
|
||||||
//#define PLATFORM_ESP2800
|
|
||||||
|
|
||||||
#include "FastLED/FastLED.h"
|
|
||||||
#include "Figments/Figments.h"
|
|
||||||
|
|
||||||
#include "Static.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "colors.h"
|
|
||||||
|
|
||||||
#include "WebTelemetry.cpp"
|
|
||||||
#include "MDNSService.cpp"
|
|
||||||
#include "Sequencer.h"
|
|
||||||
|
|
||||||
#include "animations/Power.cpp"
|
|
||||||
#include "animations/SolidAnimation.cpp"
|
|
||||||
#include "animations/Chimes.cpp"
|
|
||||||
#include "animations/Flashlight.cpp"
|
|
||||||
#include "animations/Drain.cpp"
|
|
||||||
#include "animations/UpdateStatus.h"
|
|
||||||
|
|
||||||
#include "inputs/ColorCycle.h"
|
|
||||||
#include "inputs/Buttons.h"
|
|
||||||
#include "inputs/MPU6050.h"
|
|
||||||
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
#include "platform/particle/inputs/Photon.h"
|
|
||||||
#include "platform/particle/inputs/CloudStatus.h"
|
|
||||||
#include "platform/particle/PhotonTelemetry.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SerialLogHandler logHandler;
|
|
||||||
|
|
||||||
using namespace NSFastLED;
|
|
||||||
|
|
||||||
#define MAX_BRIGHTNESS 255
|
|
||||||
//#define PSU_MILLIAMPS 4800
|
|
||||||
//#define PSU_MILLIAMPS 500
|
|
||||||
//#define PSU_MILLIAMPS 1000
|
|
||||||
#define PSU_MILLIAMPS 2000
|
|
||||||
|
|
||||||
// Enable system thread, so rendering happens while booting
|
|
||||||
SYSTEM_THREAD(ENABLED);
|
|
||||||
|
|
||||||
// Setup FastLED and the display
|
|
||||||
CRGB leds[HardwareConfig::MAX_LED_NUM];
|
|
||||||
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
|
|
||||||
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) {
|
|
||||||
uint8_t brightness = brighten8_video(beatsin8(60));
|
|
||||||
auto coords = Static<ConfigService>::instance()->coordMap();
|
|
||||||
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
|
|
||||||
if (i < coords->startPixel || i > coords->startPixel + coords->pixelCount) {
|
|
||||||
dpy->pixelAt(i) += CRGB(brightness, 0, 0);
|
|
||||||
} else {
|
|
||||||
dpy->pixelAt(i) += CRGB(255 - brightness, 255 - brightness, 255 - brightness);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class InputBlip: public Figment {
|
|
||||||
public:
|
|
||||||
InputBlip() : Figment("InputBlip", Task::Stopped) {}
|
|
||||||
|
|
||||||
void handleEvent(const InputEvent& evt) override {
|
|
||||||
if (evt.intent != InputEvent::None) {
|
|
||||||
m_time = qadd8(m_time, 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void loop() override {
|
|
||||||
if (m_time > 0) {
|
|
||||||
m_time--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void render(Display* dpy) const override {
|
|
||||||
if (m_time > 0) {
|
|
||||||
dpy->pixelAt(0) = CRGB(0, brighten8_video(ease8InOutApprox(m_time)), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
uint8_t m_time = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
InputBlip inputBlip;
|
|
||||||
|
|
||||||
InputFunc randomPulse([]() {
|
|
||||||
static unsigned int pulse = 0;
|
|
||||||
EVERY_N_MILLISECONDS(25) {
|
|
||||||
if (pulse == 0) {
|
|
||||||
pulse = random(25) + 25;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pulse > 0) {
|
|
||||||
if (random(255) >= 25) {
|
|
||||||
pulse--;
|
|
||||||
return InputEvent{InputEvent::Acceleration, beatsin16(60 * 8, 0, 4972)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InputEvent{};
|
|
||||||
}, "Pulse", Task::Running);
|
|
||||||
|
|
||||||
InputMapper keyMap([](const InputEvent& evt) {
|
|
||||||
if (evt.intent == InputEvent::UserInput) {
|
|
||||||
Buttons::Chord chord = (Buttons::Chord)evt.asInt();
|
|
||||||
switch(chord) {
|
|
||||||
case Buttons::Circle:
|
|
||||||
return InputEvent::PowerToggle;
|
|
||||||
break;
|
|
||||||
case Buttons::Triangle:
|
|
||||||
return InputEvent::NextPattern;
|
|
||||||
break;
|
|
||||||
case Buttons::Cross:
|
|
||||||
return InputEvent::UserInput;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InputEvent::None;
|
|
||||||
});
|
|
||||||
|
|
||||||
ChimesAnimation chimes{Task::Stopped};
|
|
||||||
SolidAnimation solid{Task::Running};
|
|
||||||
DrainAnimation drain{Task::Stopped};
|
|
||||||
Flashlight flashlight{Task::Stopped};
|
|
||||||
|
|
||||||
Sequencer sequencer{{
|
|
||||||
{"Idle", {"Solid", "MPU5060", "Pulse", "Hackerbots", "Kieryn", "CircadianRhythm"}},
|
|
||||||
{"Solid", {"Solid", "MPU5060", "Pulse", "CircadianRhythm"}},
|
|
||||||
{"Interactive", {"Drain", "CircadianRhythm"}},
|
|
||||||
{"Flashlight", {"Flashlight"}},
|
|
||||||
{"Nightlight", {"Drain", "Pulse", "Noisebridge"}},
|
|
||||||
{"Gay", {"Solid", "Pulse", "Rainbow", "Hackerbots", "Kieryn"}},
|
|
||||||
{"Acid", {"Chimes", "Pulse", "MPU5060", "Hackerbots", "Rainbow"}},
|
|
||||||
}};
|
|
||||||
|
|
||||||
|
|
||||||
// Render all layers to the displays
|
|
||||||
Renderer renderer{
|
|
||||||
//{&dpy, &neckDisplay},
|
|
||||||
{&dpy},
|
|
||||||
{
|
|
||||||
&chimes,
|
|
||||||
&drain,
|
|
||||||
&solid,
|
|
||||||
&flashlight,
|
|
||||||
Static<UpdateStatus>::instance(),
|
|
||||||
&displayClip,
|
|
||||||
&inputBlip,
|
|
||||||
&power,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Renderer configRenderer{
|
|
||||||
{&dpy},
|
|
||||||
{&drain, &configDisplay, &inputBlip, &power}
|
|
||||||
};
|
|
||||||
|
|
||||||
MDNSService mdnsService;
|
|
||||||
|
|
||||||
class OnlineTaskMixin : public virtual Loopable {
|
|
||||||
public:
|
|
||||||
void handleEvent(const InputEvent &evt) override {
|
|
||||||
if (evt.intent == InputEvent::NetworkStatus) {
|
|
||||||
m_online = evt.asInt();
|
|
||||||
}
|
|
||||||
if (m_online) {
|
|
||||||
handleEventOnline(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void handleEventOnline(const InputEvent &evt) = 0;
|
|
||||||
|
|
||||||
void loop() override {
|
|
||||||
if (m_online) {
|
|
||||||
loopOnline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void loopOnline() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_online = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "MQTT/MQTT.h"
|
|
||||||
|
|
||||||
class MQTTTelemetry : public BufferedInputSource, OnlineTaskMixin {
|
|
||||||
public:
|
|
||||||
MQTTTelemetry() : BufferedInputSource("MQTT"), m_client("relay.malloc.hackerbots.net", 1883, 512, 15, MQTTTelemetry::s_callback, true) {
|
|
||||||
strcpy(m_deviceName, System.deviceID().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() override {
|
|
||||||
BufferedInputSource::loop();
|
|
||||||
OnlineTaskMixin::loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleEventOnline(const InputEvent& event) override {
|
|
||||||
char response[255];
|
|
||||||
if (event.intent == InputEvent::SetPower) {
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("state").value(event.asInt() == 0 ? "OFF" : "ON");
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
||||||
} else if (event.intent == InputEvent::SetBrightness) {
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("brightness").value(event.asInt());
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
||||||
} else if (event.intent == InputEvent::SetColor) {
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("color").beginObject();
|
|
||||||
CRGB rgb = event.asRGB();
|
|
||||||
writer.name("r").value(rgb.r);
|
|
||||||
writer.name("g").value(rgb.g);
|
|
||||||
writer.name("b").value(rgb.b);
|
|
||||||
writer.endObject();
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
||||||
} else if (event.intent == InputEvent::SetPattern) {
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("effect").value(event.asString());
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish(m_statTopic, response, MQTT::QOS1);
|
|
||||||
} else {
|
|
||||||
if (m_lastIntent != event.intent) {
|
|
||||||
m_lastIntent = event.intent;
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("intent").value(event.intent);
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish("renderbug/events", response, MQTT::QOS1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loopOnline() override {
|
|
||||||
if (m_client.isConnected()) {
|
|
||||||
m_client.loop();
|
|
||||||
EVERY_N_SECONDS(10) {
|
|
||||||
char heartbeatBuf[255];
|
|
||||||
JSONBufferWriter writer(heartbeatBuf, sizeof(heartbeatBuf));
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("fps").value(FastLED.getFPS());
|
|
||||||
writer.name("os_version").value(System.version());
|
|
||||||
writer.name("free_ram").value((unsigned int)System.freeMemory());
|
|
||||||
//writer.name("uptime").value(System.uptime());
|
|
||||||
/*WiFiSignal sig = WiFi.RSSI();
|
|
||||||
writer.name("strength").value(sig.getStrength());
|
|
||||||
writer.name("quality").value(sig.getQuality());*/
|
|
||||||
writer.name("RSSI").value(WiFi.RSSI());
|
|
||||||
writer.name("SSID").value(WiFi.SSID());
|
|
||||||
//writer.name("MAC").value(WiFi.macAddress());
|
|
||||||
writer.name("localip").value(WiFi.localIP().toString());
|
|
||||||
writer.name("startPixel").value(Static<ConfigService>::instance()->coordMap()->startPixel);
|
|
||||||
writer.name("pixelCount").value(Static<ConfigService>::instance()->coordMap()->pixelCount);
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
m_client.publish(m_attrTopic, heartbeatBuf);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_client.connect("renderbug_" + String(m_deviceName) + "_" + String(Time.now()));
|
|
||||||
char response[512];
|
|
||||||
JSONBufferWriter writer(response, sizeof(response));
|
|
||||||
|
|
||||||
String devTopic = String("homeassistant/light/renderbug/") + m_deviceName;
|
|
||||||
writer.beginObject();
|
|
||||||
writer.name("~").value(devTopic.c_str());
|
|
||||||
writer.name("name").value("Renderbug");
|
|
||||||
writer.name("unique_id").value(m_deviceName);
|
|
||||||
writer.name("cmd_t").value("~/set");
|
|
||||||
writer.name("stat_t").value("~/set");
|
|
||||||
writer.name("json_attr_t").value("~/attributes");
|
|
||||||
writer.name("schema").value("json");
|
|
||||||
writer.name("brightness").value(true);
|
|
||||||
writer.name("rgb").value(true);
|
|
||||||
writer.name("ret").value(true);
|
|
||||||
|
|
||||||
writer.name("dev").beginObject();
|
|
||||||
writer.name("name").value("Renderbug");
|
|
||||||
writer.name("mdl").value("Renderbug");
|
|
||||||
writer.name("sw").value(RENDERBUG_VERSION);
|
|
||||||
writer.name("mf").value("Phong Robotics");
|
|
||||||
|
|
||||||
writer.name("ids").beginArray();
|
|
||||||
writer.value(m_deviceName);
|
|
||||||
writer.endArray();
|
|
||||||
|
|
||||||
writer.endObject();
|
|
||||||
|
|
||||||
writer.name("fx_list").beginArray();
|
|
||||||
for(const Sequencer::Scene& scene : sequencer.scenes()) {
|
|
||||||
writer.value(scene.name);
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
|
|
||||||
writer.endObject();
|
|
||||||
writer.buffer()[std::min(writer.bufferSize(), writer.dataSize())] = 0;
|
|
||||||
|
|
||||||
String configTopic = devTopic + "/config";
|
|
||||||
m_client.publish(configTopic, response, true);
|
|
||||||
|
|
||||||
String statTopic = devTopic + "/state";
|
|
||||||
String cmdTopic = devTopic + "/set";
|
|
||||||
String attrTopic = devTopic + "/attributes";
|
|
||||||
strcpy(m_statTopic, statTopic.c_str());
|
|
||||||
strcpy(m_attrTopic, attrTopic.c_str());
|
|
||||||
strcpy(m_cmdTopic, cmdTopic.c_str());
|
|
||||||
m_client.subscribe(m_cmdTopic, MQTT::QOS1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
MainLoop::instance()->dispatch(InputEvent::NetworkActivity);
|
|
||||||
if (!strcmp(topic, m_cmdTopic)) {
|
|
||||||
JSONValue cmd = JSONValue::parseCopy((char*)payload, length);
|
|
||||||
JSONObjectIterator cmdIter(cmd);
|
|
||||||
while(cmdIter.next()) {
|
|
||||||
if (cmdIter.name() == "state" && cmdIter.value().toString() == "ON") {
|
|
||||||
setEvent({InputEvent::SetPower, 1});
|
|
||||||
} else if (cmdIter.name() == "state" && cmdIter.value().toString() == "OFF") {
|
|
||||||
setEvent({InputEvent::SetPower, 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "color") {
|
|
||||||
JSONObjectIterator colorIter(cmdIter.value());
|
|
||||||
uint8_t r, g, b;
|
|
||||||
while(colorIter.next()) {
|
|
||||||
if (colorIter.name() == "r") {
|
|
||||||
r = colorIter.value().toInt();
|
|
||||||
} else if (colorIter.name() == "g") {
|
|
||||||
g = colorIter.value().toInt();
|
|
||||||
} else if (colorIter.name() == "b") {
|
|
||||||
b = colorIter.value().toInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setEvent(InputEvent{InputEvent::SetColor, CRGB{r, g, b}});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "brightness") {
|
|
||||||
uint8_t brightness = cmdIter.value().toInt();
|
|
||||||
setEvent(InputEvent{InputEvent::SetBrightness, brightness});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "effect") {
|
|
||||||
strcpy(m_patternBuf, (const char*) cmdIter.value().toString());
|
|
||||||
setEvent(InputEvent{InputEvent::SetPattern, m_patternBuf});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "pixelCount") {
|
|
||||||
setEvent(InputEvent{InputEvent::SetDisplayLength, cmdIter.value().toInt()});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "startPixel") {
|
|
||||||
setEvent(InputEvent{InputEvent::SetDisplayOffset, cmdIter.value().toInt()});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdIter.name() == "save") {
|
|
||||||
setEvent(InputEvent{InputEvent::SaveConfigurationRequest});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s_callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
Static<MQTTTelemetry>::instance()->callback(topic, payload, length);
|
|
||||||
};
|
|
||||||
|
|
||||||
MQTT m_client;
|
|
||||||
InputEvent::Intent m_lastIntent;
|
|
||||||
char m_deviceName[100];
|
|
||||||
char m_statTopic[100];
|
|
||||||
char m_attrTopic[100];
|
|
||||||
char m_cmdTopic[100];
|
|
||||||
char m_patternBuf[48];
|
|
||||||
};
|
|
||||||
|
|
||||||
STATIC_ALLOC(MQTTTelemetry);
|
|
||||||
|
|
||||||
WebTelemetry webTelemetry(sequencer);
|
|
||||||
|
|
||||||
// Cycle some random colors
|
|
||||||
ColorSequenceInput<7> noisebridgeCycle{{colorForName("Red").rgb}, "Noisebridge", Task::Stopped};
|
|
||||||
|
|
||||||
ColorSequenceInput<13> kierynCycle{{
|
|
||||||
colorForName("Cerulean").rgb,
|
|
||||||
colorForName("Electric Purple").rgb,
|
|
||||||
colorForName("Emerald").rgb,
|
|
||||||
colorForName("Sky Magenta").rgb
|
|
||||||
}, "Kieryn", Task::Stopped};
|
|
||||||
|
|
||||||
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{{
|
|
||||||
colorForName("Purple").rgb,
|
|
||||||
colorForName("White").rgb,
|
|
||||||
colorForName("Cyan").rgb,
|
|
||||||
}, "Hackerbots", Task::Stopped};
|
|
||||||
|
|
||||||
struct ConfigInputTask: public BufferedInputSource {
|
|
||||||
public:
|
|
||||||
ConfigInputTask() : BufferedInputSource("ConfigInput") {}
|
|
||||||
|
|
||||||
void handleEvent(const InputEvent& evt) override {
|
|
||||||
if (evt.intent == InputEvent::UserInput) {
|
|
||||||
Buttons::Chord chord = (Buttons::Chord) evt.asInt();
|
|
||||||
switch (chord) {
|
|
||||||
case Buttons::Circle:
|
|
||||||
m_currentIntent = nextIntent();
|
|
||||||
Log.info("Next setting... (%d)", m_currentIntent);
|
|
||||||
break;
|
|
||||||
case Buttons::CircleTriangle:
|
|
||||||
Log.info("Increment...");
|
|
||||||
increment();
|
|
||||||
break;
|
|
||||||
case Buttons::CircleCross:
|
|
||||||
Log.info("Decrement...");
|
|
||||||
decrement();
|
|
||||||
break;
|
|
||||||
case Buttons::Triangle:
|
|
||||||
Log.info("Save...");
|
|
||||||
setEvent(InputEvent::SaveConfigurationRequest);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
InputEvent::Intent m_currentIntent = InputEvent::SetDisplayLength;
|
|
||||||
|
|
||||||
void decrement() {
|
|
||||||
int current = 0;
|
|
||||||
switch (m_currentIntent) {
|
|
||||||
case InputEvent::SetDisplayLength:
|
|
||||||
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayOffset:
|
|
||||||
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
setEvent(InputEvent{m_currentIntent, current - 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
void increment() {
|
|
||||||
int current = 0;
|
|
||||||
switch (m_currentIntent) {
|
|
||||||
case InputEvent::SetDisplayLength:
|
|
||||||
current = Static<ConfigService>::instance()->coordMap()->pixelCount;
|
|
||||||
break;
|
|
||||||
case InputEvent::SetDisplayOffset:
|
|
||||||
current = Static<ConfigService>::instance()->coordMap()->startPixel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
setEvent(InputEvent{m_currentIntent, current + 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
InputEvent::Intent nextIntent() {
|
|
||||||
switch (m_currentIntent) {
|
|
||||||
case InputEvent::SetDisplayLength:
|
|
||||||
return InputEvent::SetDisplayOffset;
|
|
||||||
case InputEvent::SetDisplayOffset:
|
|
||||||
return InputEvent::SetDisplayLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Phase {
|
|
||||||
Null,
|
|
||||||
AstronomicalDay,
|
|
||||||
NauticalDay,
|
|
||||||
CivilDay,
|
|
||||||
CivilNight,
|
|
||||||
NauticalNight,
|
|
||||||
AstronomicalNight,
|
|
||||||
Evening,
|
|
||||||
Bedtime,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScheduleEntry {
|
|
||||||
uint8_t hour;
|
|
||||||
uint8_t brightness;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<ScheduleEntry, 10> schedule{{
|
|
||||||
{0, 0},
|
|
||||||
{5, 0},
|
|
||||||
{6, 0},
|
|
||||||
{7, 10},
|
|
||||||
{8, 80},
|
|
||||||
{11, 120},
|
|
||||||
{18, 200},
|
|
||||||
{19, 255},
|
|
||||||
{22, 120},
|
|
||||||
{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);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputFunc circadianRhythm([]() {
|
|
||||||
static Phase lastPhase = Null;
|
|
||||||
static bool needsUpdate = true;
|
|
||||||
if (Time.isValid()) {
|
|
||||||
EVERY_N_SECONDS(60) {
|
|
||||||
needsUpdate = true;
|
|
||||||
}
|
|
||||||
if (needsUpdate) {
|
|
||||||
needsUpdate = false;
|
|
||||||
return InputEvent{InputEvent::SetBrightness, brightnessForTime(Time.hour(), Time.minute())};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InputEvent{};
|
|
||||||
}, "CircadianRhythm", Task::Running);
|
|
||||||
|
|
||||||
// A special mainloop app for configuring hardware settings that reboots the
|
|
||||||
// device when the user is finished.
|
|
||||||
MainLoop configApp{{
|
|
||||||
// Manage read/write of configuration data
|
|
||||||
Static<ConfigService>::instance(),
|
|
||||||
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
// Update photon telemetry
|
|
||||||
Static<PhotonTelemetry>::instance(),
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Read hardware inputs
|
|
||||||
new Buttons(),
|
|
||||||
|
|
||||||
// Map input buttons to configuration commands
|
|
||||||
new ConfigInputTask(),
|
|
||||||
|
|
||||||
// Fill the entire display with a color, to see size
|
|
||||||
&configDisplay,
|
|
||||||
// Render some basic input feedback
|
|
||||||
&inputBlip,
|
|
||||||
// Render it all
|
|
||||||
&configRenderer,
|
|
||||||
}};
|
|
||||||
|
|
||||||
// Turn on,
|
|
||||||
MainLoop renderbugApp{{
|
|
||||||
|
|
||||||
// Load/update graphics configuration from EEPROM and Particle
|
|
||||||
Static<ConfigService>::instance(),
|
|
||||||
|
|
||||||
// Platform inputs
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
// Particle cloud status
|
|
||||||
Static<CloudStatus>::instance(),
|
|
||||||
|
|
||||||
// Monitor network state and provide particle API events
|
|
||||||
Static<PhotonInput>::instance(),
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Hardware drivers
|
|
||||||
new MPU5060(),
|
|
||||||
new Buttons(),
|
|
||||||
|
|
||||||
// Map buttons to events
|
|
||||||
&keyMap,
|
|
||||||
|
|
||||||
// Pattern sequencer
|
|
||||||
&sequencer,
|
|
||||||
|
|
||||||
// Daily rhythm activities
|
|
||||||
&circadianRhythm,
|
|
||||||
|
|
||||||
// Periodic motion input
|
|
||||||
&randomPulse,
|
|
||||||
|
|
||||||
// Periodic color inputs
|
|
||||||
&noisebridgeCycle,
|
|
||||||
&kierynCycle,
|
|
||||||
&rainbowCycle,
|
|
||||||
&hackerbotsCycle,
|
|
||||||
|
|
||||||
// Animations
|
|
||||||
&chimes,
|
|
||||||
&drain,
|
|
||||||
&solid,
|
|
||||||
&flashlight,
|
|
||||||
|
|
||||||
// Update UI layer
|
|
||||||
&power,
|
|
||||||
&displayClip,
|
|
||||||
Static<UpdateStatus>::instance(),
|
|
||||||
&inputBlip,
|
|
||||||
|
|
||||||
// Render everything
|
|
||||||
&renderer,
|
|
||||||
|
|
||||||
// Platform telemetry
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
// Update photon telemetry
|
|
||||||
Static<PhotonTelemetry>::instance(),
|
|
||||||
|
|
||||||
// Web telemetry UI
|
|
||||||
&webTelemetry,
|
|
||||||
|
|
||||||
// MQTT telemetry
|
|
||||||
Static<MQTTTelemetry>::instance(),
|
|
||||||
|
|
||||||
// Network discovery
|
|
||||||
&mdnsService,
|
|
||||||
#endif
|
|
||||||
}};
|
|
||||||
|
|
||||||
MainLoop &runner = renderbugApp;
|
|
||||||
|
|
||||||
retained bool LAST_BOOT_WAS_FLASH;
|
|
||||||
retained bool LAST_BOOT_WAS_SERIAL;
|
|
||||||
|
|
||||||
struct BootOptions {
|
|
||||||
static void initPins() {
|
|
||||||
pinMode(2, INPUT_PULLDOWN);
|
|
||||||
pinMode(3, INPUT_PULLDOWN);
|
|
||||||
pinMode(4, INPUT_PULLDOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
BootOptions() {
|
|
||||||
isSetup = digitalRead(2) == HIGH;
|
|
||||||
isSerial = digitalRead(3) == HIGH || LAST_BOOT_WAS_SERIAL;
|
|
||||||
isFlash = digitalRead(4) == HIGH;
|
|
||||||
|
|
||||||
LAST_BOOT_WAS_FLASH = isFlash;
|
|
||||||
LAST_BOOT_WAS_SERIAL |= isSerial;
|
|
||||||
|
|
||||||
configStatus.setActive(isSetup);
|
|
||||||
serialStatus.setActive(isSerial);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitForRelease() {
|
|
||||||
while(digitalRead(2) == HIGH || digitalRead(3) == HIGH) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSetup = false;
|
|
||||||
bool isSerial = false;
|
|
||||||
bool isFlash = false;
|
|
||||||
|
|
||||||
LEDStatus serialStatus = LEDStatus(RGB_COLOR_ORANGE, LED_PATTERN_FADE, LED_SPEED_FAST, LED_PRIORITY_BACKGROUND);
|
|
||||||
LEDStatus configStatus = LEDStatus(RGB_COLOR_YELLOW, LED_PATTERN_FADE, LED_SPEED_NORMAL, LED_PRIORITY_IMPORTANT);
|
|
||||||
};
|
|
||||||
|
|
||||||
STARTUP(BootOptions::initPins());
|
|
||||||
|
|
||||||
retained BootOptions bootopts;
|
|
||||||
|
|
||||||
ApplicationWatchdog *wd;
|
|
||||||
|
|
||||||
void watchdogHandler() {
|
|
||||||
for(int i = 0; i < 8; i++) {
|
|
||||||
leds[i] = CRGB(i % 3 ? 35 : 255, 0, 0);
|
|
||||||
}
|
|
||||||
FastLED.show();
|
|
||||||
if (LAST_BOOT_WAS_FLASH) {
|
|
||||||
System.dfu();
|
|
||||||
} else {
|
|
||||||
System.enterSafeMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tune in,
|
|
||||||
void setup() {
|
|
||||||
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
|
||||||
wd = new ApplicationWatchdog(5000, watchdogHandler, 1536);
|
|
||||||
Serial.begin(115200);
|
|
||||||
if (bootopts.isFlash) {
|
|
||||||
System.dfu();
|
|
||||||
}
|
|
||||||
if (bootopts.isSerial) {
|
|
||||||
bootopts.waitForRelease();
|
|
||||||
while(!Serial.isConnected()) {
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
Particle.process();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
Log.info("\xf0\x9f\x94\x8c Serial connected");
|
|
||||||
}
|
|
||||||
Log.info(u8"🐛 Booting Renderbug %s!", System.deviceID().c_str());
|
|
||||||
Log.info(u8"🐞 I am built for %d LEDs running on %dmA", HardwareConfig::MAX_LED_NUM, PSU_MILLIAMPS);
|
|
||||||
#ifdef PLATFORM_PHOTON
|
|
||||||
Log.info(u8"📡 Particle version %s", System.version().c_str());
|
|
||||||
#endif
|
|
||||||
Log.info(u8" Boot pin configuration:");
|
|
||||||
Log.info(u8" 2: Setup - %d", bootopts.isSetup);
|
|
||||||
Log.info(u8" 3: Serial - %d", bootopts.isSerial);
|
|
||||||
Log.info(u8" 4: Flash - %d", bootopts.isFlash);
|
|
||||||
|
|
||||||
Log.info(u8" Setting timezone to UTC-7");
|
|
||||||
Time.zone(-7);
|
|
||||||
|
|
||||||
Log.info(u8"💡 Starting FastLED...");
|
|
||||||
FastLED.addLeds<NEOPIXEL, 6>(leds, HardwareConfig::MAX_LED_NUM);
|
|
||||||
|
|
||||||
if (bootopts.isSetup) {
|
|
||||||
Log.info(u8"🌌 Starting Figment in configuration mode...");
|
|
||||||
runner = configApp;
|
|
||||||
} else {
|
|
||||||
Log.info(u8"🌌 Starting Figment...");
|
|
||||||
}
|
|
||||||
Serial.flush();
|
|
||||||
runner.start();
|
|
||||||
|
|
||||||
Log.info(u8"💽 %lu bytes of free RAM", System.freeMemory());
|
|
||||||
Log.info(u8"🚀 Setup complete! Ready to rock and roll.");
|
|
||||||
Serial.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop out.
|
|
||||||
void loop() {
|
|
||||||
runner.loop();
|
|
||||||
wd->checkin();
|
|
||||||
}
|
|
39
include/README
Normal file
39
include/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
@ -6,5 +6,5 @@
|
|||||||
uint8_t
|
uint8_t
|
||||||
AnimatedNumber::value() const
|
AnimatedNumber::value() const
|
||||||
{
|
{
|
||||||
return NSFastLED::lerp8by8(m_start, m_end, m_idx);
|
return lerp8by8(m_start, m_end, m_idx);
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "FastLED/FastLED.h"
|
#include <FastLED.h>
|
||||||
#include "./Figment.h"
|
#include "./Figment.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -68,16 +68,16 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct AnimatedRGB {
|
struct AnimatedRGB {
|
||||||
NSFastLED::CRGB start;
|
CRGB start;
|
||||||
NSFastLED::CRGB end;
|
CRGB end;
|
||||||
AnimatedNumber pos;
|
AnimatedNumber pos;
|
||||||
|
|
||||||
AnimatedRGB(const NSFastLED::CRGB& color)
|
AnimatedRGB(const CRGB& color)
|
||||||
: start(color), end(color) {}
|
: start(color), end(color) {}
|
||||||
|
|
||||||
AnimatedRGB() {}
|
AnimatedRGB() {}
|
||||||
|
|
||||||
AnimatedRGB& operator=(const NSFastLED::CRGB& rgb) {
|
AnimatedRGB& operator=(const CRGB& rgb) {
|
||||||
start = *this;
|
start = *this;
|
||||||
end = rgb;
|
end = rgb;
|
||||||
pos.set(0, 255);
|
pos.set(0, 255);
|
||||||
@ -88,11 +88,11 @@ struct AnimatedRGB {
|
|||||||
pos.update();
|
pos.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
operator NSFastLED::CRGB() const {
|
operator CRGB() const {
|
||||||
uint8_t red = NSFastLED::lerp8by8(start.red, end.red, pos);
|
uint8_t red = lerp8by8(start.red, end.red, pos);
|
||||||
uint8_t green = NSFastLED::lerp8by8(start.green, end.green, pos);
|
uint8_t green = lerp8by8(start.green, end.green, pos);
|
||||||
uint8_t blue = NSFastLED::lerp8by8(start.blue, end.blue, pos);
|
uint8_t blue = lerp8by8(start.blue, end.blue, pos);
|
||||||
return NSFastLED::CRGB(red, green, blue);
|
return CRGB(red, green, blue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,8 +1,6 @@
|
|||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
using namespace NSFastLED;
|
|
||||||
|
|
||||||
int
|
int
|
||||||
Display::pixelCount() const
|
Display::pixelCount() const
|
||||||
{
|
{
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "FastLED/FastLED.h"
|
#include <FastLED.h>
|
||||||
|
|
||||||
#include "Geometry.h"
|
#include "Geometry.h"
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ struct LinearCoordinateMapping: CoordinateMapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const {
|
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const {
|
||||||
return PhysicalCoordinates{NSFastLED::scale8(pixelCount, virtualCoords.x), 0};
|
return PhysicalCoordinates{scale8(pixelCount, virtualCoords.x), 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const override {
|
int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const override {
|
||||||
@ -35,22 +35,22 @@ struct LinearCoordinateMapping: CoordinateMapping {
|
|||||||
class Display {
|
class Display {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Display(NSFastLED::CRGB* pixels, int pixelCount, const CoordinateMapping* map)
|
Display(CRGB* pixels, int pixelCount, const CoordinateMapping* map)
|
||||||
: m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {}
|
: m_pixels(pixels), m_pixelCount(pixelCount), m_coordMap(map) {}
|
||||||
|
|
||||||
NSFastLED::CRGB& pixelAt(const PhysicalCoordinates coords);
|
CRGB& pixelAt(const PhysicalCoordinates coords);
|
||||||
NSFastLED::CRGB& pixelAt(const VirtualCoordinates coords);
|
CRGB& pixelAt(const VirtualCoordinates coords);
|
||||||
NSFastLED::CRGB& pixelAt(int idx);
|
CRGB& pixelAt(int idx);
|
||||||
|
|
||||||
int pixelCount() const;
|
int pixelCount() const;
|
||||||
NSFastLED::CRGB* pixelBacking() const;
|
CRGB* pixelBacking() const;
|
||||||
const CoordinateMapping* coordinateMapping() const;
|
const CoordinateMapping* coordinateMapping() const;
|
||||||
void clear();
|
void clear();
|
||||||
void clear(const NSFastLED::CRGB& color);
|
void clear(const CRGB& color);
|
||||||
void clear(VirtualCoordinates& start, VirtualCoordinates& end, const NSFastLED::CRGB& color);
|
void clear(VirtualCoordinates& start, VirtualCoordinates& end, const CRGB& color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NSFastLED::CRGB* m_pixels;
|
CRGB* m_pixels;
|
||||||
int m_pixelCount;
|
int m_pixelCount;
|
||||||
const CoordinateMapping* m_coordMap;
|
const CoordinateMapping* m_coordMap;
|
||||||
};
|
};
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "application.h"
|
#include <Arduino.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
class InputEvent;
|
class InputEvent;
|
||||||
@ -25,8 +26,8 @@ struct Task : public virtual Loopable {
|
|||||||
Task(const char* name) : Task(name, Running) {}
|
Task(const char* name) : Task(name, Running) {}
|
||||||
Task(const char* name, State initialState) : name(name), state(initialState) {}
|
Task(const char* name, State initialState) : name(name), state(initialState) {}
|
||||||
|
|
||||||
void start() { Log.info("* Starting %s...", name); state = Running; onStart(); }
|
void start() { state = Running; onStart(); }
|
||||||
void stop() { Log.info("* Stopping %s...", name); onStop(); state = Stopped; }
|
void stop() { onStop(); state = Stopped; }
|
||||||
virtual bool isFigment() const { return false; }
|
virtual bool isFigment() const { return false; }
|
||||||
|
|
||||||
const char* name = 0;
|
const char* name = 0;
|
@ -8,12 +8,12 @@ template<typename T> struct Coordinates {
|
|||||||
T y;
|
T y;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VirtualCoordinates: Coordinates<uint8_t> {
|
struct VirtualCoordinates: Coordinates<uint16_t> {
|
||||||
VirtualCoordinates(uint8_t _x, uint8_t _y) : Coordinates(_x, _y) {}
|
VirtualCoordinates(uint16_t _x, uint16_t _y) : Coordinates(_x, _y) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PhysicalCoordinates: Coordinates<uint8_t> {
|
struct PhysicalCoordinates: Coordinates<uint16_t> {
|
||||||
PhysicalCoordinates(uint8_t _x, uint8_t _y) : Coordinates(_x, _y) {}
|
PhysicalCoordinates(uint16_t _x, uint16_t _y) : Coordinates(_x, _y) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> struct Vector3d {
|
template<typename T> struct Vector3d {
|
@ -1,11 +1,11 @@
|
|||||||
#include "application.h"
|
#include <Arduino.h>
|
||||||
#include "./Input.h"
|
#include "./Input.h"
|
||||||
#include "./MainLoop.h"
|
#include "./MainLoop.h"
|
||||||
|
|
||||||
NSFastLED::CRGB
|
CRGB
|
||||||
Variant::asRGB() const
|
Variant::asRGB() const
|
||||||
{
|
{
|
||||||
return NSFastLED::CRGB(m_value.asRGB[0], m_value.asRGB[1], m_value.asRGB[2]);
|
return CRGB(m_value.asRGB[0], m_value.asRGB[1], m_value.asRGB[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
const char*
|
@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "application.h"
|
#include <Arduino.h>
|
||||||
#include "./Geometry.h"
|
#include "./Geometry.h"
|
||||||
#include "./Figment.h"
|
#include "./Figment.h"
|
||||||
#include "./Ringbuf.h"
|
#include "./Ringbuf.h"
|
||||||
#include "FastLED/FastLED.h"
|
#include <FastLED.h>
|
||||||
|
|
||||||
typedef Vector3d<int> MotionVec;
|
typedef Vector3d<int> MotionVec;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ struct Variant {
|
|||||||
Variant(const char* v)
|
Variant(const char* v)
|
||||||
: type(String), m_value{.asString=v} {}
|
: type(String), m_value{.asString=v} {}
|
||||||
|
|
||||||
Variant(const NSFastLED::CRGB &v)
|
Variant(const CRGB &v)
|
||||||
: type(Color), m_value{.asRGB={v.r, v.g, v.b}} {}
|
: type(Color), m_value{.asRGB={v.r, v.g, v.b}} {}
|
||||||
|
|
||||||
Variant()
|
Variant()
|
||||||
@ -30,7 +30,7 @@ struct Variant {
|
|||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
const char* asString() const;
|
const char* asString() const;
|
||||||
NSFastLED::CRGB asRGB() const;
|
CRGB asRGB() const;
|
||||||
int asInt() const;
|
int asInt() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -142,7 +142,7 @@ private:
|
|||||||
|
|
||||||
class InputMapper: public BufferedInputSource {
|
class InputMapper: public BufferedInputSource {
|
||||||
public:
|
public:
|
||||||
InputMapper(std::function<InputEvent(const InputEvent)> f) : BufferedInputSource(), m_func(f) {}
|
InputMapper(std::function<InputEvent(const InputEvent)> f, const char* name) : BufferedInputSource(name), m_func(f) {}
|
||||||
|
|
||||||
void handleEvent(const InputEvent& evt) override {
|
void handleEvent(const InputEvent& evt) override {
|
||||||
setEvent(m_func(evt));
|
setEvent(m_func(evt));
|
||||||
@ -151,3 +151,28 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::function<InputEvent(const InputEvent)> m_func;
|
std::function<InputEvent(const InputEvent)> m_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OnlineTaskMixin : public virtual Loopable {
|
||||||
|
public:
|
||||||
|
void handleEvent(const InputEvent &evt) override {
|
||||||
|
if (evt.intent == InputEvent::NetworkStatus) {
|
||||||
|
m_online = evt.asInt();
|
||||||
|
}
|
||||||
|
if (m_online) {
|
||||||
|
handleEventOnline(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleEventOnline(const InputEvent &evt) = 0;
|
||||||
|
|
||||||
|
void loop() override {
|
||||||
|
if (m_online) {
|
||||||
|
loopOnline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void loopOnline() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_online = false;
|
||||||
|
};
|
@ -2,6 +2,8 @@
|
|||||||
#include "./Input.h"
|
#include "./Input.h"
|
||||||
#include "./Figment.h"
|
#include "./Figment.h"
|
||||||
|
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
MainLoop::dispatch(const InputEvent& evt)
|
MainLoop::dispatch(const InputEvent& evt)
|
||||||
{
|
{
|
||||||
@ -19,10 +21,12 @@ MainLoop::loop()
|
|||||||
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
|
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
|
||||||
const bool jobState = (evt.intent == InputEvent::StartThing);
|
const bool jobState = (evt.intent == InputEvent::StartThing);
|
||||||
for(auto figmentJob: scheduler.tasks) {
|
for(auto figmentJob: scheduler.tasks) {
|
||||||
if (strcmp(figmentJob->name, evt.asString()) == 0) {
|
if (!strcmp(figmentJob->name, evt.asString())) {
|
||||||
if (jobState) {
|
if (jobState) {
|
||||||
|
//Log.notice("Starting %s", figmentJob->name);
|
||||||
figmentJob->start();
|
figmentJob->start();
|
||||||
} else {
|
} else {
|
||||||
|
//Log.notice("Stopping %s", figmentJob->name);
|
||||||
figmentJob->stop();
|
figmentJob->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,18 +38,19 @@ MainLoop::loop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Task* task : scheduler) {
|
for(Task* task : scheduler) {
|
||||||
//Log.info("Running %s", task->name);
|
//Log.notice("Running %s", task->name);
|
||||||
task->loop();
|
task->loop();
|
||||||
//Log.info("next");
|
//Log.notice("next");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainLoop::start()
|
MainLoop::start()
|
||||||
{
|
{
|
||||||
Log.info("*** Starting %d tasks...", scheduler.tasks.size());
|
Log.notice("*** Starting %d tasks...", scheduler.tasks.size());
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
for(auto task: scheduler) {
|
for(auto task: scheduler) {
|
||||||
|
Log.notice("** Starting %s", task->name);
|
||||||
task->start();
|
task->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +1,24 @@
|
|||||||
#include "./Renderer.h"
|
#include "./Renderer.h"
|
||||||
#include "./Display.h"
|
#include "./Display.h"
|
||||||
|
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
Renderer::loop()
|
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.info("Rendering %s", figment->name);
|
//Log.notice("Rendering %s", figment->name);
|
||||||
figment->render(dpy);
|
figment->render(dpy);
|
||||||
//Log.info("next");
|
//Log.notice("next");
|
||||||
} else {
|
} else {
|
||||||
//Log.info("Not rendering %s", figment->name);
|
//Log.notice("Not rendering %s", figment->name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
NSFastLED::FastLED.show();
|
FastLED.show();
|
||||||
|
FastLED.countFPS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -24,5 +27,5 @@ Renderer::onStart()
|
|||||||
for(Display* dpy : m_displays) {
|
for(Display* dpy : m_displays) {
|
||||||
dpy->clear();
|
dpy->clear();
|
||||||
}
|
}
|
||||||
NSFastLED::FastLED.show();
|
FastLED.show();
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
#include "./Surface.h"
|
#include "./Surface.h"
|
||||||
#include "./Display.h"
|
#include "./Display.h"
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end)
|
Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end)
|
||||||
: start(dpy->coordinateMapping()->virtualToPhysicalCoords(start)),
|
: start(dpy->coordinateMapping()->virtualToPhysicalCoords(start)),
|
||||||
@ -9,28 +10,30 @@ Surface::Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoo
|
|||||||
}
|
}
|
||||||
|
|
||||||
Surface&
|
Surface&
|
||||||
Surface::operator=(const NSFastLED::CRGB& color)
|
Surface::operator=(const CRGB& color)
|
||||||
{
|
{
|
||||||
paintWith([&](NSFastLED::CRGB& pixel) {
|
paintWith([&](CRGB& pixel) {
|
||||||
pixel = color;
|
pixel = color;
|
||||||
});
|
});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface&
|
Surface&
|
||||||
Surface::operator+=(const NSFastLED::CRGB& color)
|
Surface::operator+=(const CRGB& color)
|
||||||
{
|
{
|
||||||
paintWith([&](NSFastLED::CRGB& pixel) {
|
paintWith([&](CRGB& pixel) {
|
||||||
pixel += color;
|
pixel += color;
|
||||||
});
|
});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Surface::paintWith(std::function<void(NSFastLED::CRGB&)> func)
|
Surface::paintWith(std::function<void(CRGB&)> func)
|
||||||
{
|
{
|
||||||
for(uint8_t x = start.x; x <= end.x; x++) {
|
//Log.verbose("Painting startx=%d endx=%d starty=%d endy=%d", start.x, end.x, start.y, end.y);
|
||||||
for(uint8_t y = start.y; y <= end.y; y++) {
|
for(auto x = start.x; x <= end.x; x++) {
|
||||||
|
for(auto y = start.y; y <= end.y; y++) {
|
||||||
|
//Log.verbose("x=%d y=%d", x, y);
|
||||||
func(m_display->pixelAt(PhysicalCoordinates{x, y}));
|
func(m_display->pixelAt(PhysicalCoordinates{x, y}));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
#include "FastLED/FastLED.h"
|
#include <FastLED.h>
|
||||||
#include "./Geometry.h"
|
#include "./Geometry.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
|
|
||||||
@ -7,10 +8,10 @@ class Surface {
|
|||||||
public:
|
public:
|
||||||
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end);
|
Surface(Display* dpy, const VirtualCoordinates& start, const VirtualCoordinates& end);
|
||||||
|
|
||||||
Surface& operator=(const NSFastLED::CRGB& color);
|
Surface& operator=(const CRGB& color);
|
||||||
Surface& operator+=(const NSFastLED::CRGB& color);
|
Surface& operator+=(const CRGB& color);
|
||||||
|
|
||||||
void paintWith(std::function<void(NSFastLED::CRGB&)> func);
|
void paintWith(std::function<void(CRGB&)> func);
|
||||||
|
|
||||||
const PhysicalCoordinates start;
|
const PhysicalCoordinates start;
|
||||||
const PhysicalCoordinates end;
|
const PhysicalCoordinates end;
|
46
lib/README
Normal file
46
lib/README
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
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
|
33
out.log
Normal file
33
out.log
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
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.
|
24
platformio.ini
Normal file
24
platformio.ini
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:featheresp32]
|
||||||
|
platform = espressif32
|
||||||
|
board = featheresp32
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
fastled/FastLED@^3.4.0
|
||||||
|
thijse/ArduinoLog@^1.0.3
|
||||||
|
knolleary/PubSubClient@^2.8.0
|
||||||
|
bblanchon/ArduinoJson@^6.17.3
|
||||||
|
ricaun/ArduinoUniqueID@^1.1.0
|
||||||
|
sstaub/NTP@^1.4.0
|
||||||
|
src_filter = "+<*> -<.git/> -<.svn/> -<platform/> +<platform/arduino/>"
|
||||||
|
; upload_protocol = espota
|
||||||
|
; upload_port = 10.0.0.171
|
42
src/BootOptions.cpp
Normal file
42
src/BootOptions.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "BootOptions.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
LEDStatus serialStatus = LEDStatus(RGB_COLOR_ORANGE, LED_PATTERN_FADE, LED_SPEED_FAST, LED_PRIORITY_BACKGROUND);
|
||||||
|
LEDStatus configStatus = LEDStatus(RGB_COLOR_YELLOW, LED_PATTERN_FADE, LED_SPEED_NORMAL, LED_PRIORITY_IMPORTANT);
|
||||||
|
retained bool LAST_BOOT_WAS_FLASH;
|
||||||
|
retained bool LAST_BOOT_WAS_SERIAL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
BootOptions::initPins()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
pinMode(2, INPUT_PULLDOWN);
|
||||||
|
pinMode(3, INPUT_PULLDOWN);
|
||||||
|
pinMode(4, INPUT_PULLDOWN);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
BootOptions::BootOptions()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
isSetup = digitalRead(2) == HIGH;
|
||||||
|
isSerial = digitalRead(3) == HIGH || LAST_BOOT_WAS_SERIAL;
|
||||||
|
isFlash = digitalRead(4) == HIGH;
|
||||||
|
|
||||||
|
LAST_BOOT_WAS_FLASH = isFlash;
|
||||||
|
LAST_BOOT_WAS_SERIAL |= isSerial;
|
||||||
|
lastBootWasFlash = LAST_BOOT_WAS_FLASH;
|
||||||
|
|
||||||
|
configStatus.setActive(isSetup);
|
||||||
|
serialStatus.setActive(isSerial);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BootOptions::waitForRelease()
|
||||||
|
{
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
while(digitalRead(2) == HIGH || digitalRead(3) == HIGH) {};
|
||||||
|
#endif
|
||||||
|
}
|
14
src/BootOptions.h
Normal file
14
src/BootOptions.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct BootOptions {
|
||||||
|
static void initPins();
|
||||||
|
|
||||||
|
BootOptions();
|
||||||
|
|
||||||
|
void waitForRelease();
|
||||||
|
|
||||||
|
bool isSetup = false;
|
||||||
|
bool isSerial = false;
|
||||||
|
bool isFlash = false;
|
||||||
|
bool lastBootWasFlash = false;
|
||||||
|
};
|
109
src/Config.cpp
Normal file
109
src/Config.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "./Config.h"
|
||||||
|
#include "./Static.h"
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
|
constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
||||||
|
|
||||||
|
HardwareConfig
|
||||||
|
HardwareConfig::load() {
|
||||||
|
HardwareConfig ret;
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
EEPROM.get(0, ret);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HardwareConfig::save() {
|
||||||
|
HardwareConfig dataCopy{*this};
|
||||||
|
dataCopy.checksum = getCRC();
|
||||||
|
#ifdef PLATFORM_PHOTON
|
||||||
|
EEPROM.put(0, dataCopy);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearCoordinateMapping
|
||||||
|
HardwareConfig::toCoordMap() const
|
||||||
|
{
|
||||||
|
auto pixelCount = min(HardwareConfig::MAX_LED_NUM, std::max((uint16_t)1, data.pixelCount));
|
||||||
|
auto startPixel = min(pixelCount, std::max((uint16_t)1, data.startPixel));
|
||||||
|
return LinearCoordinateMapping{pixelCount, startPixel};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
HardwareConfig::isValid() const
|
||||||
|
{
|
||||||
|
return version == 2 && checksum == getCRC() && data.pixelCount <= MAX_LED_NUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
HardwareConfig::getCRC() const
|
||||||
|
{
|
||||||
|
const unsigned char* message = reinterpret_cast<const unsigned char*>(&data);
|
||||||
|
constexpr uint8_t length = sizeof(data);
|
||||||
|
unsigned char i, j, crc = 0;
|
||||||
|
for(i = 0; i < length; i++) {
|
||||||
|
crc ^= message[i];
|
||||||
|
for(j = 0; j < 8; j++) {
|
||||||
|
if (crc & 1) {
|
||||||
|
crc ^= CRC7_POLY;
|
||||||
|
}
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigService::onStart()
|
||||||
|
{
|
||||||
|
Log.notice("Starting configuration service...");
|
||||||
|
m_config = HardwareConfig::load();
|
||||||
|
if (m_config.isValid()) {
|
||||||
|
Log.notice("Configuration found!");
|
||||||
|
} else {
|
||||||
|
Log.notice("No configuration found. Writing defaults...");
|
||||||
|
m_config = HardwareConfig{};
|
||||||
|
m_config.save();
|
||||||
|
}
|
||||||
|
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("Loading task states...");
|
||||||
|
for(int i = 0; i < 32; i++) {
|
||||||
|
auto svc = m_config.data.serviceStates[i];
|
||||||
|
if (strlen(svc.name) > 0) {
|
||||||
|
Log.notice("* %s: %s", svc.name, svc.isDisabled? "DISABLED" : "ENABLED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigService::loop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigService::handleEvent(const InputEvent &evt)
|
||||||
|
{
|
||||||
|
switch(evt.intent) {
|
||||||
|
case InputEvent::SetDisplayLength:
|
||||||
|
//Log.info("Updating pixel count from %d to %d", m_coordMap.pixelCount, evt.asInt());
|
||||||
|
m_config.data.pixelCount = evt.asInt();
|
||||||
|
m_coordMap = m_config.toCoordMap();
|
||||||
|
//Log.info("Count is now %d", m_coordMap.pixelCount);
|
||||||
|
break;
|
||||||
|
case InputEvent::SetDisplayOffset:
|
||||||
|
//Log.info("Updating pixel offset from %d to %d", m_coordMap.startPixel, evt.asInt());
|
||||||
|
m_config.data.startPixel = evt.asInt();
|
||||||
|
m_coordMap = m_config.toCoordMap();
|
||||||
|
//Log.info("Offset is now %d", m_coordMap.startPixel);
|
||||||
|
break;
|
||||||
|
case InputEvent::SaveConfigurationRequest:
|
||||||
|
//Log.info("Saving configuration");
|
||||||
|
m_config.save();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(ConfigService);
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Particle.h"
|
#include <Figments.h>
|
||||||
#include "./Figments/Figments.h"
|
|
||||||
|
//#define PLATFORM_PHOTON
|
||||||
|
#define PLATFORM_ARDUINO
|
||||||
|
|
||||||
struct HardwareConfig {
|
struct HardwareConfig {
|
||||||
uint8_t version = 2;
|
uint8_t version = 2;
|
||||||
@ -27,7 +29,7 @@ struct HardwareConfig {
|
|||||||
LinearCoordinateMapping toCoordMap() const;
|
LinearCoordinateMapping toCoordMap() const;
|
||||||
|
|
||||||
static constexpr uint16_t MAX_LED_NUM = 255;
|
static constexpr uint16_t MAX_LED_NUM = 255;
|
||||||
static constexpr bool HAS_MPU_6050 = true;
|
static constexpr bool HAS_MPU_6050 = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t getCRC() const;
|
uint8_t getCRC() const;
|
||||||
@ -46,17 +48,6 @@ struct ConfigService: public Task {
|
|||||||
const LinearCoordinateMapping* coordMap() const { return &m_coordMap; }
|
const LinearCoordinateMapping* coordMap() const { return &m_coordMap; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onConnected();
|
|
||||||
void publishConfig() const;
|
|
||||||
int photonSave(String command);
|
|
||||||
int setPixelCount(String command);
|
|
||||||
int setStartPixel(String command);
|
|
||||||
|
|
||||||
AnimatedNumber m_pixelCount;
|
|
||||||
AnimatedNumber m_startPixel;
|
|
||||||
int m_pixelCountInt;
|
|
||||||
int m_startPixelInt;
|
|
||||||
|
|
||||||
HardwareConfig m_config;
|
HardwareConfig m_config;
|
||||||
LinearCoordinateMapping m_coordMap;
|
LinearCoordinateMapping m_coordMap;
|
||||||
};
|
};
|
@ -1,5 +1,5 @@
|
|||||||
#include "Sequencer.h"
|
#include "Sequencer.h"
|
||||||
#include "Figments/MainLoop.h"
|
#include <MainLoop.h>
|
||||||
|
|
||||||
Sequencer::Sequencer(std::vector<Sequencer::Scene> &&scenes) :
|
Sequencer::Sequencer(std::vector<Sequencer::Scene> &&scenes) :
|
||||||
Task("SceneSequencer"),
|
Task("SceneSequencer"),
|
||||||
@ -26,8 +26,9 @@ void
|
|||||||
Sequencer::handleEvent(const InputEvent& evt)
|
Sequencer::handleEvent(const InputEvent& evt)
|
||||||
{
|
{
|
||||||
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.info("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);
|
||||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +54,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);
|
||||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Figments/Figment.h"
|
#include <Figment.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Sequencer: public Task {
|
class Sequencer: public Task {
|
49
src/WiFiTask.cpp
Normal file
49
src/WiFiTask.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include <Input.h>
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "Static.h"
|
||||||
|
#include "WiFiTask.h"
|
||||||
|
|
||||||
|
WiFiTask::WiFiTask() : InputSource("WiFi") {}
|
||||||
|
|
||||||
|
void
|
||||||
|
WiFiTask::onStart()
|
||||||
|
{
|
||||||
|
Log.notice("Starting wifi...");
|
||||||
|
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");
|
||||||
|
while(WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.print('.');
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputEvent
|
||||||
|
WiFiTask::read()
|
||||||
|
{
|
||||||
|
uint8_t curStatus = WiFi.status();
|
||||||
|
if (m_lastStatus != curStatus) {
|
||||||
|
m_lastStatus = curStatus;
|
||||||
|
Log.verbose("WiFi Status: %d", curStatus);
|
||||||
|
if (curStatus == WL_CONNECTED) {
|
||||||
|
Log.notice("Connected!");
|
||||||
|
return InputEvent{InputEvent::NetworkStatus, true};
|
||||||
|
} else if (curStatus == WL_CONNECTION_LOST || curStatus == WL_DISCONNECTED) {
|
||||||
|
Log.notice("Lost wifi connection!");
|
||||||
|
return InputEvent{InputEvent::NetworkStatus, false};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InputEvent{};
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC_ALLOC(WiFiTask);
|
12
src/WiFiTask.h
Normal file
12
src/WiFiTask.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <Input.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "Static.h"
|
||||||
|
|
||||||
|
class WiFiTask : public InputSource {
|
||||||
|
public:
|
||||||
|
WiFiTask();
|
||||||
|
void onStart() override;
|
||||||
|
InputEvent read() override;
|
||||||
|
private:
|
||||||
|
uint8_t m_lastStatus = WL_IDLE_STATUS;
|
||||||
|
};
|
@ -2,8 +2,6 @@
|
|||||||
#include "../sprites/Chime.h"
|
#include "../sprites/Chime.h"
|
||||||
#include "../sprites/Blob.h"
|
#include "../sprites/Blob.h"
|
||||||
|
|
||||||
using namespace NSFastLED;
|
|
||||||
|
|
||||||
#define CHIME_LENGTH 23
|
#define CHIME_LENGTH 23
|
||||||
#define CHIME_COUNT 4
|
#define CHIME_COUNT 4
|
||||||
#define BLOB_COUNT 10
|
#define BLOB_COUNT 10
|
||||||
@ -63,9 +61,9 @@ public:
|
|||||||
m_chimes.render(dpy);
|
m_chimes.render(dpy);
|
||||||
m_blobs.render(dpy);
|
m_blobs.render(dpy);
|
||||||
Surface fullSurface(dpy, {0, 0}, {255, 0});
|
Surface fullSurface(dpy, {0, 0}, {255, 0});
|
||||||
NSFastLED::CRGB scaledColor = NSFastLED::CRGB(m_flashColor).nscale8_video(max(10, NSFastLED::ease8InOutCubic(m_flashBrightness)));
|
CRGB scaledColor = CRGB(m_flashColor).nscale8_video(std::max((uint8_t)10, ease8InOutCubic(m_flashBrightness)));
|
||||||
fullSurface.paintWith([&](NSFastLED::CRGB& pixel) {
|
fullSurface.paintWith([&](CRGB& pixel) {
|
||||||
pixel = NSFastLED::blend(scaledColor, pixel, 200);
|
pixel = blend(scaledColor, pixel, 200);
|
||||||
//pixel = scaledColor;
|
//pixel = scaledColor;
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
#include "../Figments/Figments.h"
|
#include <Figments.h>
|
||||||
|
#include <ArduinoLog.h>
|
||||||
|
|
||||||
using namespace NSFastLED;
|
|
||||||
class DrainAnimation: public Figment {
|
class DrainAnimation: public Figment {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -62,5 +62,3 @@ public:
|
|||||||
uint16_t m_pos;
|
uint16_t m_pos;
|
||||||
uint16_t m_burst;
|
uint16_t m_burst;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../Figments/Figments.h"
|
#include <Figments.h>
|
||||||
#include "../sprites/Blob.h"
|
#include "../sprites/Blob.h"
|
||||||
|
|
||||||
using NSFastLED::CHSV;
|
|
||||||
|
|
||||||
class Flashlight: public Figment {
|
class Flashlight: public Figment {
|
||||||
public:
|
public:
|
||||||
Flashlight(Task::State initialState) : Figment("Flashlight", initialState) {
|
Flashlight(Task::State initialState) : Figment("Flashlight", initialState) {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user