Compare commits
No commits in common. "wip/congress" and "master" have entirely different histories.
wip/congre
...
master
@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"rotation": 2,
|
||||
"rotation": 3,
|
||||
"strides": [
|
||||
{"x": 0, "y": 0, "pixels": 17},
|
||||
{"x": 0, "y": 1, "pixels": 17},
|
||||
|
@ -2,6 +2,7 @@
|
||||
"version": 1,
|
||||
"tasks": [
|
||||
"Renderer",
|
||||
"U8Display",
|
||||
"WiFi",
|
||||
"MQTT",
|
||||
"ArduinoOTA",
|
||||
@ -9,7 +10,7 @@
|
||||
"Serial"
|
||||
],
|
||||
"scenes": {
|
||||
"Idle": ["Solid", "IdleColors", "CircadianRhythm"],
|
||||
"Idle": ["Solid", "MPU5060", "IdleColors", "CircadianRhythm"],
|
||||
"Flashlight": ["Flashlight"]
|
||||
},
|
||||
"surfaceMap": "default",
|
||||
|
@ -5,20 +5,20 @@
|
||||
"Renderer",
|
||||
"Serial"
|
||||
],
|
||||
"surfaceMap": "djstrip",
|
||||
"defaults": {
|
||||
"mqtt.ip": "10.0.0.2",
|
||||
"power.volts": 0,
|
||||
"power.milliamps": 0,
|
||||
"power.useBPM": false,
|
||||
"bpm.idle": 45,
|
||||
"scenes": {
|
||||
"Rain": ["Rain", "Rainbow"],
|
||||
"Test": ["Test"],
|
||||
"Idle": ["Solid", "Pulse", "Rainbow", "CircadianRhythm"],
|
||||
"Acid": ["Chimes", "Pulse", "IdleColors", "Rainbow"],
|
||||
"Flashlight": ["Flashlight"]
|
||||
}
|
||||
},
|
||||
"surfaceMap": "djstrip",
|
||||
"defaults": {
|
||||
"mqtt.ip": "10.0.0.2",
|
||||
"power.volts": 0,
|
||||
"power.milliamps": 0,
|
||||
"power.useBPM": false,
|
||||
"bpm.idle": 45
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,11 @@
|
||||
"MQTT",
|
||||
"ArduinoOTA",
|
||||
"UpdateStatusAnimation",
|
||||
"Serial",
|
||||
"U8Display"
|
||||
"Serial"
|
||||
],
|
||||
"scenes": {
|
||||
"Idle": ["Solid", "Rainbow", "CircadianRhythm"],
|
||||
"Acid": ["Chimes", "Rainbow"],
|
||||
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],
|
||||
"Acid": ["Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"],
|
||||
"Flashlight": ["Flashlight"]
|
||||
},
|
||||
"surfaceMap": "default",
|
||||
|
@ -8,40 +8,24 @@
|
||||
"UpdateStatusAnimation",
|
||||
"Serial",
|
||||
"Weather",
|
||||
"Test",
|
||||
"CircadianRhythm",
|
||||
"IdleTimer"
|
||||
"CircadianRhythm"
|
||||
],
|
||||
"scenes": {
|
||||
"Clear": ["Solid", "Rainbow"],
|
||||
"Rain": ["Rain"],
|
||||
"Drizzle": ["Rain"],
|
||||
"Mist": ["Rain"],
|
||||
"Snow": ["Solid"],
|
||||
"UnknownWeather": ["Solid", "IdleColors"],
|
||||
"Test": ["Test"],
|
||||
"Idle": ["Solid", "Rainbow"],
|
||||
"Acid": ["Chimes", "IdleColors", "Rainbow"],
|
||||
"Flashlight": ["Flashlight"]
|
||||
},
|
||||
"surfaceMap": "ponder",
|
||||
"defaults": {
|
||||
"mqtt.ip": "10.0.0.2",
|
||||
"colors.sequences": {
|
||||
"Rainbow": [[255, 0, 0], [255, 127, 0], [0, 255, 0], [0, 0, 255], [128, 0, 128]],
|
||||
"Idle": [[0, 123, 167], [80, 200, 120], [207, 113, 175], [128, 0, 128], [255, 255, 255], [0, 255, 255]],
|
||||
"Wet": [[3, 177, 252], [3, 78, 252], [126, 141, 242]],
|
||||
"Snowy": [[215, 219, 245], [255, 255, 255], [192, 250, 244]],
|
||||
"Sunny": [[213, 237, 95], [237, 180, 95], [245, 178, 10]]
|
||||
},
|
||||
"colors.scenes": {
|
||||
"Clear": "Sunny",
|
||||
"Idle": "Rainbow",
|
||||
"Rain": "Wet",
|
||||
"Drizzle": "Wet",
|
||||
"Mist": "Wet",
|
||||
"Snow": "Snowy"
|
||||
},
|
||||
"colors.secondsPerColor": 9,
|
||||
"scenes": {
|
||||
"Clear": ["Solid"],
|
||||
"Cloudy": ["Cloudy", "Solid"],
|
||||
"Rain": ["Rain", "Solid"],
|
||||
"Drizzle": ["Rain", "Cloudy", "Solid"],
|
||||
"Mist": ["Cloudy", "Solid"],
|
||||
"Snow": ["Solid"],
|
||||
"UnknownWeather": ["Thinking"],
|
||||
"Start": ["Thinking"],
|
||||
"Idle": ["Solid"],
|
||||
"Test": ["Test"]
|
||||
}
|
||||
"power.volts": 0,
|
||||
"power.milliamps": 0
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"tasks": [
|
||||
"Renderer",
|
||||
"Serial"
|
||||
]
|
||||
}
|
@ -6,9 +6,6 @@ class Args {
|
||||
String *str;
|
||||
public:
|
||||
Args(String *str) : str(str) {}
|
||||
operator const char*() const {
|
||||
return str->c_str();
|
||||
}
|
||||
String operator[](int pos) {
|
||||
char buf[64];
|
||||
strncpy(buf, str->c_str(), sizeof(buf));
|
||||
|
@ -108,7 +108,7 @@ struct InputEvent: public Variant {
|
||||
InputEvent()
|
||||
: Variant(), intent(None) {}
|
||||
|
||||
bool operator!=(const InputEvent::Intent& otherIntent) const {
|
||||
bool operator!=(const InputEvent::Intent& otherIntent) {
|
||||
return intent != otherIntent;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ Renderer::loop()
|
||||
{
|
||||
uint16_t totalPower = 0;
|
||||
for(Display* dpy : m_displays) {
|
||||
dpy->clear();
|
||||
totalPower += calculate_unscaled_power_mW(dpy->pixelBacking(), dpy->pixelCount());
|
||||
for(Figment* figment : m_figments) {
|
||||
if (figment->state == Task::Running) {
|
||||
|
@ -43,14 +43,6 @@ Surface::operator+=(const CRGB& color)
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
Surface::blend(const CRGB& color, uint8_t pct)
|
||||
{
|
||||
paintWith([&](CRGB& pixel) {
|
||||
nblend(pixel, color, pct);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Surface::paintWith(std::function<void(CRGB&)> func)
|
||||
{
|
||||
|
@ -31,8 +31,6 @@ public:
|
||||
*/
|
||||
Surface& operator+=(const CRGB& color);
|
||||
|
||||
void blend(const CRGB& color, uint8_t pct);
|
||||
|
||||
/**
|
||||
* OR operation that applies the given color to every pixel on the surface
|
||||
*/
|
||||
|
@ -11,18 +11,21 @@
|
||||
[env]
|
||||
src_filter = +<*>, -<.git/>, -<.svn/>, -<platform/>
|
||||
lib_ldf_mode = chain+
|
||||
extra_scripts = post:verify-configs.py, pre:build-hal.py
|
||||
extra_scripts = pre:verify-configs.py, pre:build-hal.py
|
||||
check_flags =
|
||||
cppcheck: --inline-suppr --suppress=*:*/.pio/*
|
||||
board_build.filesystem = littlefs
|
||||
upload_speed = 115200
|
||||
monitor_speed = 115200
|
||||
build_type = debug
|
||||
build_flags =
|
||||
-DFASTLED_ALL_PINS_HARDWARE_SPI
|
||||
src_build_flags =
|
||||
-DFASTLED_ALL_PINS_HARDWARE_SPI
|
||||
-DRENDERBUG_VERSION=3
|
||||
-DRENDERBUG_LED_PIN=14
|
||||
-DRENDERBUG_LED_PACKING=RGB
|
||||
-DDEFAULT_PATTERN_INDEX=0
|
||||
-fstack-protector
|
||||
-Wall
|
||||
lib_deps =
|
||||
|
153
src/Colors.cpp
153
src/Colors.cpp
@ -1,153 +0,0 @@
|
||||
#include "Colors.h"
|
||||
#include "Static.h"
|
||||
|
||||
Colors::Colors()
|
||||
: BufferedInputSource("Colors"),
|
||||
m_colorTimer(0),
|
||||
m_secondsPerColor(9)
|
||||
{
|
||||
state = Task::Running;
|
||||
}
|
||||
|
||||
void
|
||||
Colors::loop()
|
||||
{
|
||||
EVERY_N_SECONDS(1) {
|
||||
if (m_colorTimer > 0) {
|
||||
Log.verbose("colors: tick");
|
||||
m_colorTimer -= 1;
|
||||
|
||||
if (m_colorTimer == 0) {
|
||||
rotate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigTaskMixin::loop();
|
||||
}
|
||||
|
||||
void
|
||||
Colors::handleEvent(const InputEvent& evt)
|
||||
{
|
||||
if (evt.intent == InputEvent::SetScene) {
|
||||
setScene(evt.asString());
|
||||
}
|
||||
ConfigTaskMixin::handleEvent(evt);
|
||||
}
|
||||
|
||||
void
|
||||
Colors::setSequence(const char* seqName)
|
||||
{
|
||||
bool found = false;
|
||||
int idx = 0;
|
||||
for(Sequence seq : m_sequences) {
|
||||
if (strcmp(seq.name.c_str(), seqName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (found) {
|
||||
Log.info("colors: Setting sequence to %s", seqName);
|
||||
m_idx = idx;
|
||||
rotate();
|
||||
} else {
|
||||
Log.warning("colors: Unknown sequence %s", seqName);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Colors::rotate()
|
||||
{
|
||||
if (!m_sequences.empty()) {
|
||||
Log.info("colors: rotate!") ;
|
||||
m_colorIdx += 1;
|
||||
m_colorIdx %= m_sequences[m_idx].colors.size();
|
||||
m_colorTimer = m_secondsPerColor;
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetColor, m_sequences[m_idx].colors[m_colorIdx]});
|
||||
//setEvent(InputEvent{InputEvent::SetColor, m_sequences[m_idx].colors[m_colorIdx]});
|
||||
} else {
|
||||
Log.info("colors: No colors to rotate");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Colors::setScene(const char* sceneName)
|
||||
{
|
||||
auto found = m_sceneMap.find(sceneName);
|
||||
if (found != m_sceneMap.end()) {
|
||||
Log.info("colors: Running scene %s", sceneName);
|
||||
setSequence(found->second);
|
||||
} else {
|
||||
Log.warning("colors: Unknown scene %s", sceneName);
|
||||
m_colorTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Colors::handleConfigChange(const Configuration& cfg)
|
||||
{
|
||||
Log.info("colors: reloading config");
|
||||
m_sequences.clear();
|
||||
m_colorIdx = 0;
|
||||
m_idx = 0;
|
||||
m_secondsPerColor = cfg.get("colors.secondsPerColor", 9);
|
||||
m_colorTimer = 0;
|
||||
JsonObject seqJson = cfg.get("colors.sequences");
|
||||
for(const auto seq : seqJson) {
|
||||
Log.info("colors: Found color sequence %s", seq.key().c_str());
|
||||
std::vector<CRGB> colors;
|
||||
for(JsonArray rgb : seq.value().as<JsonArray>()) {
|
||||
colors.emplace_back(CRGB(rgb[0], rgb[1], rgb[2]));
|
||||
}
|
||||
m_sequences.emplace_back(Sequence{
|
||||
seq.key().c_str(),
|
||||
std::move(colors)
|
||||
});
|
||||
}
|
||||
|
||||
m_sceneMap.clear();
|
||||
JsonObject sceneJson = cfg.get("colors.scenes");
|
||||
for(const auto scene : sceneJson) {
|
||||
Log.info("colors: Found scene %s", scene.key().c_str());
|
||||
m_sceneMap[scene.key().c_str()] = scene.value().as<JsonString>().c_str();
|
||||
}
|
||||
|
||||
rotate();
|
||||
}
|
||||
|
||||
void
|
||||
Colors::doSequences(Args& args, Print& out)
|
||||
{
|
||||
out.println("Color sequences:");
|
||||
for(const Sequence& seq : m_sequences) {
|
||||
out.print("\t");
|
||||
out.println(seq.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Colors::doSequence(Args& args, Print& out)
|
||||
{
|
||||
setSequence(args[1].c_str());
|
||||
}
|
||||
|
||||
void
|
||||
Colors::doRotate(Args& args, Print& out)
|
||||
{
|
||||
rotate();
|
||||
}
|
||||
|
||||
const std::vector<Command>&
|
||||
Colors::commands() const
|
||||
{
|
||||
static const std::vector<Command> _commands = {
|
||||
{"sequences", &Colors::doSequences},
|
||||
{"sequence", &Colors::doSequence},
|
||||
{"rotate", &Colors::doRotate}
|
||||
};
|
||||
return _commands;
|
||||
}
|
||||
|
||||
STATIC_TASK(Colors);
|
||||
STATIC_ALLOC(Colors);
|
36
src/Colors.h
36
src/Colors.h
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Figments.h>
|
||||
#include "Config.h"
|
||||
#include <map>
|
||||
|
||||
class Colors: public BufferedInputSource, ConfigTaskMixin {
|
||||
public:
|
||||
struct Sequence {
|
||||
String name;
|
||||
const std::vector<CRGB> colors;
|
||||
};
|
||||
|
||||
Colors();
|
||||
void loop() override;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
void handleConfigChange(const Configuration& cfg) override;
|
||||
const std::vector<Command>& commands() const override;
|
||||
|
||||
private:
|
||||
uint16_t m_idx;
|
||||
uint16_t m_colorIdx;
|
||||
uint16_t m_colorTimer;
|
||||
uint16_t m_secondsPerColor;
|
||||
std::vector<Sequence> m_sequences;
|
||||
std::map<const char*, const char*> m_sceneMap;
|
||||
|
||||
void setScene(const char* sceneName);
|
||||
void setSequence(const char* seqName);
|
||||
|
||||
void doSequences(Args& args, Print& out);
|
||||
void doSequence(Args& args, Print& out);
|
||||
void doRotate(Args& args, Print& out);
|
||||
|
||||
void rotate();
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
#include "./Config.h"
|
||||
#include "./Static.h"
|
||||
#include "./Sequencer.h"
|
||||
|
||||
#include <ArduinoLog.h>
|
||||
#include <ArduinoJson.h>
|
||||
@ -22,12 +23,6 @@ Configuration::Configuration(const JsonObject& data)
|
||||
{
|
||||
}
|
||||
|
||||
JsonVariant
|
||||
Configuration::get(const char* key) const
|
||||
{
|
||||
return m_json[key];
|
||||
}
|
||||
|
||||
const char*
|
||||
Configuration::get(const char* key, const char* defaultVal) const
|
||||
{
|
||||
@ -58,6 +53,8 @@ Configuration::get(const char* key, bool defaultVal) const
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<2048> jsonConfig;
|
||||
|
||||
constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
||||
|
||||
HardwareConfig
|
||||
@ -152,15 +149,12 @@ ConfigService::loadMap(const String& mapName)
|
||||
{
|
||||
String fname = String("/maps/") + mapName + ".json";
|
||||
if (LittleFS.exists(fname)) {
|
||||
DynamicJsonDocument jsonConfig(2048);
|
||||
File configFile = LittleFS.open(fname, "r");
|
||||
Log.notice("config: Loading coordinate map from %s", fname.c_str());
|
||||
deserializeJson(jsonConfig, configFile);
|
||||
configFile.close();
|
||||
JsonArray strideList = jsonConfig["strides"];
|
||||
uint8_t rotation = jsonConfig["rotation"];
|
||||
m_jsonMap.load(strideList);
|
||||
m_jsonMap.rotation = rotation;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -177,13 +171,25 @@ ConfigService::loadProfile(const char* profileName)
|
||||
if (LittleFS.exists(fname)) {
|
||||
strncpy(m_config.data.loadedProfile, fname.c_str(), sizeof(m_config.data.loadedProfile));
|
||||
File configFile = LittleFS.open(fname, "r");
|
||||
DynamicJsonDocument jsonConfig(2048*2);
|
||||
auto err = deserializeJson(jsonConfig, configFile);
|
||||
jsonConfig.clear();
|
||||
deserializeJson(jsonConfig, configFile);
|
||||
configFile.close();
|
||||
|
||||
if (err) {
|
||||
Log.error("JSON failure: %s", err.c_str());
|
||||
//int profileLogLevel = max(0, min(6, jsonConfig["logLevel"]));
|
||||
//Log.setLevel(profileLogLevel);
|
||||
//Log.trace("config: \t %d logging level");
|
||||
|
||||
JsonObject sceneList = jsonConfig["scenes"];
|
||||
std::vector<Sequencer::Scene> scenes;
|
||||
for(JsonPair pair : sceneList) {
|
||||
Log.notice("config: \tFound scene %s", pair.key().c_str());
|
||||
std::vector<const char*> patterns;
|
||||
for(const char* taskName : pair.value().as<JsonArray>()) {
|
||||
patterns.push_back(taskName);
|
||||
}
|
||||
scenes.push_back(Sequencer::Scene{pair.key().c_str(), patterns});
|
||||
}
|
||||
Static<Sequencer>::instance()->setScenes(std::move(scenes));
|
||||
|
||||
JsonArray taskList = jsonConfig["tasks"];
|
||||
Log.notice("config: Starting %d tasks", taskList.size());
|
||||
@ -191,8 +197,14 @@ ConfigService::loadProfile(const char* profileName)
|
||||
MainLoop::instance()->dispatchSync(InputEvent{InputEvent::StartThing, taskList[i].as<const char*>()});
|
||||
}
|
||||
|
||||
JsonObject defaults = jsonConfig["defaults"];
|
||||
Log.notice("config: Loading %d app configurations", defaults.size());
|
||||
MainLoop::instance()->dispatchSync(InputEvent{InputEvent::ConfigurationChanged, &defaults});
|
||||
|
||||
String mapName = jsonConfig["surfaceMap"];
|
||||
|
||||
jsonConfig.clear();
|
||||
|
||||
if (mapName.isEmpty()) {
|
||||
Log.warning("config: No coordinate map defined! Defaulting to linear mapping.", mapName.c_str());
|
||||
m_jsonMap.loadDefault();
|
||||
@ -200,11 +212,6 @@ ConfigService::loadProfile(const char* profileName)
|
||||
Log.warning("config: Couldn't load coordinate map %s!!! Defaulting to linear mapping.", mapName.c_str());
|
||||
m_jsonMap.loadDefault();
|
||||
}
|
||||
|
||||
JsonObject defaults = jsonConfig["defaults"];
|
||||
Log.notice("config: Loading %d app configurations", defaults.size());
|
||||
MainLoop::instance()->dispatchSync(InputEvent{InputEvent::ConfigurationChanged, &defaults});
|
||||
|
||||
Log.notice("config: Loaded!");
|
||||
strcpy(m_config.data.loadedProfile, profileName);
|
||||
} else {
|
||||
@ -216,8 +223,8 @@ ConfigService::loadProfile(const char* profileName)
|
||||
Log.notice("config: Configured to use %d pixels", m_jsonMap.physicalPixelCount());
|
||||
PhysicalCoordinates topLeft = m_jsonMap.virtualToPhysicalCoords({0, 0});
|
||||
PhysicalCoordinates bottomRight = m_jsonMap.virtualToPhysicalCoords({255, 255});
|
||||
Log.info(" (0,0) -> (%d, %d) -> %d", topLeft.x, topLeft.y, m_jsonMap.physicalCoordsToIndex(topLeft));
|
||||
Log.info(" (255,255) -> (%d, %d) -> %d", bottomRight.x, bottomRight.y, m_jsonMap.physicalCoordsToIndex(bottomRight));
|
||||
Log.verbose(" (0,0) -> (%d, %d) -> %d", topLeft.x, topLeft.y, m_jsonMap.physicalCoordsToIndex(topLeft));
|
||||
Log.verbose(" (255,255) -> (%d, %d) -> %d", bottomRight.x, bottomRight.y, m_jsonMap.physicalCoordsToIndex(bottomRight));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -286,16 +293,6 @@ ConfigService::doCoordMap(Args& args, Print& out)
|
||||
out.println(buf);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigService::doRotate(Args& args, Print& out)
|
||||
{
|
||||
out.print("Rotation: ");
|
||||
out.print(m_jsonMap.rotation);
|
||||
out.print(" ");
|
||||
m_jsonMap.rotation = atoi(args[1].c_str());
|
||||
out.println(m_jsonMap.rotation);
|
||||
}
|
||||
|
||||
const std::vector<Command>&
|
||||
ConfigService::commands() const
|
||||
{
|
||||
@ -303,8 +300,7 @@ ConfigService::commands() const
|
||||
{"save", &ConfigService::doSave},
|
||||
{"profile", &ConfigService::doSetProfile},
|
||||
{"maps", &ConfigService::doMapList},
|
||||
{"coordmap", &ConfigService::doCoordMap},
|
||||
{"rotate", &ConfigService::doRotate}
|
||||
{"coordmap", &ConfigService::doCoordMap}
|
||||
};
|
||||
return _commands;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ class Configuration {
|
||||
const char* get(const char* key, const char* defaultVal) const;
|
||||
int get(const char* key, int defaultVal) const;
|
||||
bool get(const char* key, bool defaultVal) const;
|
||||
JsonVariant get(const char* name) const;
|
||||
|
||||
private:
|
||||
const JsonObject& m_json;
|
||||
@ -80,5 +79,4 @@ private:
|
||||
void doSetProfile(Args& args, Print& print);
|
||||
void doCoordMap(Args& args, Print& print);
|
||||
void doMapList(Args& args, Print& print);
|
||||
void doRotate(Args& args, Print& print);
|
||||
};
|
||||
|
@ -11,33 +11,24 @@ JsonCoordinateMapping::physicalPixelCount() const {
|
||||
|
||||
int
|
||||
JsonCoordinateMapping::physicalCoordsToIndex(const PhysicalCoordinates localCoords) const {
|
||||
return displayMap[localCoords.x].physicalIdx + localCoords.y;
|
||||
uint8_t idx = 0;
|
||||
bool inverse = false;
|
||||
for(int i = 0; i < localCoords.x; i++) {
|
||||
idx += displayMap[i].length;
|
||||
inverse = !inverse;
|
||||
}
|
||||
|
||||
VirtualCoordinates
|
||||
JsonCoordinateMapping::rotate(const VirtualCoordinates coords, uint8_t rot) const
|
||||
{
|
||||
if (rot == 0) {
|
||||
return coords;
|
||||
} else if (rot == 1) {
|
||||
return VirtualCoordinates{coords.y, coords.x};
|
||||
} else if (rot == 2) {
|
||||
return VirtualCoordinates{255 - coords.y, 255 - coords.x};
|
||||
} else if (rot == 3) {
|
||||
return VirtualCoordinates{255 - coords.x, 255 - coords.y};
|
||||
if (inverse) {
|
||||
idx += std::max(0, displayMap[localCoords.x].length - 1 - std::max(0, (int)localCoords.y - displayMap[localCoords.x].y));
|
||||
} else {
|
||||
return coords;
|
||||
idx += std::min((int)displayMap[localCoords.x].length - 1, std::max(0, (int)localCoords.y - displayMap[localCoords.x].y));
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
PhysicalCoordinates
|
||||
JsonCoordinateMapping::virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const {
|
||||
// 0, 0 -> first pixel, first strand
|
||||
// 0, 255 -> first pixel, last strand
|
||||
// 255, 255 -> last pixel, last strand
|
||||
const VirtualCoordinates rotated = rotate(virtualCoords, rotation);
|
||||
const uint8_t spanIdx = scale8(strideCount-1, rotated.x);
|
||||
const uint8_t spanOffset = scale8(displayMap[spanIdx].length, rotated.y);
|
||||
const uint8_t spanIdx = scale8(strideCount-1, virtualCoords.x);
|
||||
const uint8_t spanOffset = scale8(maxStrideSize - 1, virtualCoords.y);
|
||||
return PhysicalCoordinates{spanIdx, spanOffset};
|
||||
}
|
||||
|
||||
@ -57,23 +48,23 @@ JsonCoordinateMapping::physicalToVirtualCoords(const PhysicalCoordinates localCo
|
||||
void
|
||||
JsonCoordinateMapping::load(const JsonArray& strideList)
|
||||
{
|
||||
maxStrideSize = 0;
|
||||
strideCount = strideList.size();
|
||||
uint16_t idx = 0;
|
||||
for(int i = 0; i < strideList.size();i++) {
|
||||
JsonObject strideObj = strideList[i];
|
||||
int length = strideObj["pixels"];
|
||||
int x = strideObj["x"];
|
||||
int y = strideObj["y"];
|
||||
bool reverse = strideObj["reverse"];
|
||||
displayMap[i] = Span{length, x, y, reverse, idx};
|
||||
idx += length;
|
||||
displayMap[i] = Span{length, x, y};
|
||||
Log.info("config: Found stride (%d, %d): %d", x, y, length);
|
||||
maxStrideSize = length > maxStrideSize ? length : maxStrideSize;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JsonCoordinateMapping::loadDefault()
|
||||
{
|
||||
displayMap[0] = Span{255, 0, 0, false, 0};
|
||||
displayMap[0] = Span{255, 0, 0};
|
||||
maxStrideSize = 255;
|
||||
strideCount = 1;
|
||||
}
|
||||
|
@ -7,18 +7,14 @@ struct JsonCoordinateMapping : CoordinateMapping {
|
||||
int length = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
bool reverse = false;
|
||||
|
||||
uint16_t physicalIdx = 0;
|
||||
|
||||
Span() {}
|
||||
Span(int length, int x, int y, bool rev, uint16_t idx) : length(length), x(x), y(y), reverse(rev), physicalIdx(idx) {}
|
||||
Span(int length, int x, int y) : length(length), x(x), y(y) {}
|
||||
};
|
||||
|
||||
uint8_t rotation = 0;
|
||||
|
||||
Span displayMap[32];
|
||||
int strideCount = 0;
|
||||
int maxStrideSize = 0;
|
||||
|
||||
void load(const JsonArray& strides);
|
||||
void loadDefault();
|
||||
@ -27,6 +23,4 @@ struct JsonCoordinateMapping : CoordinateMapping {
|
||||
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const override;
|
||||
int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const override;
|
||||
unsigned int physicalPixelCount() const override;
|
||||
|
||||
VirtualCoordinates rotate(const VirtualCoordinates coords, uint8_t rotation) const;
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ LogService::eventValue(const InputEvent& evt)
|
||||
void
|
||||
LogService::handleEvent(const InputEvent& evt) {
|
||||
if (evt.intent != InputEvent::None) {
|
||||
if (true || evt.intent != m_lastEvent.intent) {
|
||||
if (evt.intent != m_lastEvent.intent) {
|
||||
if (m_duplicateEvents > 0) {
|
||||
Log.trace("Suppressed reporting %d duplicate events.", m_duplicateEvents);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ void printNewline(Print* logOutput, int logLevel)
|
||||
}
|
||||
#endif
|
||||
|
||||
int Platform::s_timezone = 1;
|
||||
int Platform::s_timezone = 0;
|
||||
Platform::TaskRegistration* Platform::firstTask = NULL;
|
||||
Platform::TaskRegistration* Platform::lastTask = NULL;
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
#include "Scripts.h"
|
||||
#include "Static.h"
|
||||
|
||||
Scripts::Scripts()
|
||||
: Task("Scripts")
|
||||
{
|
||||
state = Running;
|
||||
}
|
||||
|
||||
void
|
||||
Scripts::loop()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Scripts::doEval(Args& args, Print& out)
|
||||
{
|
||||
String fullCmd((const char*)args);
|
||||
String fullScript(fullCmd.substring(fullCmd.indexOf(" ") + 1));
|
||||
}
|
||||
|
||||
const std::vector<Command>&
|
||||
Scripts::commands() const
|
||||
{
|
||||
static const std::vector<Command> _commands{
|
||||
{"eval", &Scripts::doEval}
|
||||
};
|
||||
|
||||
return _commands;
|
||||
}
|
||||
|
||||
STATIC_ALLOC(Scripts);
|
||||
STATIC_TASK(Scripts);
|
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
#include <Figments.h>
|
||||
|
||||
class Scripts : public Task {
|
||||
public:
|
||||
Scripts();
|
||||
void loop() override;
|
||||
const std::vector<Command>& commands() const;
|
||||
|
||||
private:
|
||||
void doEval(Args& args, Print& out);
|
||||
};
|
@ -3,9 +3,8 @@
|
||||
#include "Static.h"
|
||||
|
||||
Sequencer::Sequencer() :
|
||||
Task("Sequencer"),
|
||||
m_idx(0),
|
||||
m_sceneBeforeIdle(-1)
|
||||
Task("SceneSequencer"),
|
||||
m_idx(0)
|
||||
{
|
||||
state = Task::Running;
|
||||
}
|
||||
@ -13,32 +12,28 @@ Sequencer::Sequencer() :
|
||||
void
|
||||
Sequencer::Scene::start()
|
||||
{
|
||||
for(const auto& pattern : patterns) {
|
||||
//pattern.getBytes(namebuf, sizeof(namebuf));
|
||||
Log.info("sequencer: Starting pattern task %s", pattern.c_str());
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern.c_str()});
|
||||
for(const char* pattern : patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::Scene::stop()
|
||||
{
|
||||
for(const auto& pattern : patterns) {
|
||||
Log.info("sequencer: Stopping pattern task %s", pattern.c_str());
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern.c_str()});
|
||||
for(const char* pattern : patterns) {
|
||||
Log.verbose("Stopping pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::loop()
|
||||
{
|
||||
ConfigTaskMixin::loop();
|
||||
}
|
||||
Sequencer::loop() {}
|
||||
|
||||
const char*
|
||||
Sequencer::currentSceneName()
|
||||
{
|
||||
return m_scenes[m_idx].name.c_str();
|
||||
return m_scenes[m_idx].name;
|
||||
}
|
||||
|
||||
const std::vector<Sequencer::Scene>
|
||||
@ -53,64 +48,30 @@ Sequencer::onStart()
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::handleEvent(const InputEvent& evt)
|
||||
Sequencer::setScenes(std::vector<Scene> &&scenes)
|
||||
{
|
||||
if (!m_scenes.empty()) {
|
||||
if (evt.intent == InputEvent::ReadyToRoll) {
|
||||
Log.info("sequencer: Activating startup scene");
|
||||
if (!changeToScene("Start")) {
|
||||
Log.info("sequencer: Start scene not found, falling back to first scene");
|
||||
changeToScene(m_scenes[0].name.c_str());
|
||||
}
|
||||
} else if (evt.intent == InputEvent::SetScene && m_scenes[m_idx].name == evt.asString()) {
|
||||
return;
|
||||
} else if (evt.intent == InputEvent::SetScene) {
|
||||
changeToScene(evt.asString());
|
||||
} else if (evt.intent == InputEvent::IdleStart) {
|
||||
Log.info("sequencer: Activating idle scene");
|
||||
m_sceneBeforeIdle = m_idx;
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetScene, "Idle"});
|
||||
} else if (evt.intent == InputEvent::IdleStop && m_sceneBeforeIdle >= 0) {
|
||||
Log.info("sequencer: Resuming pre-idle scene");
|
||||
//changeToScene(m_scenes[m_sceneBeforeIdle].name.c_str());
|
||||
String sceneName = m_scenes[m_sceneBeforeIdle].name;
|
||||
m_sceneBeforeIdle = -1;
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetScene, sceneName.c_str()});
|
||||
}
|
||||
}
|
||||
ConfigTaskMixin::handleEvent(evt);
|
||||
m_idx = 0;
|
||||
m_scenes = scenes;
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::handleConfigChange(const Configuration& cfg)
|
||||
Sequencer::handleEvent(const InputEvent& evt)
|
||||
{
|
||||
Log.notice("sequencer: Loading scenes");
|
||||
|
||||
JsonObject sceneList = cfg.get("scenes");
|
||||
m_idx = 0;
|
||||
m_scenes.clear();
|
||||
for(JsonPair pair : sceneList) {
|
||||
Log.notice("sequencer: Found scene %s", pair.key().c_str());
|
||||
std::vector<String> patterns;
|
||||
for(const char* taskName : pair.value().as<JsonArray>()) {
|
||||
patterns.push_back(taskName);
|
||||
if (evt.intent == InputEvent::ReadyToRoll && !m_scenes.empty()) {
|
||||
Log.notice("Starting pattern %s!", m_scenes[m_idx].name);
|
||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
m_scenes.push_back(Scene{pair.key().c_str(), patterns});
|
||||
}
|
||||
if (m_scenes.empty()) {
|
||||
Log.warning("sequencer: Zero scenes loaded!");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Sequencer::changeToScene(const char* name)
|
||||
{
|
||||
Log.notice("sequencer: Switching scene to %s!", name);
|
||||
} else if (evt.intent == InputEvent::SetScene && evt.asString() == m_scenes[m_idx].name) {
|
||||
return;
|
||||
} else if (evt.intent == InputEvent::SetScene) {
|
||||
Log.notice("Switching scene to %s!", evt.asString());
|
||||
|
||||
int newIdx = 0;
|
||||
bool found = false;
|
||||
for(newIdx = 0; newIdx < m_scenes.size(); newIdx++) {
|
||||
if (!strcmp(name, m_scenes[newIdx].name.c_str())) {
|
||||
if (!strcmp(evt.asString(), m_scenes[newIdx].name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -120,10 +81,9 @@ Sequencer::changeToScene(const char* name)
|
||||
m_scenes[m_idx].stop();
|
||||
m_idx = newIdx;
|
||||
m_scenes[m_idx].start();
|
||||
return true;
|
||||
} else {
|
||||
Log.notice("sequencer: Scene not found!");
|
||||
return false;
|
||||
Log.notice("Scene not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +92,7 @@ Sequencer::doScenes(Args& args, Print& out)
|
||||
{
|
||||
out.println("Available scenes: ");
|
||||
for (auto scene : scenes()) {
|
||||
out.println(scene.name.c_str());
|
||||
out.println(scene.name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,8 +101,8 @@ static String s;
|
||||
void
|
||||
Sequencer::doScene(Args& args, Print& out)
|
||||
{
|
||||
String sceneName = args[1];
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetScene, sceneName.c_str()});
|
||||
s = args[1];
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetScene, s.c_str()});
|
||||
}
|
||||
|
||||
const std::vector<Command>&
|
||||
|
@ -2,15 +2,14 @@
|
||||
|
||||
#include <Figment.h>
|
||||
#include <vector>
|
||||
#include "Config.h"
|
||||
|
||||
class Sequencer: public Task, ConfigTaskMixin {
|
||||
class Sequencer: public Task {
|
||||
public:
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
String name;
|
||||
std::vector<String> patterns;
|
||||
const char* name;
|
||||
std::vector<const char*> patterns;
|
||||
void start();
|
||||
void stop();
|
||||
};
|
||||
@ -20,7 +19,7 @@ public:
|
||||
void loop() override;
|
||||
void onStart() override;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
void handleConfigChange(const Configuration& cfg) override;
|
||||
void setScenes(std::vector<Scene> &&scenes);
|
||||
|
||||
const char* currentSceneName();
|
||||
const std::vector<Scene> scenes() const;
|
||||
@ -28,12 +27,8 @@ public:
|
||||
|
||||
private:
|
||||
int m_idx;
|
||||
int m_sceneBeforeIdle;
|
||||
bool m_hasStartupScene;
|
||||
std::vector<Scene> m_scenes;
|
||||
|
||||
bool changeToScene(const char* name);
|
||||
|
||||
void doScene(Args& args, Print& out);
|
||||
void doScenes(Args& args, Print& out);
|
||||
};
|
||||
|
@ -1,46 +0,0 @@
|
||||
#include "Cloudy.h"
|
||||
#include "../Static.h"
|
||||
|
||||
Cloudy::Cloudy()
|
||||
: Figment("Cloudy")
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Cloudy::loop()
|
||||
{
|
||||
m_noiseOffset += 1;
|
||||
EVERY_N_MILLISECONDS(60) {
|
||||
m_hue.update(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cloudy::render(Display* dpy) const
|
||||
{
|
||||
Surface sfc = Surface(dpy, {0, 0}, {255, 128});
|
||||
uint8_t noiseY = sin8(m_noiseOffset % 255);
|
||||
uint8_t noiseX = cos8(m_noiseOffset % 255);
|
||||
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
uint8_t brightness = inoise8(noiseX + surfaceCoords.x, noiseY + surfaceCoords.y);
|
||||
uint8_t saturation = inoise8(noiseY + surfaceCoords.y, noiseX + surfaceCoords.x);
|
||||
nblend(pixel, CHSV(m_hue, scale8(saturation, 128), brightness), 255 - surfaceCoords.y);
|
||||
});
|
||||
Surface horizon = Surface(dpy, {0, 64}, {255, 128});
|
||||
horizon.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
uint8_t brightness = inoise8(noiseX + surfaceCoords.x, noiseY + surfaceCoords.y);
|
||||
uint8_t saturation = inoise8(noiseY + surfaceCoords.y, noiseX + surfaceCoords.x);
|
||||
nblend(pixel, CHSV(m_hue + (saturation % 45), std::max((uint8_t)64, (uint8_t)saturation), brightness), surfaceCoords.y);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Cloudy::handleEvent(const InputEvent& evt) {
|
||||
if (evt.intent == InputEvent::SetColor) {
|
||||
CHSV next = rgb2hsv_approximate(evt.asRGB());
|
||||
m_hue.set(next.h);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_ALLOC(Cloudy);
|
||||
STATIC_TASK(Cloudy);
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
#include <Figments.h>
|
||||
|
||||
class Cloudy: public Figment {
|
||||
private:
|
||||
uint16_t m_noiseOffset;
|
||||
AnimatedNumber m_hue;
|
||||
public:
|
||||
Cloudy();
|
||||
void render(Display* dpy) const override;
|
||||
void loop() override;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
};
|
@ -8,13 +8,11 @@ RainAnimation::RainAnimation() : Figment("Rain")
|
||||
void
|
||||
RainAnimation::render(Display* dpy) const
|
||||
{
|
||||
Surface ground = Surface(dpy, {0, 64}, {255, 255});
|
||||
Surface sfc = Surface(dpy, {0, 0}, {255, 255});
|
||||
uint8_t noiseY = sin8(m_noiseOffset % 255);
|
||||
uint8_t noiseX = cos8(m_noiseOffset % 255);
|
||||
ground.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
uint8_t brightness = inoise8(noiseX + surfaceCoords.x, noiseY + surfaceCoords.y);
|
||||
uint8_t saturation = inoise8(noiseY + surfaceCoords.y, noiseX + surfaceCoords.x);
|
||||
nblend(pixel, CHSV(m_hue, saturation, brightness), 255 - surfaceCoords.y);
|
||||
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
pixel = CHSV(m_hue, inoise8(noiseX + coords.x, coords.y), inoise8(m_noiseOffset + coords.x, noiseY + coords.y));
|
||||
});
|
||||
m_drops.render(dpy);
|
||||
}
|
||||
@ -22,7 +20,7 @@ RainAnimation::render(Display* dpy) const
|
||||
void
|
||||
RainAnimation::loop()
|
||||
{
|
||||
EVERY_N_MILLISECONDS(120) {
|
||||
EVERY_N_MILLISECONDS(250) {
|
||||
m_drops.update();
|
||||
}
|
||||
EVERY_N_MILLISECONDS(60) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
class RainAnimation: public Figment {
|
||||
private:
|
||||
struct Raindrop {
|
||||
int size = 10;
|
||||
int size = 20;
|
||||
int x = random(255);
|
||||
int y = random(255);
|
||||
CHSV fg{180, 255, 255};
|
||||
|
@ -47,20 +47,22 @@ void SolidAnimation::loop() {
|
||||
});
|
||||
m_blobs.update();
|
||||
}
|
||||
m_noiseOffset += 1;
|
||||
}
|
||||
|
||||
void SolidAnimation::render(Display* dpy) const {
|
||||
Surface sfc = Surface(dpy, {0, 0}, {255, 255});
|
||||
uint8_t noiseY = sin8(m_noiseOffset % 255);
|
||||
uint8_t noiseX = cos8(m_noiseOffset % 255);
|
||||
CHSV color(rgb2hsv_approximate(CRGB(m_red, m_green, m_blue)));
|
||||
|
||||
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
uint8_t brightness = inoise8(noiseX + surfaceCoords.x, noiseY + surfaceCoords.y);
|
||||
uint8_t saturation = inoise8(noiseY + surfaceCoords.y, noiseX + surfaceCoords.x);
|
||||
nblend(pixel, CHSV(color.h, std::max((uint8_t)128, saturation), scale8(color.v, brightness)), 128);
|
||||
});
|
||||
CRGB color(m_red.value(), m_green.value(), m_blue.value());
|
||||
CRGB scaledPrev = m_prevColor;
|
||||
scaledPrev = color.nscale8(30);
|
||||
uint8_t frame = ease8InOutApprox(m_changePct);
|
||||
if (F_LIKELY(frame == 255)) {
|
||||
Surface(dpy, {0, 0}, {255, 255}) = color.nscale8(10);
|
||||
} else {
|
||||
uint8_t cutoff = (frame / 2);
|
||||
uint8_t rotation = m_horizontal ? 0 : 128;
|
||||
Surface(dpy, {0, 0}, {128 - cutoff, 255}, rotation) = scaledPrev;
|
||||
Surface(dpy, {128 - cutoff, 0}, {128 + cutoff, 255}, rotation) = scaledPrev;
|
||||
Surface(dpy, {128 + cutoff, 0}, {255, 255}, rotation) = scaledPrev;
|
||||
}
|
||||
m_blobs.render(dpy);
|
||||
}
|
||||
STATIC_ALLOC(SolidAnimation);
|
||||
|
@ -5,7 +5,6 @@
|
||||
class SolidAnimation: public Figment {
|
||||
private:
|
||||
AnimatedNumber m_red, m_green, m_blue, m_changePct;
|
||||
uint16_t m_noiseOffset;
|
||||
CRGB m_curColor;
|
||||
CRGB m_prevColor;
|
||||
static constexpr int blobCount = 20;
|
||||
|
@ -7,104 +7,25 @@ TestAnimation::loop()
|
||||
{
|
||||
m_x += 1;
|
||||
m_y += 1;
|
||||
m_hue += 1;
|
||||
}
|
||||
|
||||
void
|
||||
TestAnimation::handleEvent(const InputEvent& evt)
|
||||
{
|
||||
if (evt.intent == InputEvent::SetColor) {
|
||||
m_color = evt.asRGB();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestAnimation::render(Display* dpy) const
|
||||
{
|
||||
CRGB drawColor = blend(m_color, CHSV(m_hue, 255, 255), 128);
|
||||
if (m_mode == Raw) {
|
||||
for(unsigned int i = 0; i < dpy->pixelCount(); i++) {
|
||||
dpy->pixelAt(i) = drawColor;
|
||||
dpy->pixelAt(i) = CRGB{255, 255, 255};
|
||||
}
|
||||
} else if (m_mode == Top) {
|
||||
Surface{dpy, {0, 0}, {255, 0}} = drawColor;
|
||||
} else if (m_mode == Left) {
|
||||
Surface{dpy, {0, 0}, {0, 255}} = drawColor;
|
||||
} else if (m_mode == Right) {
|
||||
Surface{dpy, {255, 0}, {255, 255}} = drawColor;
|
||||
} else if (m_mode == Bottom) {
|
||||
Surface{dpy, {0, 255}, {255, 255}} = drawColor;
|
||||
} else if (m_mode == Outline) {
|
||||
return;
|
||||
// Blank the canvas to white
|
||||
Surface{dpy, {0, 0}, {255, 255}} = CRGB{255, 255, 255};
|
||||
// Draw red line on top row
|
||||
Surface{dpy, {0, 0}, {255, 0}} = CRGB{255, 0, 0};
|
||||
// Green line on first column
|
||||
Surface{dpy, {0, 0}, {0, 255}} = CRGB{0, 255, 0};
|
||||
} else if (m_mode == White) {
|
||||
Surface{dpy, {0, 0}, {255, 255}} = CRGB{255, 255, 255};
|
||||
} else if (m_mode == Red) {
|
||||
Surface{dpy, {0, 0}, {255, 255}} = CRGB{255, 0, 0};
|
||||
} else if (m_mode == Green) {
|
||||
Surface{dpy, {0, 0}, {255, 255}} = CRGB{0, 255, 0};
|
||||
} else if (m_mode == Blue) {
|
||||
Surface{dpy, {0, 0}, {255, 255}} = CRGB{0, 0, 255};
|
||||
} else if (m_mode == HSV) {
|
||||
Surface{dpy, {0, 0}, {255, 255}}.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates&, const VirtualCoordinates& surfaceCoords) {
|
||||
pixel = CHSV(coords.x, coords.y, 255);
|
||||
});
|
||||
} else if (m_mode == RGB) {
|
||||
Surface{dpy, {0, 0}, {255, 255}}.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates&, const VirtualCoordinates& surfaceCoords) {
|
||||
pixel = CRGB(coords.x, coords.y, (coords.x/2) + (coords.y/2));
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
TestAnimation::doMode(Args& args, Print& out)
|
||||
{
|
||||
String s = args[1];
|
||||
if (s == "") {
|
||||
out.println("Available modes: raw outline white red green blue hsv rgb none top left right bottom");
|
||||
out.print("Current mode: ");
|
||||
out.println((int)m_mode);
|
||||
} else if (s == "raw") {
|
||||
m_mode = Raw;
|
||||
} else if (s == "outline") {
|
||||
m_mode = Outline;
|
||||
} else if (s == "white") {
|
||||
m_mode = White;
|
||||
} else if (s == "red") {
|
||||
m_mode = Red;
|
||||
} else if (s == "green") {
|
||||
m_mode = Green;
|
||||
} else if (s == "blue") {
|
||||
m_mode = Blue;
|
||||
} else if (s == "hsv") {
|
||||
m_mode = HSV;
|
||||
} else if (s == "rgb") {
|
||||
m_mode = RGB;
|
||||
} else if (s == "top") {
|
||||
m_mode = Top;
|
||||
} else if (s == "left") {
|
||||
m_mode = Left;
|
||||
} else if (s == "right") {
|
||||
m_mode = Right;
|
||||
} else if (s == "bottom") {
|
||||
m_mode = Bottom;
|
||||
} else if (s == "none") {
|
||||
m_mode = None;
|
||||
} else {
|
||||
out.println("Unknown test mode");
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Command>&
|
||||
TestAnimation::commands() const
|
||||
{
|
||||
static const std::vector<Command> _commands{
|
||||
{"test", &TestAnimation::doMode}
|
||||
};
|
||||
return _commands;
|
||||
//Surface{dpy, {m_x, 0}, {m_x, 255}} = CRGB{255, 0, 0};
|
||||
///Surface{dpy, {0, m_y}, {255, m_y}} = CRGB{255, 0, 0};
|
||||
//dpy->pixelAt(VirtualCoordinates{m_x, m_y}) = CRGB{255, 0, 255};
|
||||
}
|
||||
|
||||
STATIC_ALLOC(TestAnimation);
|
||||
|
@ -5,31 +5,8 @@ public:
|
||||
TestAnimation() : Figment("Test") {}
|
||||
void loop() override;
|
||||
void render(Display* dpy) const override;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
const std::vector<Command>& commands() const override;
|
||||
|
||||
private:
|
||||
uint8_t m_x;
|
||||
uint8_t m_y;
|
||||
CRGB m_color;
|
||||
uint8_t m_hue;
|
||||
|
||||
enum Mode {
|
||||
None,
|
||||
Raw,
|
||||
Outline,
|
||||
White,
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
HSV,
|
||||
RGB,
|
||||
Top,
|
||||
Left,
|
||||
Right,
|
||||
Bottom
|
||||
};
|
||||
|
||||
Mode m_mode = None;
|
||||
void doMode(Args&, Print&);
|
||||
};
|
||||
|
@ -1,26 +0,0 @@
|
||||
#include <Figments.h>
|
||||
#include "../Static.h"
|
||||
|
||||
class Thinking: public Figment {
|
||||
public:
|
||||
Thinking() : Figment("Thinking") {}
|
||||
void render(Display* dpy) const override {
|
||||
Surface sfc = Surface(dpy, {0, 0}, {255, 255});
|
||||
uint8_t noiseY = sin8(m_noiseOffset % 255);
|
||||
uint8_t noiseX = cos8(m_noiseOffset % 255);
|
||||
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
|
||||
pixel = CHSV(inoise8(noiseX + surfaceCoords.x, noiseY + surfaceCoords.y), 255, inoise8(noiseY + surfaceCoords.y, noiseX + surfaceCoords.x));
|
||||
});
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
EVERY_N_MILLISECONDS(15) {
|
||||
m_noiseOffset += 1;
|
||||
}
|
||||
}
|
||||
private:
|
||||
int m_noiseOffset;
|
||||
};
|
||||
|
||||
STATIC_ALLOC(Thinking);
|
||||
STATIC_TASK(Thinking);
|
@ -131,17 +131,10 @@ SerialInput::doHelp(Args& args, Print& out)
|
||||
out.println("Available commands:");
|
||||
auto sched = MainLoop::instance()->scheduler;
|
||||
for(auto task : sched.tasks) {
|
||||
const auto taskCommands = task->commands();
|
||||
if (!taskCommands.empty()) {
|
||||
out.print(task->name);
|
||||
out.println(":");
|
||||
out.print("\t");
|
||||
for(auto &command : task->commands()) {
|
||||
out.print(command.name);
|
||||
out.print(" ");
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
|
@ -1,147 +0,0 @@
|
||||
#include "Weather.h"
|
||||
#include "../Static.h"
|
||||
|
||||
Weather::Weather()
|
||||
: BufferedInputSource("Weather")
|
||||
{
|
||||
m_parser.setListener(this);
|
||||
}
|
||||
|
||||
void
|
||||
Weather::handleEvent(const InputEvent& evt)
|
||||
{
|
||||
BufferedInputSource::handleEvent(evt);
|
||||
OnlineTaskMixin::handleEvent(evt);
|
||||
if (evt.intent == InputEvent::IdleStart) {
|
||||
doWeatherEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::loop()
|
||||
{
|
||||
BufferedInputSource::loop();
|
||||
OnlineTaskMixin::loop();
|
||||
EVERY_N_SECONDS(3600) {
|
||||
update();
|
||||
}
|
||||
if (m_fetching) {
|
||||
parse();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::update()
|
||||
{
|
||||
if (!m_fetching) {
|
||||
Log.info("Connecting to weather service");
|
||||
m_parser.reset();
|
||||
const String host = "api.openweathermap.org";
|
||||
const uint8_t port = 80;
|
||||
#ifdef ESP32
|
||||
m_fetching = m_client.connect(host.c_str(), port);
|
||||
#else
|
||||
m_fetching = m_client.connect(host, port);
|
||||
#endif
|
||||
if (m_fetching) {
|
||||
Log.info("Ok!");
|
||||
m_foundBody = false;
|
||||
m_client.print(
|
||||
"GET /data/2.5/weather?id=2950159&appid=f7aea43b54bcd27f542195a809c1463f&units=metric&lang=en HTTP/1.1\r\n"
|
||||
"Host: api.openweathermap.org\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
);
|
||||
} else {
|
||||
Log.info("No :(");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::parse()
|
||||
{
|
||||
if (m_client.available()) {
|
||||
char c = m_client.read();
|
||||
if (c == '{') {
|
||||
m_foundBody = true;
|
||||
}
|
||||
if (m_foundBody) {
|
||||
m_parser.parse(c);
|
||||
}
|
||||
} else if (!m_client.connected()) {
|
||||
Log.info("Disconnected");
|
||||
m_client.stop();
|
||||
m_fetching = false;
|
||||
doWeatherEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::key(String key)
|
||||
{
|
||||
m_curKey = key;
|
||||
}
|
||||
|
||||
void
|
||||
Weather::doWeatherEvent()
|
||||
{
|
||||
Log.info("Dispatching weather event for %d", m_weatherId);
|
||||
if (m_weatherId >= 200 && m_weatherId < 300 ) {
|
||||
setEvent(InputEvent::SetScene, "Thunderstorm");
|
||||
setEvent(InputEvent::SetColor, CRGB(255, 187, 0));
|
||||
} else if (m_weatherId >= 300 && m_weatherId < 400) {
|
||||
setEvent(InputEvent::SetScene, "Drizzle");
|
||||
setEvent(InputEvent::SetColor, CRGB(93, 183, 201));
|
||||
} else if (m_weatherId >= 500 && m_weatherId < 600) {
|
||||
setEvent(InputEvent::SetScene, "Rain");
|
||||
setEvent(InputEvent::SetColor, CRGB(64, 64, 255));
|
||||
} else if (m_weatherId >= 600 && m_weatherId < 700) {
|
||||
setEvent(InputEvent::SetScene, "Snow");
|
||||
setEvent(InputEvent::SetColor, CRGB(255, 255, 255));
|
||||
} else if (m_weatherId == 701) {
|
||||
setEvent(InputEvent::SetScene, "Mist");
|
||||
setEvent(InputEvent::SetColor, CRGB(77, 255, 124));
|
||||
} else if (m_weatherId == 800) {
|
||||
setEvent(InputEvent::SetScene, "Clear");
|
||||
setEvent(InputEvent::SetColor, CRGB(255, 249, 79));
|
||||
} else if (m_weatherId >= 801 && m_weatherId <= 804) {
|
||||
setEvent(InputEvent::SetScene, "Cloudy");
|
||||
setEvent(InputEvent::SetColor, CRGB(102, 110, 110));
|
||||
} else {
|
||||
setEvent(InputEvent::SetScene, "UnknownWeather");
|
||||
setEvent(InputEvent::SetColor, CRGB(255, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::value(String value)
|
||||
{
|
||||
if (m_curField == "weather") {
|
||||
if (m_curKey == "id") {
|
||||
m_weatherId = atoi(value.c_str());
|
||||
} else if (m_curKey == "main") {
|
||||
Log.info("WEATHER ENCOUNTERED: %s", value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Weather::startObject()
|
||||
{
|
||||
m_curField = m_curKey;
|
||||
}
|
||||
|
||||
void
|
||||
Weather::endObject()
|
||||
{
|
||||
m_curField = "";
|
||||
}
|
||||
|
||||
void Weather::endArray() {}
|
||||
void Weather::startArray() {}
|
||||
void Weather::endDocument() {}
|
||||
void Weather::whitespace(char c) {}
|
||||
void Weather::startDocument() {}
|
||||
|
||||
STATIC_ALLOC(Weather);
|
||||
STATIC_TASK(Weather);
|
@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Figments.h>
|
||||
#include <JsonListener.h>
|
||||
#include <JsonStreamingParser.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
class Weather : public BufferedInputSource, OnlineTaskMixin, JsonListener {
|
||||
public:
|
||||
Weather();
|
||||
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
void loop() override;
|
||||
|
||||
void onOnline() override {
|
||||
update();
|
||||
}
|
||||
|
||||
void onStart() override {
|
||||
update();
|
||||
}
|
||||
|
||||
void loopOnline() override {
|
||||
}
|
||||
|
||||
void update();
|
||||
void parse();
|
||||
|
||||
virtual void whitespace(char c);
|
||||
virtual void startDocument();
|
||||
virtual void key(String key);
|
||||
virtual void value(String value);
|
||||
virtual void endArray();
|
||||
virtual void endObject();
|
||||
virtual void endDocument();
|
||||
virtual void startArray();
|
||||
virtual void startObject();
|
||||
|
||||
private:
|
||||
JsonStreamingParser m_parser;
|
||||
WiFiClient m_client;
|
||||
bool m_fetching = false;
|
||||
String m_curKey;
|
||||
String m_curField;
|
||||
bool m_foundBody = false;
|
||||
|
||||
int m_weatherId = 0;
|
||||
|
||||
void doWeatherEvent();
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ ColorSequenceInput<9> idleCycle{{
|
||||
CRGB(0, 255, 255), // Cyan
|
||||
}, "IdleColors"};
|
||||
|
||||
//REGISTER_TASK(idleCycle);
|
||||
REGISTER_TASK(idleCycle);
|
||||
|
||||
ColorSequenceInput<7> rainbowCycle{{
|
||||
CRGB(255, 0, 0), // Red
|
||||
@ -54,7 +54,7 @@ ColorSequenceInput<7> rainbowCycle{{
|
||||
CRGB(128, 0, 128), // Purple
|
||||
}, "Rainbow"};
|
||||
|
||||
//REGISTER_TASK(rainbowCycle);
|
||||
REGISTER_TASK(rainbowCycle);
|
||||
|
||||
MainLoop* runner = &SafeMode::safeModeApp;
|
||||
|
||||
|
@ -9,8 +9,6 @@ __NOINIT_ATTR static uint8_t s_rebootCount;
|
||||
__NOINIT_ATTR static uint16_t s_forceSafeMode;
|
||||
#define SAFE_MODE_MAGIC 6942
|
||||
|
||||
__NOINIT_ATTR static struct tm s_lastGoodTime;
|
||||
|
||||
WiFiUDP wifiUdp;
|
||||
NTPClient timeClient(wifiUdp, "pool.ntp.org", 0);
|
||||
|
||||
@ -46,12 +44,8 @@ template<>
|
||||
bool
|
||||
PlatformImpl<HAL_ESP8266>::getLocalTime(struct tm* timedata, int timezone)
|
||||
{
|
||||
if (timeClient.isTimeSet()) {
|
||||
s_lastGoodTime.tm_min = (timeClient.getMinutes() + timezone) % 23;
|
||||
s_lastGoodTime.tm_hour = timeClient.getHours();
|
||||
s_lastGoodTime.tm_sec = timeClient.getSeconds();
|
||||
}
|
||||
memcpy(timedata, &s_lastGoodTime, sizeof(s_lastGoodTime));
|
||||
timedata->tm_hour = (timeClient.getHours() + timezone) % 23;
|
||||
timedata->tm_min = timeClient.getMinutes();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -60,9 +54,6 @@ void
|
||||
PlatformImpl<HAL_ESP8266>::startNTP()
|
||||
{
|
||||
timeClient.begin();
|
||||
if (s_lastGoodTime.tm_hour >= 24 || s_lastGoodTime.tm_min >= 60 || s_lastGoodTime.tm_sec >= 60) {
|
||||
memset(&s_lastGoodTime, 0, sizeof(s_lastGoodTime));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -64,7 +64,9 @@ public:
|
||||
uint8_t val = lerp8by8(0, m_brightness, pixelMod);
|
||||
CHSV blobColor(m_hue, m_saturation, val);
|
||||
|
||||
nblend(pixel, CRGB(blobColor), 80);
|
||||
//PhysicalCoordinates pos{startPos.x + (i*m_fadeDir), startPos.y};
|
||||
|
||||
pixel += blend(CRGB(blobColor), pixel, 80);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user