From eddc443d72e8d8c94674e629f12745fb08af4da1 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 24 Sep 2026 11:29:14 +0200 Subject: [PATCH] replacement for rgbLedWrite() to support new v6 of the WS2812B driver --- lib_standalone/Arduino.cpp | 2 - lib_standalone/Arduino.h | 2 - src/core/analogsensor.cpp | 10 ++-- src/core/led.cpp | 111 ++++++++++++++++++++++++++++++++++++- src/core/led.h | 24 ++++++++ src/emsesp_version.h | 2 +- 6 files changed, 140 insertions(+), 11 deletions(-) diff --git a/lib_standalone/Arduino.cpp b/lib_standalone/Arduino.cpp index 3932cce76..1faf07175 100644 --- a/lib_standalone/Arduino.cpp +++ b/lib_standalone/Arduino.cpp @@ -148,7 +148,5 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) { void ledcAttachPin(uint8_t pin, uint8_t chan) {}; void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {}; void ledcWrite(uint8_t chan, uint32_t duty) {}; -void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {}; -void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {}; #endif \ No newline at end of file diff --git a/lib_standalone/Arduino.h b/lib_standalone/Arduino.h index e1f19c0fe..e01080ee8 100644 --- a/lib_standalone/Arduino.h +++ b/lib_standalone/Arduino.h @@ -83,8 +83,6 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num); void ledcAttachPin(uint8_t pin, uint8_t chan); void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num); void ledcWrite(uint8_t chan, uint32_t duty); -void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); -void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); inline int rtc_get_reset_reason(int cpu_no) { return 0; diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp index ce7537140..c38286615 100644 --- a/src/core/analogsensor.cpp +++ b/src/core/analogsensor.cpp @@ -307,7 +307,7 @@ void AnalogSensor::reload(bool get_nvs) { uint8_t r = v / 10000; uint8_t g = (v - r * 10000) / 100; uint8_t b = v % 100; - rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b); + rgb_led::write(sensor.gpio(), 2 * r, 2 * g, 2 * b); LOG_DEBUG("RGB set to %d, %d, %d", r, g, b); } else if (sensor.type() == AnalogType::DIGITAL_OUT) { LOG_DEBUG("Digital Write on GPIO %02d", sensor.gpio()); @@ -405,8 +405,8 @@ void AnalogSensor::measure() { sensor.sum_ = (sensor.sum_ * 15 + a * 16) / 16; sensor.analog_ = sensor.sum_ / 16; } - if (sensor.analog_ > 0 && sensor.analog_ < 3300 && (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1)) { - sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (3300 - sensor.analog_) * (Rt / R0)) / Beta) + if (sensor.analog_ > 0 && sensor.analog_ < NTC_VREF_MV && (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1)) { + sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (NTC_VREF_MV - sensor.analog_) * (Rt / R0)) / Beta) - T0); // Temperature in Celsius sensor.last_reading_ = sensor.analog_; sensorreads_++; @@ -424,7 +424,7 @@ void AnalogSensor::measure() { edgecnt[index] = 0; portEXIT_CRITICAL_ISR(&mux); sensor.set_value(sensor.factor() * 1000000.0 / t); - } else if (micros() - edge[index] > 10000000ul && sensor.value() > 0) { + } else if (micros() - edge[index] > FREQ_ZERO_TIMEOUT_US && sensor.value() > 0) { sensor.set_value(0); } if (sensor.value() != oldval) { @@ -999,7 +999,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) { uint8_t r = v / 10000; uint8_t g = (v - r * 10000) / 100; uint8_t b = v % 100; - rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b); + rgb_led::write(sensor.gpio(), 2 * r, 2 * g, 2 * b); LOG_DEBUG("RGB set to %d, %d, %d", r, g, b); } else if (sensor.type() == AnalogType::PULSE) { uint8_t v = val; diff --git a/src/core/led.cpp b/src/core/led.cpp index 958ac9ac3..f6c708e8c 100644 --- a/src/core/led.cpp +++ b/src/core/led.cpp @@ -19,8 +19,117 @@ #include "led.h" #include "emsesp.h" +#ifndef EMSESP_STANDALONE +#include +#endif + namespace emsesp { +namespace rgb_led { + +#ifndef EMSESP_STANDALONE + +namespace { + +// durations in 100 ns ticks +constexpr uint16_t T0H = 3; // 300 ns +constexpr uint16_t T0L = 9; // 900 ns +constexpr uint16_t T1H = 8; // 800 ns +constexpr uint16_t T1L = 6; // 600 ns + +constexpr uint32_t RMT_FREQ_HZ = 10000000; // 10 MHz +constexpr uint32_t RESET_US = 300; +constexpr uint8_t NO_PIN = 0xFF; +constexpr uint8_t MAX_PINS = 2; // status LED + an RGB analog sensor; the ESP32-C3 only has 2 RMT TX channels + +struct Channel { + uint8_t pin = NO_PIN; + uint32_t last_tx_us = 0; +}; + +Channel channels[MAX_PINS]; + +Channel * init(uint8_t pin) { + Channel * free_slot = nullptr; + for (auto & ch : channels) { + if (ch.pin == pin) { + return &ch; + } + if (!free_slot && ch.pin == NO_PIN) { + free_slot = &ch; + } + } + + if (!free_slot) { + // all slots in use: release the oldest one + free_slot = &channels[0]; + end(free_slot->pin); + } + + if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ_HZ)) { + return nullptr; + } + free_slot->pin = pin; + free_slot->last_tx_us = micros() - RESET_US; // no wait on first write + return free_slot; +} + +} // namespace + +bool write(uint8_t pin, uint8_t red, uint8_t green, uint8_t blue, Order order) { + Channel * ch = init(pin); + if (!ch) { + return false; + } + + // enforce the latch/reset gap since the previous frame (V6 needs > 280 us) + const uint32_t elapsed = micros() - ch->last_tx_us; + if (elapsed < RESET_US) { + delayMicroseconds(RESET_US - elapsed); + } + + const uint8_t bytes[3] = {order == Order::GRB ? green : red, order == Order::GRB ? red : green, blue}; + + rmt_data_t frame[24]; + for (uint8_t i = 0; i < 24; i++) { + const bool one = bytes[i >> 3] & (0x80 >> (i & 7)); + frame[i].level0 = 1; + frame[i].duration0 = one ? T1H : T0H; + frame[i].level1 = 0; + frame[i].duration1 = one ? T1L : T0L; + } + + const bool ok = rmtWrite(pin, frame, 24, RMT_WAIT_FOR_EVER); + ch->last_tx_us = micros(); + return ok; +} + +void end(uint8_t pin) { + for (auto & ch : channels) { + if (ch.pin == pin && pin != NO_PIN) { + rmtDeinit(pin); + ch.pin = NO_PIN; + } + } +} + +#else + +bool write(uint8_t, uint8_t, uint8_t, uint8_t, Order) { + return true; +} + +void end(uint8_t) { +} + +#endif + +bool off(uint8_t pin) { + return write(pin, 0, 0, 0); +} + +} // namespace rgb_led + uuid::log::Logger LED::logger_{F_(led), uuid::log::Facility::KERN}; // initialise the LED, fetching the settings from the WebSettingsService @@ -189,7 +298,7 @@ void LED::set_led(Color color) { const uint8_t blue = rgb_table[idx][2]; if (led_type_) { - rgbLedWrite(led_gpio_, red, green, blue); + rgb_led::write(led_gpio_, red, green, blue); } else { digitalWrite(led_gpio_, (red == 0 && green == 0 && blue == 0) || color == Color::OFF ? !LED_ON : LED_ON); } diff --git a/src/core/led.h b/src/core/led.h index 3c4502d26..f12f8bba2 100644 --- a/src/core/led.h +++ b/src/core/led.h @@ -24,6 +24,30 @@ namespace emsesp { +// WS2812B driver replacing arduino-esp32's rgbLedWrite()/neopixelWrite(), which hard-code +// T0H = 400 ns, T1L = 400 ns and have no reset gap. The WS2812B-2020-V6 needs T0H 220-380 ns, +// T1L 580-1000 ns and a reset > 280 us, otherwise it misreads 0-bits as 1-bits (washed-out white). +// Timings used (10 MHz RMT clock, 1 tick = 100 ns) fit both the original WS2812B and the V6: +// +// sent old WS2812B WS2812B-2020-V6 +// T0H 300 ns 250-550 ns 220-380 ns +// T0L 900 ns 700-1000 ns 580-1000 ns +// T1H 800 ns 650-950 ns 580-1000 ns +// T1L 600 ns 300-600 ns 580-1000 ns +// Reset >300 us >50 us >280 us +// +// Do not mix with rgbLedWrite()/neopixelWrite() on the same pin, as both claim the pin's RMT channel. +namespace rgb_led { + +enum class Order : uint8_t { GRB, RGB }; // WS2812B is GRB + +// Blocks ~30 us for the transmit, plus up to the reset gap if called again right after the previous update +bool write(uint8_t pin, uint8_t red, uint8_t green, uint8_t blue, Order order = Order::GRB); +bool off(uint8_t pin); +void end(uint8_t pin); // release the pin's RMT channel, e.g. before reusing the pin for something else + +} // namespace rgb_led + class LED { public: enum Color : uint8_t { diff --git a/src/emsesp_version.h b/src/emsesp_version.h index 7d790a329..dd02903f5 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.9.0-dev.13" +#define EMSESP_APP_VERSION "3.9.0-dev.14"