Separate FreeRTOS task for LED

This commit is contained in:
Martin Bauer 2021-11-17 23:18:10 +01:00
parent c68114dc4c
commit 5a41c03e8e
8 changed files with 302 additions and 91 deletions

View File

@ -1,11 +1,74 @@
Connections
-----------
- SDA purple 23 Reader
- SCK green 22 ----------
- MOSI orange 18
- MISO yellow 19
- IRQ grey
- GND black - GND black
- RST blue 5 - RST blue 3.3V
- 3.3V red - 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

View File

@ -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 <typename TLedStrip>
class AbstractEffect
{
public:
virtual int operator()(TLedStrip &s) = 0;
};
template <typename TLedStrip>
class EffectCircle : public AbstractEffect<TLedStrip>
{
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 <typename TLedStrip>
AbstractEffect<TLedStrip> *makeEffect(const char *buffer)
{
const EffectID &effectId = *reinterpret_cast<const EffectID *>(buffer);
if (effectId == EffectID::CIRCLE)
{
auto cfg = reinterpret_cast<const EffectCircularConfig *>(buffer + sizeof(EffectID));
return new (effectStorage) EffectCircle<TLedStrip>(*cfg);
}
// read effect id code from buffer
// read config from buffer
}

View File

@ -75,6 +75,13 @@ void setLedRGBW(LedStripRGBW<TNumLeds> &s, int idx, const ColorRGBW &c)
s.set(idx, c.r, c.g, c.b, c.w); s.set(idx, c.r, c.g, c.b, c.w);
} }
template <int TNumLeds>
void setLedRGBW(LedStripRGBW<TNumLeds> &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 <int TNumLeds> template <int TNumLeds>
void clear(LedStripRGBW<TNumLeds> &s) void clear(LedStripRGBW<TNumLeds> &s)
{ {

View File

@ -17,6 +17,7 @@ class EffectCircular
public: public:
static constexpr auto NUM_LEDS = numLeds<TLedStrip>(); static constexpr auto NUM_LEDS = numLeds<TLedStrip>();
static constexpr int DELAY_MS = 10; static constexpr int DELAY_MS = 10;
using ConfigType = EffectCircularConfig;
EffectCircular(const EffectCircularConfig &cfg, TLedStrip &ledStrip) EffectCircular(const EffectCircularConfig &cfg, TLedStrip &ledStrip)
: config_(cfg), : config_(cfg),
@ -72,3 +73,22 @@ private:
int widthInLeds_; int widthInLeds_;
float invWidth_; float invWidth_;
}; };
// Traits
template <>
struct EffectIdToConfig<EffectId::CIRCULAR>
{
using type = EffectCircularConfig;
};
template <>
struct EffectConfigToId<EffectCircularConfig>
{
static constexpr auto id = EffectId::CIRCULAR;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::CIRCULAR, TLedStrip>
{
using type = EffectCircular<TLedStrip>;
};

View File

@ -1,7 +1,7 @@
#pragma once
enum class EffectID enum class EffectId
{ {
OFF,
STATIC, STATIC,
CIRCULAR, CIRCULAR,
CIRCLE_WAVE, CIRCLE_WAVE,
@ -9,9 +9,17 @@ enum class EffectID
RAINBOW_FADE, RAINBOW_FADE,
}; };
template <typename TLedStrip> template <EffectId id>
class AbstractEffect struct EffectIdToConfig
{
};
template <typename EffectConfig>
struct EffectConfigToId
{
};
template <EffectId id, typename TLedStrip>
struct EffectIdToClass
{ {
public:
virtual int operator()(TLedStrip &s) = 0;
}; };

View File

@ -0,0 +1,51 @@
#pragma once
#include "effects/Common.h"
#include "helpers/ColorRGBW.h"
struct EffectStaticConfig
{
ColorRGBW color;
};
template <typename TLedStrip>
class EffectStatic
{
public:
static constexpr auto NUM_LEDS = numLeds<TLedStrip>();
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<EffectId::STATIC>
{
using type = EffectStaticConfig;
};
template <>
struct EffectConfigToId<EffectStaticConfig>
{
static constexpr auto id = EffectId::STATIC;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::STATIC, TLedStrip>
{
using type = EffectStatic<TLedStrip>;
};

124
espmusicmouse/src/TaskLed.h Normal file
View File

@ -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 <functional>
#include <cstring>
static constexpr int MAX_EFFECT_CONFIG_SIZE = 128;
static constexpr int MAX_EFFECT_CLASS_SIZE = 128;
template <typename TLedStrip>
class LedTask
{
public:
void begin(TLedStrip &strip, Esp32DriverRGBW &driver);
template <typename TEffectConfig>
void startEffect(const TEffectConfig &cfg);
private:
template <typename T>
friend void _led_task_func(void *);
QueueHandle_t queue_ = nullptr;
TLedStrip *ledStrip_ = nullptr;
Esp32DriverRGBW *driver_ = nullptr;
};
// -----------------------------------------------------------------------------------------------
template <EffectId staticEffectId, typename TLedStrip>
bool dispatchEffectId(EffectId dynamicEffectId, std::function<int()> &effectFunction, TLedStrip &ledStrip,
unsigned char *msgBuffer, unsigned char *effectStorage)
{
if (staticEffectId == dynamicEffectId)
{
typename EffectIdToConfig<staticEffectId>::type cfg;
memcpy(&cfg, msgBuffer + sizeof(EffectId), sizeof(decltype(cfg)));
using TEffect = typename EffectIdToClass<staticEffectId, TLedStrip>::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 <typename TLedStrip>
void _led_task_func(void *params)
{
LedTask<TLedStrip> *task = reinterpret_cast<LedTask<TLedStrip> *>(params);
unsigned char msgBuffer[MAX_EFFECT_CONFIG_SIZE];
unsigned char effectStorage[MAX_EFFECT_CLASS_SIZE];
std::function<int()> 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<EffectId::CIRCULAR>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");}
else if (dispatchEffectId<EffectId::STATIC >(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 <typename TLedStrip>
void LedTask<TLedStrip>::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<TLedStrip>, "led task", MAX_EFFECT_CLASS_SIZE + MAX_EFFECT_CONFIG_SIZE + 2048,
(void *)(this), 1, nullptr);
}
template <typename TLedStrip>
template <typename TEffectConfig>
void LedTask<TLedStrip>::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<TEffectConfig>::id;
memcpy(buffer, &id, sizeof(EffectId));
memcpy(buffer + sizeof(EffectId), &cfg, sizeof(TEffectConfig));
xQueueSend(queue_, (void *)buffer, (TickType_t)10);
}

View File

@ -9,6 +9,9 @@
#include "containers/LedStripRGBW.h" #include "containers/LedStripRGBW.h"
#include "drivers/Esp32DriverRGBW.h" #include "drivers/Esp32DriverRGBW.h"
#include "effects/Circular.h" #include "effects/Circular.h"
#include "effects/Static.h"
#include "TaskLed.h"
MFRC522 rfid; // Instance of the class MFRC522 rfid; // Instance of the class
@ -18,14 +21,7 @@ MFRC522::MIFARE_Key key;
LedStripRGBW<51> ledStrip; LedStripRGBW<51> ledStrip;
Esp32DriverRGBW ledDriver; Esp32DriverRGBW ledDriver;
EffectCircular<decltype(ledStrip)> effectFox(EffectCircularConfig{60.0f, 180.0f, ColorRGBW{15, 230, 230, 0} * 0.2f}, LedTask<decltype(ledStrip)> ledTask;
ledStrip);
EffectCircular<decltype(ledStrip)> effectOwl(EffectCircularConfig{360.0f, 180.0f, ColorRGBW{0, 0, 0, 150} * 0.2f},
ledStrip);
bool owl = false;
bool fox = false;
void tag_handler(uint8_t *sn) void tag_handler(uint8_t *sn)
{ {
@ -38,27 +34,18 @@ void tag_handler(uint8_t *sn)
if (sn[4] == 0x30) if (sn[4] == 0x30)
{ {
Serial.println("Fuchs"); Serial.println("Fuchs");
fox = true; ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}});
owl = false;
////////////////////////////////////////////////////////////////////////////////////
//led.setRange(0, 50, 0, 0, 243, 0);
//led.setRange(led.numLeds() - 40, led.numLeds(), 0, 0, 243, 0);
} }
if (sn[4] == 0xf0) if (sn[4] == 0xf0)
{ {
Serial.println("Eule"); Serial.println("Eule");
owl = true; ledTask.startEffect(EffectCircularConfig{180, 180, ColorRGBW{0, 0, 0, 128}});
fox = false;
//led.setRange(0, 50, 0, 0, 0, 254);
//led.setRange(led.numLeds() - 40, led.numLeds(), 0, 0, 0, 254);
} }
} }
else else
{ {
//led.clear();
owl = false;
fox = false;
Serial.println("Nichts"); Serial.println("Nichts");
ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}});
} }
//led.transmit(); //led.transmit();
} }
@ -101,6 +88,8 @@ void setup()
//pinMode(25, INPUT_PULLUP); //pinMode(25, INPUT_PULLUP);
//pinMode(14, INPUT_PULLUP); //pinMode(14, INPUT_PULLUP);
//pinMode(13, INPUT_PULLUP); //pinMode(13, INPUT_PULLUP);
ledTask.begin(ledStrip, ledDriver);
ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}});
} }
bool btn2state = true; bool btn2state = true;
@ -127,7 +116,9 @@ void loop()
delay(3000); delay(3000);
} }
*/ */
int delayVal;
#if 0
int delayVal = 100;
if (owl) if (owl)
delayVal = effectOwl(); delayVal = effectOwl();
else if (fox) else if (fox)
@ -137,6 +128,8 @@ void loop()
ledDriver.writeSync(ledStrip.rawData(), ledStrip.numLeds()); ledDriver.writeSync(ledStrip.rawData(), ledStrip.numLeds());
delay(delayVal); delay(delayVal);
#endif
/* /*
auto delayMs = animation(led); auto delayMs = animation(led);
led.transmit(); led.transmit();