From 34f2f016f9a12e3e8abdea92d3f57aba9b39e3f3 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Sun, 4 Oct 2026 00:29:26 +0200 Subject: [PATCH] Firmware: port to Arduino core 3 / ESP-IDF 5.5 and pin every dependency The unpinned platform had moved to Arduino core 3, which the firmware no longer built against. Pinned now: pioarduino's platform 55.03.312-1 (Arduino core 3.3.12, ESP-IDF 5.5.5) - PlatformIO's own espressif32 platform still ships core 2 - plus MFRC522 1.4.12, AceButton 1.10.1 and native 1.2.1. - LED strips: the legacy RMT driver is replaced by the new rmt_tx API with a bytes encoder, at the same 50 ns tick, bit timings and byte order. ESP-IDF 5 aborts at boot when both drivers are linked, and the driver now picks the channel itself, so Esp32DriverRGBW::begin() takes only the pin. - Button backlights: ledcAttach instead of ledcSetup/ledcAttachPin, and ledcWrite is addressed by pin rather than channel. - Rotary encoder: gpio_reset_pin instead of the removed gpio_pad_select_gpio. The touch pads stay on the legacy touch driver: on the plain ESP32 it only warns, its conflict check passes as long as the new driver is not linked (it is not), and moving would change the raw counts the thresholds were tuned against. src_filter is build_src_filter now. Not yet tried on the device. Co-Authored-By: Claude Opus 5.5 --- esp-firmware/lib/ledtl/Esp32DriverRGBW.cpp | 102 +++++++----------- .../lib/ledtl/drivers/Esp32DriverRGBW.h | 12 ++- .../lib/rotary_encoder/rotary_encoder.c | 4 +- esp-firmware/platformio.ini | 16 +-- esp-firmware/src/Messages.h | 6 +- esp-firmware/src/main.cpp | 16 ++- 6 files changed, 69 insertions(+), 87 deletions(-) diff --git a/esp-firmware/lib/ledtl/Esp32DriverRGBW.cpp b/esp-firmware/lib/ledtl/Esp32DriverRGBW.cpp index cb86513..4d9ce1c 100644 --- a/esp-firmware/lib/ledtl/Esp32DriverRGBW.cpp +++ b/esp-firmware/lib/ledtl/Esp32DriverRGBW.cpp @@ -2,93 +2,72 @@ #include "drivers/Esp32DriverRGBW.h" -#include -#include +#include +#include -// Timing constants -static constexpr uint16_t DIVIDER = 4; -static constexpr double RMT_DURATION_NS = 12.5; // Minimum time of a single RMT duration based on clock ns +// Timing constants, in ns +static constexpr uint32_t RESOLUTION_HZ = 20 * 1000 * 1000; // one RMT tick = 50 ns +static constexpr uint32_t TICK_NS = 1000 * 1000 * 1000 / RESOLUTION_HZ; static constexpr uint32_t T0H = 300; static constexpr uint32_t T0L = 900; static constexpr uint32_t T1H = 600; static constexpr uint32_t T1L = 600; -static constexpr uint32_t TRS = 80000; -static constexpr rmt_item32_t bit0Data = {uint32_t(T0H / (RMT_DURATION_NS * DIVIDER)), 1, uint32_t(T0L / (RMT_DURATION_NS * DIVIDER)), 0}; -static constexpr rmt_item32_t bit1Data = {uint32_t(T1H / (RMT_DURATION_NS * DIVIDER)), 1, uint32_t(T1L / (RMT_DURATION_NS * DIVIDER)), 0}; -// Function registered at the RMT driver that converts regular uint8_t values containing r,g,b,w values -// to rmt_item32_t for each bit. (8 bit -> 32 * 8 bit) -static void IRAM_ATTR uint8ToRmtAdaptor(const void *src, rmt_item32_t *dest, size_t srcSize, - size_t wantedNum, size_t *translatedSize, size_t *itemNum) +static rmt_symbol_word_t symbol(uint32_t highNs, uint32_t lowNs) { - if (src == NULL || dest == NULL) - { - *translatedSize = 0; - *itemNum = 0; - return; - } - - size_t size = 0; - size_t num = 0; - uint8_t *psrc = (uint8_t *)src; - rmt_item32_t *pdest = dest; - while (size < srcSize && num < wantedNum) - { - for (int i = 0; i < 8; i++) - { - // MSB first - const bool isBitSet = *psrc & (1 << (7 - i)); - pdest->val = isBitSet ? bit1Data.val : bit0Data.val; - num++; - pdest++; - } - size++; - psrc++; - } - *translatedSize = size; - *itemNum = num; + rmt_symbol_word_t s = {}; + s.duration0 = highNs / TICK_NS; + s.level0 = 1; + s.duration1 = lowNs / TICK_NS; + s.level1 = 0; + return s; } -void Esp32DriverRGBW::begin(int gpio, int rmtChannel) +void Esp32DriverRGBW::begin(int gpio) { - rmtChannel_ = rmtChannel; transmitting_ = false; - rmt_config_t rmt_tx; - rmt_tx.channel = static_cast(rmtChannel); - rmt_tx.gpio_num = static_cast(gpio); - rmt_tx.rmt_mode = RMT_MODE_TX; - rmt_tx.mem_block_num = 1; - rmt_tx.clk_div = DIVIDER; - rmt_tx.tx_config.loop_en = false; - rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - rmt_tx.tx_config.carrier_en = false; - rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - rmt_tx.tx_config.idle_output_en = true; + rmt_tx_channel_config_t channelConfig = {}; + channelConfig.gpio_num = static_cast(gpio); + channelConfig.clk_src = RMT_CLK_SRC_DEFAULT; + channelConfig.resolution_hz = RESOLUTION_HZ; + channelConfig.mem_block_symbols = 64; // one block, refilled from the interrupt + channelConfig.trans_queue_depth = 4; + ESP_ERROR_CHECK(rmt_new_tx_channel(&channelConfig, &channel_)); - ESP_ERROR_CHECK(rmt_config(&rmt_tx)); - ESP_ERROR_CHECK(rmt_driver_install(rmt_tx.channel, 0, 0)); + // Every byte goes out MSB first, one RMT symbol per bit - the strip's own byte order + // (g, r, b, w) is already how LedStripRGBW lays the data out in memory. + rmt_bytes_encoder_config_t encoderConfig = {}; + encoderConfig.bit0 = symbol(T0H, T0L); + encoderConfig.bit1 = symbol(T1H, T1L); + encoderConfig.flags.msb_first = 1; + ESP_ERROR_CHECK(rmt_new_bytes_encoder(&encoderConfig, &encoder_)); - rmt_translator_init((rmt_channel_t)rmtChannel, uint8ToRmtAdaptor); + ESP_ERROR_CHECK(rmt_enable(channel_)); } void Esp32DriverRGBW::end() { - ESP_ERROR_CHECK(rmt_driver_uninstall((rmt_channel_t)rmtChannel_)); + waitForTransmissionToFinish(); + ESP_ERROR_CHECK(rmt_disable(channel_)); + ESP_ERROR_CHECK(rmt_del_encoder(encoder_)); + ESP_ERROR_CHECK(rmt_del_channel(channel_)); + encoder_ = nullptr; + channel_ = nullptr; } void Esp32DriverRGBW::writeSync(const uint32_t *rgbwData, int numLeds) { + writeAsync(rgbwData, numLeds); waitForTransmissionToFinish(); - auto data = reinterpret_cast(rgbwData); - ESP_ERROR_CHECK(rmt_write_sample((rmt_channel_t)rmtChannel_, data, numLeds * 4, true)); } void Esp32DriverRGBW::writeAsync(const uint32_t *rgbwData, int numLeds) { waitForTransmissionToFinish(); - auto data = reinterpret_cast(rgbwData); - ESP_ERROR_CHECK(rmt_write_sample((rmt_channel_t)rmtChannel_, data, numLeds * 4, false)); + rmt_transmit_config_t transmitConfig = {}; + transmitConfig.loop_count = 0; + ESP_ERROR_CHECK(rmt_transmit(channel_, encoder_, rgbwData, numLeds * 4, &transmitConfig)); transmitting_ = true; } @@ -96,8 +75,7 @@ bool Esp32DriverRGBW::waitForTransmissionToFinish(int waitMs) { if (!transmitting_) return true; - auto ret = rmt_wait_tx_done((rmt_channel_t)rmtChannel_, waitMs / portTICK_PERIOD_MS); - if (ret == ESP_OK) + if (rmt_tx_wait_all_done(channel_, waitMs) == ESP_OK) { transmitting_ = false; return true; @@ -106,4 +84,4 @@ bool Esp32DriverRGBW::waitForTransmissionToFinish(int waitMs) return false; } -#endif \ No newline at end of file +#endif diff --git a/esp-firmware/lib/ledtl/drivers/Esp32DriverRGBW.h b/esp-firmware/lib/ledtl/drivers/Esp32DriverRGBW.h index dda9088..a691ba1 100644 --- a/esp-firmware/lib/ledtl/drivers/Esp32DriverRGBW.h +++ b/esp-firmware/lib/ledtl/drivers/Esp32DriverRGBW.h @@ -4,10 +4,13 @@ #include "containers/LedStripRGBW.h" +#include + class Esp32DriverRGBW { public: - void begin(int gpio, int rmtChannel); + // The RMT channel is picked by the driver; only the pin is ours to choose. + void begin(int gpio); void end(); void writeSync(const uint32_t *rgbwData, int numLeds); @@ -16,8 +19,9 @@ public: bool waitForTransmissionToFinish(int waitMs = 1000); private: - int rmtChannel_; - bool transmitting_; + rmt_channel_handle_t channel_ = nullptr; + rmt_encoder_handle_t encoder_ = nullptr; + bool transmitting_ = false; }; -#endif \ No newline at end of file +#endif diff --git a/esp-firmware/lib/rotary_encoder/rotary_encoder.c b/esp-firmware/lib/rotary_encoder/rotary_encoder.c index d9aa854..d81de14 100644 --- a/esp-firmware/lib/rotary_encoder/rotary_encoder.c +++ b/esp-firmware/lib/rotary_encoder/rotary_encoder.c @@ -220,12 +220,12 @@ esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gp info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET; // configure GPIOs - gpio_pad_select_gpio(info->pin_a); + gpio_reset_pin(info->pin_a); gpio_set_pull_mode(info->pin_a, GPIO_PULLUP_ONLY); gpio_set_direction(info->pin_a, GPIO_MODE_INPUT); gpio_set_intr_type(info->pin_a, GPIO_INTR_ANYEDGE); - gpio_pad_select_gpio(info->pin_b); + gpio_reset_pin(info->pin_b); gpio_set_pull_mode(info->pin_b, GPIO_PULLUP_ONLY); gpio_set_direction(info->pin_b, GPIO_MODE_INPUT); gpio_set_intr_type(info->pin_b, GPIO_INTR_ANYEDGE); diff --git a/esp-firmware/platformio.ini b/esp-firmware/platformio.ini index df60b87..6e38933 100644 --- a/esp-firmware/platformio.ini +++ b/esp-firmware/platformio.ini @@ -13,7 +13,9 @@ data_dir = data default_envs = esp32 [env:esp32] -platform = espressif32 +; pioarduino's fork rather than PlatformIO's own espressif32 platform, which stays on +; Arduino core 2.x. Pinned to a release: 55.03.312-1 is Arduino core 3.3.12 on ESP-IDF 5.5. +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.312-1/platform-espressif32.zip board = esp-wrover-kit board_upload.flash_size = "4MB" build_flags = -DPLATFORM_ESP32 @@ -21,14 +23,14 @@ framework = arduino monitor_port = /dev/ttyUSB0 upload_port = /dev/ttyUSB0 monitor_speed = 115200 -src_filter = +<*> - +build_src_filter = +<*> - lib_deps = - miguelbalboa/MFRC522 - bxparks/AceButton@^1.9.1 + miguelbalboa/MFRC522@1.4.12 + bxparks/AceButton@1.10.1 [env:native] -platform = native -src_filter = +<*> - +platform = native@1.2.1 +build_src_filter = +<*> - build_flags = -Ilib/ledtl -g -DPLATFORM_NATIVE lib_deps = - bxparks/AceButton@^1.9.1 + bxparks/AceButton@1.10.1 diff --git a/esp-firmware/src/Messages.h b/esp-firmware/src/Messages.h index d7b4f27..93d5f3a 100644 --- a/esp-firmware/src/Messages.h +++ b/esp-firmware/src/Messages.h @@ -183,7 +183,7 @@ inline bool handleLedEffect(TLedTask *ledTask, MessageHostToFw msgType, MessageH } template -inline void handleIncomingMessagesFromHost(LedTask1 *ledTaskCircle, LedTask2 *ledTaskMouse, LedTaskShelf *ledTaskShelf, uint8_t ledChannelLeft, uint8_t ledChannelRight) +inline void handleIncomingMessagesFromHost(LedTask1 *ledTaskCircle, LedTask2 *ledTaskMouse, LedTaskShelf *ledTaskShelf, uint8_t ledPinLeft, uint8_t ledPinRight) { if (Serial.available() < sizeof(MAGIC_TOKEN_FW_TO_HOST) + sizeof(MessageHostToFw) + sizeof(uint16_t)) return; @@ -238,12 +238,12 @@ inline void handleIncomingMessagesFromHost(LedTask1 *ledTaskCircle, LedTask2 *le else if (msgType == MessageHostToFw::PREV_BUTTON_LED) { float *val = reinterpret_cast(msgBuffer); - ledcWrite(ledChannelLeft, uint32_t(255 * (*val))); + ledcWrite(ledPinLeft, uint32_t(255 * (*val))); } else if (msgType == MessageHostToFw::NEXT_BUTTON_LED) { float *val = reinterpret_cast(msgBuffer); - ledcWrite(ledChannelRight, uint32_t(255 * (*val))); + ledcWrite(ledPinRight, uint32_t(255 * (*val))); } else Serial.println("Unknown message type"); diff --git a/esp-firmware/src/main.cpp b/esp-firmware/src/main.cpp index 91c7144..04ff0c6 100644 --- a/esp-firmware/src/main.cpp +++ b/esp-firmware/src/main.cpp @@ -117,11 +117,9 @@ void setupButtons() buttonRight.setEventHandler(handleButtonEvent); buttonRotary.setEventHandler(handleButtonEvent); - ledcSetup(0, PWM_FREQ, PWM_RESOLUTION); - ledcAttachPin(BUTTON_LEFT_LED_PIN, 0); - - ledcSetup(1, PWM_FREQ, PWM_RESOLUTION); - ledcAttachPin(BUTTON_RIGHT_LED_PIN, 1); + // Arduino core 3 picks the LEDC channel itself; from here on the pin is the handle. + ledcAttach(BUTTON_LEFT_LED_PIN, PWM_FREQ, PWM_RESOLUTION); + ledcAttach(BUTTON_RIGHT_LED_PIN, PWM_FREQ, PWM_RESOLUTION); } void handleButtons() @@ -139,7 +137,7 @@ LedTask ledTaskCircle; void setupLedCircle() { - ledDriverCircle.begin(22, 0); + ledDriverCircle.begin(22); ledTaskCircle.begin(ledStripCircle, ledDriverCircle); ledTaskCircle.startEffect(EffectStaticConfig(ColorRGBW{0, 0, 0, 0})); } @@ -152,7 +150,7 @@ LedTask ledTaskMouse; void setupMouseLeds() { - ledDriverMouse.begin(16, 1); + ledDriverMouse.begin(16); ledTaskMouse.begin(ledStripMouse, ledDriverMouse); ledTaskMouse.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}, 0, 0}); } @@ -165,7 +163,7 @@ LedTask ledTaskShelf; void setupShelfLeds() { - ledDriverShelf.begin(17, 2); + ledDriverShelf.begin(17); ledTaskShelf.begin(ledStripShelf, ledDriverShelf); ledTaskShelf.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}, 0, 0}); } @@ -234,7 +232,7 @@ void setup() void loop() { - handleIncomingMessagesFromHost(&ledTaskCircle, &ledTaskMouse, &ledTaskShelf, 0, 1); + handleIncomingMessagesFromHost(&ledTaskCircle, &ledTaskMouse, &ledTaskShelf, BUTTON_LEFT_LED_PIN, BUTTON_RIGHT_LED_PIN); handleTouchInputs(); handleRotaryEncoder(); handleButtons();