mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-10-28 11:14:58 +00:00
replacement for rgbLedWrite() to support new v6 of the WS2812B driver
This commit is contained in:
@@ -148,7 +148,5 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) {
|
|||||||
void ledcAttachPin(uint8_t pin, uint8_t chan) {};
|
void ledcAttachPin(uint8_t pin, uint8_t chan) {};
|
||||||
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {};
|
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {};
|
||||||
void ledcWrite(uint8_t chan, uint32_t duty) {};
|
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
|
#endif
|
||||||
@@ -83,8 +83,6 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
|
|||||||
void ledcAttachPin(uint8_t pin, uint8_t chan);
|
void ledcAttachPin(uint8_t pin, uint8_t chan);
|
||||||
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num);
|
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num);
|
||||||
void ledcWrite(uint8_t chan, uint32_t duty);
|
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) {
|
inline int rtc_get_reset_reason(int cpu_no) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
uint8_t r = v / 10000;
|
uint8_t r = v / 10000;
|
||||||
uint8_t g = (v - r * 10000) / 100;
|
uint8_t g = (v - r * 10000) / 100;
|
||||||
uint8_t b = v % 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);
|
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
||||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||||
LOG_DEBUG("Digital Write on GPIO %02d", sensor.gpio());
|
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.sum_ = (sensor.sum_ * 15 + a * 16) / 16;
|
||||||
sensor.analog_ = sensor.sum_ / 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)) {
|
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_ / (3300 - sensor.analog_) * (Rt / R0)) / Beta)
|
sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (NTC_VREF_MV - sensor.analog_) * (Rt / R0)) / Beta)
|
||||||
- T0); // Temperature in Celsius
|
- T0); // Temperature in Celsius
|
||||||
sensor.last_reading_ = sensor.analog_;
|
sensor.last_reading_ = sensor.analog_;
|
||||||
sensorreads_++;
|
sensorreads_++;
|
||||||
@@ -424,7 +424,7 @@ void AnalogSensor::measure() {
|
|||||||
edgecnt[index] = 0;
|
edgecnt[index] = 0;
|
||||||
portEXIT_CRITICAL_ISR(&mux);
|
portEXIT_CRITICAL_ISR(&mux);
|
||||||
sensor.set_value(sensor.factor() * 1000000.0 / t);
|
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);
|
sensor.set_value(0);
|
||||||
}
|
}
|
||||||
if (sensor.value() != oldval) {
|
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 r = v / 10000;
|
||||||
uint8_t g = (v - r * 10000) / 100;
|
uint8_t g = (v - r * 10000) / 100;
|
||||||
uint8_t b = v % 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);
|
LOG_DEBUG("RGB set to %d, %d, %d", r, g, b);
|
||||||
} else if (sensor.type() == AnalogType::PULSE) {
|
} else if (sensor.type() == AnalogType::PULSE) {
|
||||||
uint8_t v = val;
|
uint8_t v = val;
|
||||||
|
|||||||
+110
-1
@@ -19,8 +19,117 @@
|
|||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
#include <esp32-hal-rmt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
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};
|
uuid::log::Logger LED::logger_{F_(led), uuid::log::Facility::KERN};
|
||||||
|
|
||||||
// initialise the LED, fetching the settings from the WebSettingsService
|
// 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];
|
const uint8_t blue = rgb_table[idx][2];
|
||||||
|
|
||||||
if (led_type_) {
|
if (led_type_) {
|
||||||
rgbLedWrite(led_gpio_, red, green, blue);
|
rgb_led::write(led_gpio_, red, green, blue);
|
||||||
} else {
|
} else {
|
||||||
digitalWrite(led_gpio_, (red == 0 && green == 0 && blue == 0) || color == Color::OFF ? !LED_ON : LED_ON);
|
digitalWrite(led_gpio_, (red == 0 && green == 0 && blue == 0) || color == Color::OFF ? !LED_ON : LED_ON);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,30 @@
|
|||||||
|
|
||||||
namespace emsesp {
|
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 {
|
class LED {
|
||||||
public:
|
public:
|
||||||
enum Color : uint8_t {
|
enum Color : uint8_t {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.13"
|
#define EMSESP_APP_VERSION "3.9.0-dev.14"
|
||||||
|
|||||||
Reference in New Issue
Block a user