Refactor HardwareLed class to use FastLED library and template for GPIO pin specification

This commit is contained in:
2025-10-04 12:03:59 +02:00
parent c65050cd44
commit c8b7882c2d
4 changed files with 89 additions and 47 deletions
+69 -37
View File
@@ -1,5 +1,6 @@
#include "hardware_led.hpp" #include "hardware_led.hpp"
#include <Arduino.h> #include <Arduino.h>
#include <math.h>
#define DEBUG_LED #define DEBUG_LED
#ifdef DEBUG_LED #ifdef DEBUG_LED
@@ -8,8 +9,10 @@
#define LOG(msg) #define LOG(msg)
#endif #endif
HardwareLed::HardwareLed(uint8_t pin, uint8_t numPixels) template<uint8_t PIN>
: m_strip(numPixels > 0 ? numPixels : 1, pin, NEO_GRB + NEO_KHZ800), HardwareLed<PIN>::HardwareLed(uint8_t numPixels)
: m_leds(new CRGB[numPixels > 0 ? numPixels : 1]),
m_numPixels(numPixels > 0 ? numPixels : 1),
m_currentConfig(hardware_LedConfig_init_default), m_currentConfig(hardware_LedConfig_init_default),
m_isActive(false), m_isActive(false),
m_startTime(0), m_startTime(0),
@@ -20,20 +23,31 @@ HardwareLed::HardwareLed(uint8_t pin, uint8_t numPixels)
m_fadeCurrentColor(0), m_fadeCurrentColor(0),
m_fadeTargetColor(0), m_fadeTargetColor(0),
m_fadeProgress(0.0f), m_fadeProgress(0.0f),
m_fadeStartTime(0) {} m_fadeStartTime(0),
m_callback(nullptr) {}
void HardwareLed::begin() { template<uint8_t PIN>
m_strip.begin(); HardwareLed<PIN>::~HardwareLed() {
m_strip.show(); delete[] m_leds;
}
template<uint8_t PIN>
void HardwareLed<PIN>::begin() {
FastLED.addLeds<WS2812B, PIN, GRB>(m_leds, m_numPixels).setCorrection(TypicalLEDStrip);
FastLED.setMaxPowerInVoltsAndMilliamps(5, m_numPixels * 60); // Limit power
FastLED.clear();
FastLED.show();
randomSeed(analogRead(0)); randomSeed(analogRead(0));
} }
void HardwareLed::end() { template<uint8_t PIN>
m_strip.clear(); void HardwareLed<PIN>::end() {
m_strip.show(); FastLED.clear();
FastLED.show();
} }
void HardwareLed::update() { template<uint8_t PIN>
void HardwareLed<PIN>::update() {
static unsigned long lastUpdate = 0; static unsigned long lastUpdate = 0;
unsigned long now = millis(); unsigned long now = millis();
if (now - lastUpdate < 10) return; // Limit to ~100 FPS if (now - lastUpdate < 10) return; // Limit to ~100 FPS
@@ -44,8 +58,8 @@ void HardwareLed::update() {
// Safe against millis() overflow due to unsigned arithmetic // Safe against millis() overflow due to unsigned arithmetic
if (m_currentConfig.duration_ms > 0 && (now - m_startTime) >= m_currentConfig.duration_ms) { if (m_currentConfig.duration_ms > 0 && (now - m_startTime) >= m_currentConfig.duration_ms) {
m_isActive = false; m_isActive = false;
m_strip.clear(); FastLED.clear();
m_strip.show(); FastLED.show();
if (m_callback) m_callback(); if (m_callback) m_callback();
return; return;
} }
@@ -69,7 +83,8 @@ void HardwareLed::update() {
} }
} }
void HardwareLed::set(const hardware_LedConfig& config) { template<uint8_t PIN>
void HardwareLed<PIN>::set(const hardware_LedConfig& config) {
if (config.brightness > 255) { if (config.brightness > 255) {
LOG("Error: Brightness exceeds 255"); LOG("Error: Brightness exceeds 255");
return; return;
@@ -79,8 +94,8 @@ void HardwareLed::set(const hardware_LedConfig& config) {
config.animation_params.fade_params.colors_count > 5)) { config.animation_params.fade_params.colors_count > 5)) {
LOG("Error: Invalid colors_count in FadeParams"); LOG("Error: Invalid colors_count in FadeParams");
m_isActive = false; m_isActive = false;
m_strip.clear(); FastLED.clear();
m_strip.show(); FastLED.show();
return; return;
} }
if (config.which_animation_params == hardware_LedConfig_pulse_params_tag && if (config.which_animation_params == hardware_LedConfig_pulse_params_tag &&
@@ -105,20 +120,24 @@ void HardwareLed::set(const hardware_LedConfig& config) {
update(); update();
} }
void HardwareLed::applyStatic(const hardware_StaticParams& params) { template<uint8_t PIN>
void HardwareLed<PIN>::applyStatic(const hardware_StaticParams& params) {
setColor(params.color, m_currentConfig.brightness); setColor(params.color, m_currentConfig.brightness);
} }
void HardwareLed::applyPulse(const hardware_PulseParams& params) { template<uint8_t PIN>
void HardwareLed<PIN>::applyPulse(const hardware_PulseParams& params) {
unsigned long now = millis(); unsigned long now = millis();
float speed_ms = max(params.speed_ms, 1U); // Prevent division by zero float speed_ms = max(params.speed_ms, 1U); // Prevent division by zero
float phase = fmod((now - m_startTime) / (float)speed_ms, 1.0f); float phase = fmod((now - m_startTime) / (float)speed_ms, 1.0f);
float brightnessFactor = 0.5f + 0.5f * sin(2 * PI * phase); float brightnessFactor = 0.5f + 0.5f * sin(2 * PI * phase);
uint8_t pulseBrightness = m_currentConfig.brightness * brightnessFactor; uint16_t pulseBrightness = roundf(m_currentConfig.brightness * brightnessFactor);
setColor(params.color, pulseBrightness); if (pulseBrightness > 255) pulseBrightness = 255;
setColor(params.color, (uint8_t)pulseBrightness);
} }
void HardwareLed::applyFade(const hardware_FadeParams& params) { template<uint8_t PIN>
void HardwareLed<PIN>::applyFade(const hardware_FadeParams& params) {
if (params.colors_count == 0 || params.colors_count > 5) { if (params.colors_count == 0 || params.colors_count > 5) {
LOG("Error: Invalid colors_count in FadeParams"); LOG("Error: Invalid colors_count in FadeParams");
return; return;
@@ -138,46 +157,59 @@ void HardwareLed::applyFade(const hardware_FadeParams& params) {
setColor(interpolatedColor, m_currentConfig.brightness); setColor(interpolatedColor, m_currentConfig.brightness);
} }
void HardwareLed::applyFlicker(const hardware_FlickerParams& params) { template<uint8_t PIN>
void HardwareLed<PIN>::applyFlicker(const hardware_FlickerParams& params) {
uint32_t intensity = min(params.intensity, 100U); // Clamp to 0-100 uint32_t intensity = min(params.intensity, 100U); // Clamp to 0-100
uint8_t threshold = map(intensity, 0, 100, 0, 255); uint8_t threshold = map(intensity, 0, 100, 0, 255);
bool showPixel = random(255) < threshold; bool showPixel = random(255) < threshold;
if (showPixel != (m_strip.getPixelColor(0) != 0)) { if (showPixel != (m_leds[0] != CRGB(0, 0, 0))) {
if (showPixel) { if (showPixel) {
setColor(params.color, m_currentConfig.brightness); setColor(params.color, m_currentConfig.brightness);
} else { } else {
m_strip.clear(); FastLED.clear();
m_strip.show(); FastLED.show();
} }
} }
} }
void HardwareLed::setColor(uint32_t color, uint8_t brightness) { template<uint8_t PIN>
void HardwareLed<PIN>::setColor(uint32_t color, uint8_t brightness) {
uint8_t r = (color >> 16) & 0xFF; uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF; uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF; uint8_t b = color & 0xFF;
r = (r * brightness) / 255; uint16_t r_scaled = ((uint16_t)r * brightness) / 255;
g = (g * brightness) / 255; uint16_t g_scaled = ((uint16_t)g * brightness) / 255;
b = (b * brightness) / 255; uint16_t b_scaled = ((uint16_t)b * brightness) / 255;
for (uint16_t i = 0; i < m_strip.numPixels(); i++) { r = (r_scaled > 255) ? 255 : (uint8_t)r_scaled;
m_strip.setPixelColor(i, r, g, b); g = (g_scaled > 255) ? 255 : (uint8_t)g_scaled;
b = (b_scaled > 255) ? 255 : (uint8_t)b_scaled;
for (uint16_t i = 0; i < m_numPixels; i++) {
m_leds[i] = CRGB(r, g, b);
} }
m_strip.show(); FastLED.show();
} }
uint32_t HardwareLed::lerpColor(uint32_t color1, uint32_t color2, float t) const { template<uint8_t PIN>
uint32_t HardwareLed<PIN>::lerpColor(uint32_t color1, uint32_t color2, float t) const {
uint8_t r1 = (color1 >> 16) & 0xFF; uint8_t r1 = (color1 >> 16) & 0xFF;
uint8_t g1 = (color1 >> 8) & 0xFF; uint8_t g1 = (color1 >> 8) & 0xFF;
uint8_t b1 = color1 & 0xFF; uint8_t b1 = color1 & 0xFF;
uint8_t r2 = (color2 >> 16) & 0xFF; uint8_t r2 = (color2 >> 16) & 0xFF;
uint8_t g2 = (color2 >> 8) & 0xFF; uint8_t g2 = (color2 >> 8) & 0xFF;
uint8_t b2 = color2 & 0xFF; uint8_t b2 = color2 & 0xFF;
uint8_t r = r1 + (r2 - r1) * t; float r_f = r1 + (r2 - r1) * t;
uint8_t g = g1 + (g2 - g1) * t; float g_f = g1 + (g2 - g1) * t;
uint8_t b = b1 + (b2 - b1) * t; float b_f = b1 + (b2 - b1) * t;
uint8_t r = (uint8_t)roundf(r_f);
uint8_t g = (uint8_t)roundf(g_f);
uint8_t b = (uint8_t)roundf(b_f);
return (r << 16) | (g << 8) | b; return (r << 16) | (g << 8) | b;
} }
float HardwareLed::estimateCurrent_mA() const { template<uint8_t PIN>
return m_strip.numPixels() * (m_currentConfig.brightness / 255.0f) * 60.0f; float HardwareLed<PIN>::estimateCurrent_mA() const {
return m_numPixels * (m_currentConfig.brightness / 255.0f) * 60.0f;
} }
// Explicit instantiation for pin D2 (GPIO 2)
template class HardwareLed<2>;
+17 -7
View File
@@ -1,21 +1,30 @@
#pragma once #pragma once
#include "hardware.pb.h" #include "hardware.pb.h"
#include <Adafruit_NeoPixel.h> #include <FastLED.h>
/** /**
* @class HardwareLed * @class HardwareLed
* @brief Manages an LED strip on ESP8266 using Adafruit NeoPixel library. * @brief Manages an LED strip on ESP8266 using FastLED library.
* Supports animations defined by hardware_LedConfig (static, pulse, fade, flicker). * Supports animations defined by hardware_LedConfig (static, pulse, fade, flicker).
* Template parameter PIN specifies the GPIO pin at compile time.
*
* @tparam PIN The GPIO pin connected to the LED strip (e.g., 2 for D2 on ESP8266).
*/ */
template<uint8_t PIN>
class HardwareLed { class HardwareLed {
public: public:
using AnimationCallback = void (*)(); using AnimationCallback = void (*)();
HardwareLed(uint8_t pin, uint8_t numPixels = 1); /**
* @brief Constructor for HardwareLed.
* @param numPixels Number of pixels in the strip (default: 1).
*/
HardwareLed(uint8_t numPixels = 1);
~HardwareLed();
void begin(); void begin();
void end(); void end();
void update(); // Removed const void update();
void set(const hardware_LedConfig& config); void set(const hardware_LedConfig& config);
void setAnimationCallback(AnimationCallback cb) { m_callback = cb; } void setAnimationCallback(AnimationCallback cb) { m_callback = cb; }
float estimateCurrent_mA() const; float estimateCurrent_mA() const;
@@ -23,13 +32,15 @@ public:
private: private:
void applyStatic(const hardware_StaticParams& params); void applyStatic(const hardware_StaticParams& params);
void applyPulse(const hardware_PulseParams& params); void applyPulse(const hardware_PulseParams& params);
void applyFade(const hardware_FadeParams& params); // Removed const void applyFade(const hardware_FadeParams& params);
void applyFlicker(const hardware_FlickerParams& params); void applyFlicker(const hardware_FlickerParams& params);
void setColor(uint32_t color, uint8_t brightness = 255); void setColor(uint32_t color, uint8_t brightness = 255);
uint32_t lerpColor(uint32_t color1, uint32_t color2, float t) const; uint32_t lerpColor(uint32_t color1, uint32_t color2, float t) const;
Adafruit_NeoPixel m_strip; CRGB* m_leds; // Dynamic array for pixel data
uint8_t m_numPixels;
hardware_LedConfig m_currentConfig = hardware_LedConfig_init_default; hardware_LedConfig m_currentConfig = hardware_LedConfig_init_default;
bool m_isActive;
unsigned long m_startTime; unsigned long m_startTime;
uint8_t m_pulseState; uint8_t m_pulseState;
unsigned long m_lastPulseTime; unsigned long m_lastPulseTime;
@@ -39,6 +50,5 @@ private:
uint32_t m_fadeTargetColor; uint32_t m_fadeTargetColor;
float m_fadeProgress; float m_fadeProgress;
unsigned long m_fadeStartTime; unsigned long m_fadeStartTime;
bool m_isActive;
AnimationCallback m_callback = nullptr; AnimationCallback m_callback = nullptr;
}; };
+1 -1
View File
@@ -17,7 +17,7 @@ monitor_speed = 9600
board_build.filesystem = littlefs board_build.filesystem = littlefs
lib_deps = lib_deps =
miguelbalboa/MFRC522@^1.4.12 miguelbalboa/MFRC522@^1.4.12
adafruit/Adafruit NeoPixel@^1.15.1 ;adafruit/Adafruit NeoPixel@^1.15.1
fastled/FastLED@^3.10.3 fastled/FastLED@^3.10.3
nanopb/Nanopb@^0.4.91 nanopb/Nanopb@^0.4.91
custom_nanopb_protos = custom_nanopb_protos =
+1 -1
View File
@@ -4,7 +4,7 @@
#include "hardware.pb.h" #include "hardware.pb.h"
// Demo for HardwareLed // Demo for HardwareLed
HardwareLed led(2); // Assuming NeoPixel on pin 4 (D2 on ESP8266) HardwareLed<2> led; // Assuming NeoPixel on pin 2 (D2 on ESP8266)
// Demo for HardwareRfid // Demo for HardwareRfid
HardwareRfid rfid(4, 5); // SS=D2 (GPIO4), RST=D1 (GPIO5) HardwareRfid rfid(4, 5); // SS=D2 (GPIO4), RST=D1 (GPIO5)