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 <noreply@anthropic.com>
This commit is contained in:
@@ -2,93 +2,72 @@
|
||||
|
||||
#include "drivers/Esp32DriverRGBW.h"
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/rmt.h>
|
||||
#include <driver/rmt_encoder.h>
|
||||
#include <esp_check.h>
|
||||
|
||||
// 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<rmt_channel_t>(rmtChannel);
|
||||
rmt_tx.gpio_num = static_cast<gpio_num_t>(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_num_t>(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<const uint8_t *>(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<const uint8_t *>(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
|
||||
#endif
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#include "containers/LedStripRGBW.h"
|
||||
|
||||
#include <driver/rmt_tx.h>
|
||||
|
||||
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
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = +<*> -<host_test.cpp>
|
||||
build_src_filter = +<*> -<host_test.cpp>
|
||||
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 = +<*> -<main.cpp>
|
||||
platform = native@1.2.1
|
||||
build_src_filter = +<*> -<main.cpp>
|
||||
build_flags = -Ilib/ledtl -g -DPLATFORM_NATIVE
|
||||
lib_deps =
|
||||
bxparks/AceButton@^1.9.1
|
||||
bxparks/AceButton@1.10.1
|
||||
|
||||
@@ -183,7 +183,7 @@ inline bool handleLedEffect(TLedTask *ledTask, MessageHostToFw msgType, MessageH
|
||||
}
|
||||
|
||||
template <typename LedTask1, typename LedTask2, typename LedTaskShelf>
|
||||
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<float *>(msgBuffer);
|
||||
ledcWrite(ledChannelLeft, uint32_t(255 * (*val)));
|
||||
ledcWrite(ledPinLeft, uint32_t(255 * (*val)));
|
||||
}
|
||||
else if (msgType == MessageHostToFw::NEXT_BUTTON_LED)
|
||||
{
|
||||
float *val = reinterpret_cast<float *>(msgBuffer);
|
||||
ledcWrite(ledChannelRight, uint32_t(255 * (*val)));
|
||||
ledcWrite(ledPinRight, uint32_t(255 * (*val)));
|
||||
}
|
||||
else
|
||||
Serial.println("Unknown message type");
|
||||
|
||||
@@ -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<decltype(ledStripCircle)> 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<decltype(ledStripMouse)> 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<decltype(ledStripShelf)> 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();
|
||||
|
||||
Reference in New Issue
Block a user