From 5a41c03e8ec96bec4ac29a81b362f409b477b5bf Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Wed, 17 Nov 2021 23:18:10 +0100 Subject: [PATCH] Separate FreeRTOS task for LED --- espmusicmouse/doc.md | 81 ++++++++++-- espmusicmouse/lib/ledtl/LedStripe.h | 55 -------- .../lib/ledtl/containers/LedStripRGBW.h | 7 + espmusicmouse/lib/ledtl/effects/Circular.h | 20 +++ espmusicmouse/lib/ledtl/effects/Common.h | 20 ++- espmusicmouse/lib/ledtl/effects/Static.h | 51 +++++++ espmusicmouse/src/TaskLed.h | 124 ++++++++++++++++++ espmusicmouse/src/main.cpp | 35 ++--- 8 files changed, 302 insertions(+), 91 deletions(-) delete mode 100644 espmusicmouse/lib/ledtl/LedStripe.h create mode 100644 espmusicmouse/lib/ledtl/effects/Static.h create mode 100644 espmusicmouse/src/TaskLed.h diff --git a/espmusicmouse/doc.md b/espmusicmouse/doc.md index e31fd7e..9a5305e 100644 --- a/espmusicmouse/doc.md +++ b/espmusicmouse/doc.md @@ -1,11 +1,74 @@ -Connections ------------ -- SDA purple 23 -- SCK green 22 -- MOSI orange 18 -- MISO yellow 19 -- IRQ grey +Reader +---------- + - GND black -- RST blue 5 -- 3.3V red \ No newline at end of file +- RST blue 3.3V +- 3.3V red +- MISO brown 21 +- SDA green 19 +- SCK yellow 18 +- MOSI orange 5 +- IRQ green single cable not connected + + +Button Board: +------------- + +- rot in | white 13 +- btn2 led | grey 12 +- btn2 in | purple 14 +- rotB | blue 27 +- rotA | green 26 +- btn1 in | yellow 25 +- btn1 led | orange 33 + +rot="rotary encoder" +in=button sense in +led = 5V pwm + + + +Firmware Planning +----------------- + +- input commands: + - led: effect + parameters + - off + - single color + - multiple color HSV fade, list of colors with timings + - circular motion (already exists) + - chained events? e.g. circle two times then fade + - effects: + - welle fuer an und aus + - breathe waehrend an, oder farbgradient + - +- output infos: + - nfc read: with id + - nfc remove + - button presses, (possible also long press, double click, etc) + - rotary encoder up down + current numeric state + - on led effect end? + + + + +TODO +---- + +1) case redesign + - slightly smaller led ring (10mm -> 9mm) [ok] + - thicker top of inner ring, but cutouts for reader [ok] + - adjust reader stands position [ok] + - bottom for led ring snap-in [ok] + - bottom for inner ring [ok] + - stands for own "pcb" [ok] + - 2 cutouts for cables [ok] + - checks, compared to existing print + - same diameter, very slightly smaller + - larger overlap of LED ring + - minimal wall thickness for led ring top and side + - check total height - compare to existing + + + diff --git a/espmusicmouse/lib/ledtl/LedStripe.h b/espmusicmouse/lib/ledtl/LedStripe.h deleted file mode 100644 index eec80ce..0000000 --- a/espmusicmouse/lib/ledtl/LedStripe.h +++ /dev/null @@ -1,55 +0,0 @@ - -// overall layout: -// queue for each led stripe -// - -enum class EffectID -{ - OFF, - STATIC, - CIRCLE, - CIRCLE_WAVE, - COLOR_FADE, - RAINBOW_FADE, -}; - -struct EffectCircularConfig -{ - float speed; // in degrees per second - float width; // width in degrees - float brightnessFalloffFactor; -}; - -template -class AbstractEffect -{ -public: - virtual int operator()(TLedStrip &s) = 0; -}; - -template -class EffectCircle : public AbstractEffect -{ -public: - EffectCircle(const EffectCircularConfig &cfg) : config_(cfg) {} - int operator()(TLedStrip &s) override; - -private: - EffectCircularConfig config_; - float currentPosition_; // between 0 and 1 -}; - -unsigned char effectStorage[128]; - -template -AbstractEffect *makeEffect(const char *buffer) -{ - const EffectID &effectId = *reinterpret_cast(buffer); - if (effectId == EffectID::CIRCLE) - { - auto cfg = reinterpret_cast(buffer + sizeof(EffectID)); - return new (effectStorage) EffectCircle(*cfg); - } - // read effect id code from buffer - // read config from buffer -} diff --git a/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h b/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h index 7957655..4d39212 100644 --- a/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h +++ b/espmusicmouse/lib/ledtl/containers/LedStripRGBW.h @@ -75,6 +75,13 @@ void setLedRGBW(LedStripRGBW &s, int idx, const ColorRGBW &c) s.set(idx, c.r, c.g, c.b, c.w); } +template +void setLedRGBW(LedStripRGBW &s, int beginIdx, int endIdx, const ColorRGBW &c) +{ + for (int i = beginIdx; i < endIdx; ++i) + s.set(i, c.r, c.g, c.b, c.w); +} + template void clear(LedStripRGBW &s) { diff --git a/espmusicmouse/lib/ledtl/effects/Circular.h b/espmusicmouse/lib/ledtl/effects/Circular.h index f39bde0..25a1528 100644 --- a/espmusicmouse/lib/ledtl/effects/Circular.h +++ b/espmusicmouse/lib/ledtl/effects/Circular.h @@ -17,6 +17,7 @@ class EffectCircular public: static constexpr auto NUM_LEDS = numLeds(); static constexpr int DELAY_MS = 10; + using ConfigType = EffectCircularConfig; EffectCircular(const EffectCircularConfig &cfg, TLedStrip &ledStrip) : config_(cfg), @@ -72,3 +73,22 @@ private: int widthInLeds_; float invWidth_; }; + +// Traits +template <> +struct EffectIdToConfig +{ + using type = EffectCircularConfig; +}; + +template <> +struct EffectConfigToId +{ + static constexpr auto id = EffectId::CIRCULAR; +}; + +template +struct EffectIdToClass +{ + using type = EffectCircular; +}; \ No newline at end of file diff --git a/espmusicmouse/lib/ledtl/effects/Common.h b/espmusicmouse/lib/ledtl/effects/Common.h index c21e004..b4036cd 100644 --- a/espmusicmouse/lib/ledtl/effects/Common.h +++ b/espmusicmouse/lib/ledtl/effects/Common.h @@ -1,7 +1,7 @@ +#pragma once -enum class EffectID +enum class EffectId { - OFF, STATIC, CIRCULAR, CIRCLE_WAVE, @@ -9,9 +9,17 @@ enum class EffectID RAINBOW_FADE, }; -template -class AbstractEffect +template +struct EffectIdToConfig +{ +}; + +template +struct EffectConfigToId +{ +}; + +template +struct EffectIdToClass { -public: - virtual int operator()(TLedStrip &s) = 0; }; \ No newline at end of file diff --git a/espmusicmouse/lib/ledtl/effects/Static.h b/espmusicmouse/lib/ledtl/effects/Static.h new file mode 100644 index 0000000..c1d356d --- /dev/null +++ b/espmusicmouse/lib/ledtl/effects/Static.h @@ -0,0 +1,51 @@ +#pragma once + +#include "effects/Common.h" +#include "helpers/ColorRGBW.h" + +struct EffectStaticConfig +{ + ColorRGBW color; +}; + +template +class EffectStatic +{ +public: + static constexpr auto NUM_LEDS = numLeds(); + + EffectStatic(const EffectStaticConfig &cfg, TLedStrip &ledStrip) + : config_(cfg), + ledStrip_(ledStrip) + { + } + + int operator()() + { + setLedRGBW(ledStrip_, 0, NUM_LEDS, config_.color); + return 10000; // nothing changing, return some large time to sleep + } + +private: + EffectStaticConfig config_; + TLedStrip &ledStrip_; +}; + +// Traits +template <> +struct EffectIdToConfig +{ + using type = EffectStaticConfig; +}; + +template <> +struct EffectConfigToId +{ + static constexpr auto id = EffectId::STATIC; +}; + +template +struct EffectIdToClass +{ + using type = EffectStatic; +}; \ No newline at end of file diff --git a/espmusicmouse/src/TaskLed.h b/espmusicmouse/src/TaskLed.h new file mode 100644 index 0000000..43fa388 --- /dev/null +++ b/espmusicmouse/src/TaskLed.h @@ -0,0 +1,124 @@ +#pragma once + +#include "effects/Circular.h" +#include "effects/Common.h" +#include "drivers/Esp32DriverRGBW.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#include +#include + +static constexpr int MAX_EFFECT_CONFIG_SIZE = 128; +static constexpr int MAX_EFFECT_CLASS_SIZE = 128; + +template +class LedTask +{ +public: + void begin(TLedStrip &strip, Esp32DriverRGBW &driver); + + template + void startEffect(const TEffectConfig &cfg); + +private: + template + friend void _led_task_func(void *); + + QueueHandle_t queue_ = nullptr; + TLedStrip *ledStrip_ = nullptr; + Esp32DriverRGBW *driver_ = nullptr; +}; + +// ----------------------------------------------------------------------------------------------- + +template +bool dispatchEffectId(EffectId dynamicEffectId, std::function &effectFunction, TLedStrip &ledStrip, + unsigned char *msgBuffer, unsigned char *effectStorage) +{ + if (staticEffectId == dynamicEffectId) + { + typename EffectIdToConfig::type cfg; + memcpy(&cfg, msgBuffer + sizeof(EffectId), sizeof(decltype(cfg))); + using TEffect = typename EffectIdToClass::type; + static_assert(sizeof(TEffect) < MAX_EFFECT_CLASS_SIZE, "Effect to big for effectStorage, increase MAX_EFFECT_CLASS_SIZE"); + TEffect *effect = new (effectStorage) TEffect(cfg, ledStrip); + effectFunction = [effect]() + { return (*effect)(); }; + return true; + } + else + return false; +} + +template +void _led_task_func(void *params) +{ + LedTask *task = reinterpret_cast *>(params); + unsigned char msgBuffer[MAX_EFFECT_CONFIG_SIZE]; + unsigned char effectStorage[MAX_EFFECT_CLASS_SIZE]; + + std::function effectFunction = []() -> int + { return 100000; }; + + int timeoutMsForEffect = 100000; // huge timeout since there is no effect in the beginning + + while (true) + { + if (xQueueReceive(task->queue_, msgBuffer, (TickType_t)(timeoutMsForEffect / portTICK_PERIOD_MS)) == pdTRUE) + { + // Read and parse effect from queue + EffectId id; + memcpy(&id, msgBuffer, sizeof(EffectId)); + + TLedStrip &ledStrip = *(task->ledStrip_); + // clang-format off + if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");} + else if (dispatchEffectId(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");} + // clang-format on + + timeoutMsForEffect = 0; + } + else + { + timeoutMsForEffect = effectFunction(); + task->driver_->writeSync(task->ledStrip_->rawData(), task->ledStrip_->numLeds()); + } + } +} + +template +void LedTask::begin(TLedStrip &strip, Esp32DriverRGBW &driver) +{ + queue_ = xQueueCreate(4, MAX_EFFECT_CONFIG_SIZE); + if (!queue_) + Serial.println("Failed to create LED effect queue"); + + ledStrip_ = &strip; + driver_ = &driver; + + xTaskCreate(_led_task_func, "led task", MAX_EFFECT_CLASS_SIZE + MAX_EFFECT_CONFIG_SIZE + 2048, + (void *)(this), 1, nullptr); +} + +template +template +void LedTask::startEffect(const TEffectConfig &cfg) +{ + static constexpr auto msgSize = sizeof(TEffectConfig) + sizeof(EffectId); + static_assert(msgSize < MAX_EFFECT_CONFIG_SIZE, + "Effect config too large, increase MAX_EFFECT_CONFIG_SIZE"); + + unsigned char buffer[MAX_EFFECT_CONFIG_SIZE]; + if (queue_ == nullptr) + { + Serial.println("Trying to start effect before queue was set up!"); + return; + } + EffectId id = EffectConfigToId::id; + memcpy(buffer, &id, sizeof(EffectId)); + memcpy(buffer + sizeof(EffectId), &cfg, sizeof(TEffectConfig)); + xQueueSend(queue_, (void *)buffer, (TickType_t)10); +} diff --git a/espmusicmouse/src/main.cpp b/espmusicmouse/src/main.cpp index 9161a57..988a401 100644 --- a/espmusicmouse/src/main.cpp +++ b/espmusicmouse/src/main.cpp @@ -9,6 +9,9 @@ #include "containers/LedStripRGBW.h" #include "drivers/Esp32DriverRGBW.h" #include "effects/Circular.h" +#include "effects/Static.h" + +#include "TaskLed.h" MFRC522 rfid; // Instance of the class @@ -18,14 +21,7 @@ MFRC522::MIFARE_Key key; LedStripRGBW<51> ledStrip; Esp32DriverRGBW ledDriver; -EffectCircular effectFox(EffectCircularConfig{60.0f, 180.0f, ColorRGBW{15, 230, 230, 0} * 0.2f}, - ledStrip); - -EffectCircular effectOwl(EffectCircularConfig{360.0f, 180.0f, ColorRGBW{0, 0, 0, 150} * 0.2f}, - ledStrip); - -bool owl = false; -bool fox = false; +LedTask ledTask; void tag_handler(uint8_t *sn) { @@ -38,27 +34,18 @@ void tag_handler(uint8_t *sn) if (sn[4] == 0x30) { Serial.println("Fuchs"); - fox = true; - owl = false; - //////////////////////////////////////////////////////////////////////////////////// - //led.setRange(0, 50, 0, 0, 243, 0); - //led.setRange(led.numLeds() - 40, led.numLeds(), 0, 0, 243, 0); + ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}}); } if (sn[4] == 0xf0) { Serial.println("Eule"); - owl = true; - fox = false; - //led.setRange(0, 50, 0, 0, 0, 254); - //led.setRange(led.numLeds() - 40, led.numLeds(), 0, 0, 0, 254); + ledTask.startEffect(EffectCircularConfig{180, 180, ColorRGBW{0, 0, 0, 128}}); } } else { - //led.clear(); - owl = false; - fox = false; Serial.println("Nichts"); + ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}}); } //led.transmit(); } @@ -101,6 +88,8 @@ void setup() //pinMode(25, INPUT_PULLUP); //pinMode(14, INPUT_PULLUP); //pinMode(13, INPUT_PULLUP); + ledTask.begin(ledStrip, ledDriver); + ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}}); } bool btn2state = true; @@ -127,7 +116,9 @@ void loop() delay(3000); } */ - int delayVal; + +#if 0 + int delayVal = 100; if (owl) delayVal = effectOwl(); else if (fox) @@ -137,6 +128,8 @@ void loop() ledDriver.writeSync(ledStrip.rawData(), ledStrip.numLeds()); delay(delayVal); +#endif + /* auto delayMs = animation(led); led.transmit();