From 329446d0d8a9a13f17158295771bedbb75f8c26c Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 07:22:40 -0800 Subject: [PATCH 01/10] Syncing ESP-IDF v5.3 changes - Adding pthread dependency for esp-idf due to not being included by default in all cases - more renames of ESP32 -> ESP_PLATFORM, some still remain but are outside the areas of idf v5.3 fixes - ADC cleanups based on API changes / deprecations. - Fix bootloader CAN RX timeout value not being used. - TWAI: When working in single-core mode IPC calls will fail, so skip them. - TWAI: ISR discards bus-error status when in error-passive as it counts twice! - TWAI: drop TX frames when in error-passive stateu as they will not be sent by the underlying hardware. - TWAI: added additional validations to detect TX stuck condition in watchdog. - Uninitialized: C++23 fix due to std::aligned_storage deprecation --- arduino/idf/CMakeLists.txt | 2 + src/freertos_drivers/arduino/ArduinoGpio.hxx | 6 +- src/freertos_drivers/common/CpuLoad.cxx | 36 +++-- .../esp32/Esp32AdcOneShot.hxx | 129 +++++++++++++++--- .../esp32/Esp32AdcUnitManager.cxx | 85 ++++++++++++ .../esp32/Esp32BootloaderHal.hxx | 6 +- .../esp32/Esp32CoreDumpUtil.hxx | 43 ++++-- src/freertos_drivers/esp32/Esp32Gpio.hxx | 17 ++- .../esp32/Esp32HardwareTwai.cxx | 80 +++++++---- .../esp32/Esp32HardwareTwai.hxx | 26 ++-- src/freertos_drivers/esp32/Esp32Ledc.hxx | 14 +- .../esp32/Esp32WiFiManager.cxx | 2 +- src/utils/Uninitialized.hxx | 8 ++ 13 files changed, 361 insertions(+), 93 deletions(-) create mode 100644 src/freertos_drivers/esp32/Esp32AdcUnitManager.cxx diff --git a/arduino/idf/CMakeLists.txt b/arduino/idf/CMakeLists.txt index 078cfe8cd..5bf3f4313 100644 --- a/arduino/idf/CMakeLists.txt +++ b/arduino/idf/CMakeLists.txt @@ -23,6 +23,7 @@ set(IDF_DEPS espcoredump hal heap + pthread vfs mdns) @@ -37,6 +38,7 @@ idf_component_register(SRC_DIRS "${SOURCE_DIRS}" target_compile_options(${COMPONENT_LIB} PUBLIC $<$:-Wno-volatile>) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-type-limits) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-ignored-qualifiers) +target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-strict-aliasing) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers) target_compile_options(${COMPONENT_LIB} PRIVATE $<$:-Wno-class-memaccess>) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough) diff --git a/src/freertos_drivers/arduino/ArduinoGpio.hxx b/src/freertos_drivers/arduino/ArduinoGpio.hxx index 6a552c9f4..7885a38c5 100644 --- a/src/freertos_drivers/arduino/ArduinoGpio.hxx +++ b/src/freertos_drivers/arduino/ArduinoGpio.hxx @@ -37,7 +37,7 @@ #include "os/Gpio.hxx" #include "GpioWrapper.hxx" -#if defined(ESP32) +#ifdef ESP_PLATFORM #include #include #else @@ -119,7 +119,7 @@ public: /// @return true if pin is configured as an output pin. static bool is_output() { -#if defined(ESP32) +#ifdef ESP_PLATFORM if(digitalPinIsValid(PIN_NUM) && digitalPinCanOutput(PIN_NUM)) { // pins 32 and below use the first GPIO controller @@ -132,7 +132,7 @@ public: return GPIO.enable1_w1ts.val & ((uint32_t)1 << (PIN_NUM & 31)); } } -#endif +#endif // ESP_PLATFORM return false; } diff --git a/src/freertos_drivers/common/CpuLoad.cxx b/src/freertos_drivers/common/CpuLoad.cxx index 7f57db1b8..654437af1 100644 --- a/src/freertos_drivers/common/CpuLoad.cxx +++ b/src/freertos_drivers/common/CpuLoad.cxx @@ -39,9 +39,10 @@ #include "os/os.h" #include "freertos_includes.h" -#ifdef ESP32 +#ifdef ESP_PLATFORM #include "sdkconfig.h" -#endif // ESP32 +#include +#endif // ESP_PLATFORM extern "C" { @@ -117,13 +118,26 @@ uint8_t CpuLoad::get_load() { return (avg_ * 100) >> SHIFT_ONE; } +#ifdef ESP_PLATFORM +// In ESP-IDF v5.3.0 there were method renames as part of standardizing the +// APIs with the upstream FreeRTOS version, as such these methods were +// renamed and require usage of wrappers. +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,3,0) +#define FREERTOS_GET_CURRENT_TASK_HANDLE_FOR_CORE(core) xTaskGetCurrentTaskHandleForCore(core) +#define FREERTOS_GET_IDLE_TASK_HANDLE_FOR_CORE(core) xTaskGetIdleTaskHandleForCore(core) +#else +#define FREERTOS_GET_CURRENT_TASK_HANDLE_FOR_CORE(core) xTaskGetCurrentTaskHandleForCPU(core) +#define FREERTOS_GET_IDLE_TASK_HANDLE_FOR_CORE(core) xTaskGetIdleTaskHandleForCPU(core) +#endif +#endif // ESP_PLATFORM + void cpuload_tick(unsigned irq) { if (!Singleton::exists()) return; // On the ESP32 it is necessary to use a slightly different approach for // recording CPU usage metrics since there may be additional CPU cores. -#ifdef ESP32 +#ifdef ESP_PLATFORM if (irq != 0) { Singleton::instance()->record_value(true, (uintptr_t)irq); @@ -132,21 +146,21 @@ void cpuload_tick(unsigned irq) { // Record the first CPU core (PRO_CPU). NOTE: This assumes that OpenMRN // is running on this core. - auto hdl = xTaskGetCurrentTaskHandleForCPU(PRO_CPU_NUM); - bool is_idle = xTaskGetIdleTaskHandleForCPU(PRO_CPU_NUM) == hdl; + auto hdl = FREERTOS_GET_CURRENT_TASK_HANDLE_FOR_CORE(PRO_CPU_NUM); + bool is_idle = FREERTOS_GET_IDLE_TASK_HANDLE_FOR_CORE(PRO_CPU_NUM) == hdl; Singleton::instance()->record_value(!is_idle, (uintptr_t)hdl); } // NOTE: The ESP32-S2 and ESP32-C3 are single-core SoC and defines the // FREERTOS_UNICORE flag which we can use here to disable recording of the -// APP_CPU. +// APP_CPU. This can also be used on dual core SoCs with some restrictions. #ifndef CONFIG_FREERTOS_UNICORE // Record the second CPU core (APP_CPU). NOTE: this is where application // code typically runs. - auto hdl = xTaskGetCurrentTaskHandleForCPU(APP_CPU_NUM); - bool is_idle = xTaskGetIdleTaskHandleForCPU(APP_CPU_NUM) == hdl; + auto hdl = FREERTOS_GET_CURRENT_TASK_HANDLE_FOR_CORE(APP_CPU_NUM); + bool is_idle = FREERTOS_GET_IDLE_TASK_HANDLE_FOR_CORE(APP_CPU_NUM) == hdl; Singleton::instance()->record_value(!is_idle, (uintptr_t)hdl); -#endif // CONFIG_FREERTOS_UNICORE -#else // NOT ESP32 +#endif // !CONFIG_FREERTOS_UNICORE +#else // NOT ESP_PLATFORM if (irq != 0) { Singleton::instance()->record_value(true, (uintptr_t)irq); @@ -155,7 +169,7 @@ void cpuload_tick(unsigned irq) auto hdl = xTaskGetCurrentTaskHandle(); bool is_idle = xTaskGetIdleTaskHandle() == hdl; Singleton::instance()->record_value(!is_idle, (uintptr_t)hdl); -#endif // ESP32 +#endif // ESP_PLATFORM } } // extern "C" diff --git a/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx b/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx index 8af0ee0a3..27e7feade 100644 --- a/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx +++ b/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx @@ -46,9 +46,42 @@ #endif // __has_include driver/adc_types_legacy.h #endif // has_include +#include #include +#include +#ifndef CONFIG_ADC_SUPPRESS_DEPRECATE_WARN +#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1 +#endif +#include +#include +#include #include +/// This class manages a single ADC Unit. +class Esp32ADCUnitManager +{ +public: + /// Constructor. + /// + /// @param unit @ref adc_unit_t that this instance owns the handle for. + Esp32ADCUnitManager(const adc_unit_t unit); + + /// Initializes the underlying hardware unit if not already initialized. + void hw_init(); + + /// @returns the handle to use for this ADC unit. + adc_oneshot_unit_handle_t handle(); +private: + /// ADC unit identifier that this instance manages. + const adc_unit_t unit_; + + /// @ref adc_oneshot_unit_handle_t that this instance manages. + adc_oneshot_unit_handle_t handle_; +}; + +/// Declaration of ADC unit managers. +extern Esp32ADCUnitManager Esp32AdcUnit[SOC_ADC_PERIPH_NUM]; + /// Defines an ADC input pin. /// /// Do not use this class directly. Use @ref ADC_PIN instead. @@ -59,56 +92,114 @@ public: using Defs::BITS; using Defs::CHANNEL; using Defs::PIN; - using Defs::HANDLE; + using Defs::CALIB_HANDLE; + using Defs::VREF_CALIB; + /// Initializes the underlying ADC hardware for use, including calibration + /// for millivolt reading. static void hw_init() { - // due to using #if/#elif/#endif it is not possible to include this in - // the ADC_PIN wrapper code. - const adc_oneshot_unit_init_cfg_t unit_config = - { #if CONFIG_IDF_TARGET_ESP32 - .unit_id = PIN >= 30 ? ADC_UNIT_1 : ADC_UNIT_2, + const adc_unit_t unit = PIN >= 30 ? ADC_UNIT_1 : ADC_UNIT_2; #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - .unit_id = PIN <= 10 ? ADC_UNIT_1 : ADC_UNIT_2, + const adc_unit_t unit = PIN <= 10 ? ADC_UNIT_1 : ADC_UNIT_2; #elif CONFIG_IDF_TARGET_ESP32C3 - .unit_id = PIN <= 4 ? ADC_UNIT_1 : ADC_UNIT_2, + const adc_unit_t unit = PIN <= 4 ? ADC_UNIT_1 : ADC_UNIT_2; +#else + #warning Unable to determine ADC unit number, defaulting to ADC_UNIT_1 + const adc_unit_t unit = ADC_UNIT_1; #endif - .ulp_mode = ADC_ULP_MODE_DISABLE, - }; const adc_oneshot_chan_cfg_t channel_config = { .atten = ATTEN, .bitwidth = BITS, }; + // Initialize the ADC unit. + Esp32AdcUnit[unit].hw_init(); + LOG(VERBOSE, "[Esp32ADCInput] Configuring ADC%d:%d input pin %d, " "attenuation %d, bits %d", - unit_config.unit_id, CHANNEL, PIN, ATTEN, BITS); - ESP_ERROR_CHECK(adc_oneshot_new_unit(&unit_config, &HANDLE)); + unit, CHANNEL, PIN, ATTEN, BITS); + + // Initialize the ADC channel using the handle from the unit manager. + ESP_ERROR_CHECK(adc_oneshot_config_channel( + Esp32AdcUnit[unit].handle(), CHANNEL, &channel_config)); + +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + adc_cali_curve_fitting_config_t config = + { + .unit_id = unit, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,2) + .chan = CHANNEL, +#endif + .atten = ATTEN, + .bitwidth = BITS + }; + ESP_ERROR_CHECK( + adc_cali_create_scheme_curve_fitting(&config, &CALIB_HANDLE)); +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + adc_cali_line_fitting_config_t config = + { + .unit_id = unit, + .atten = ATTEN, + .bitwidth = BITS + }; ESP_ERROR_CHECK( - adc_oneshot_config_channel(HANDLE, CHANNEL, &channel_config)); + adc_cali_create_scheme_line_fitting(&config, &CALIB_HANDLE)); +#else + esp_adc_cal_characterize(unit, ATTEN, BITS, DEFAULT_VREF, &VREF_CALIB); + LOG(VERBOSE, "[Esp32ADCInput] Using vRef of %d mV", VREF_CALIB.vref); +#endif } - /// NO-OP + /// NO-OP, unsupported static void hw_set_to_safe() { // NO-OP } - /// NO-OP + /// NO-OP, unsupported static void set(bool value) { // NO-OP } + /// @return raw sample from the ADC hardware. static int sample() { +#if CONFIG_IDF_TARGET_ESP32 + const adc_unit_t unit = PIN >= 30 ? ADC_UNIT_1 : ADC_UNIT_2; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + const adc_unit_t unit = PIN <= 10 ? ADC_UNIT_1 : ADC_UNIT_2; +#elif CONFIG_IDF_TARGET_ESP32C3 + const adc_unit_t unit = PIN <= 4 ? ADC_UNIT_1 : ADC_UNIT_2; +#else + const adc_unit_t unit = ADC_UNIT_1; +#endif int value = 0; - ESP_ERROR_CHECK(adc_oneshot_read(HANDLE, CHANNEL, &value)); + ESP_ERROR_CHECK( + adc_oneshot_read(Esp32AdcUnit[unit].handle(), CHANNEL, &value)); return value; } + + /// @return Approximate millivolt value from the ADC hardware. + static int sample_mv() + { +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED || \ + ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + int calib_mv = 0; + ESP_ERROR_CHECK( + adc_cali_raw_to_voltage(CALIB_HANDLE, sample(), &calib_mv)); + return calib_mv; +#else + return esp_adc_cal_raw_to_voltage(sample(), &VREF_CALIB); +#endif + } +private: + /// Default ADC voltage reference value. + static constexpr uint32_t DEFAULT_VREF = 1100; }; /// Helper macro for an ADC GPIO input on the ESP32. @@ -200,7 +291,8 @@ public: static const gpio_num_t PIN = (gpio_num_t)ADC_CHANNEL##_GPIO_NUM; \ static const adc_atten_t ATTEN = (adc_atten_t)ATTENUATION; \ static const adc_bitwidth_t BITS = (adc_bitwidth_t)BIT_RANGE; \ - static adc_oneshot_unit_handle_t HANDLE; \ + static adc_cali_handle_t CALIB_HANDLE; \ + static esp_adc_cal_characteristics_t VREF_CALIB; \ public: \ static const gpio_num_t pin() \ { \ @@ -211,7 +303,8 @@ public: return CHANNEL; \ } \ }; \ - adc_oneshot_unit_handle_t NAME##Defs::HANDLE; \ + adc_cali_handle_t NAME##Defs::CALIB_HANDLE; \ + esp_adc_cal_characteristics_t NAME##Defs::VREF_CALIB; \ typedef Esp32ADCInput NAME##_Pin #endif // _DRIVERS_ESP32ADCONESHOT_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32AdcUnitManager.cxx b/src/freertos_drivers/esp32/Esp32AdcUnitManager.cxx new file mode 100644 index 000000000..689d349c7 --- /dev/null +++ b/src/freertos_drivers/esp32/Esp32AdcUnitManager.cxx @@ -0,0 +1,85 @@ +/** \copyright + * Copyright (c) 2023, Mike Dunston + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file Esp32AdcUnitManager.cxx + * + * Implementation of ADC conversion unit manager. + * + * @author Mike Dunston + * @date 21 July 2023 + */ + +#if defined(ESP_PLATFORM) + +#include "freertos_drivers/esp32/Esp32AdcOneShot.hxx" + +Esp32ADCUnitManager Esp32AdcUnit[SOC_ADC_PERIPH_NUM] = +{ + ADC_UNIT_1, +#if SOC_ADC_PERIPH_NUM >= 2 + ADC_UNIT_2 +#endif // SOC_ADC_PERIPH_NUM == 2 +}; + +// Constructor. +Esp32ADCUnitManager::Esp32ADCUnitManager(const adc_unit_t unit) + : unit_(unit), handle_(nullptr) +{ +} + +// Initializes the underlying hardware unit if not already initialized. +void Esp32ADCUnitManager::hw_init() +{ + // due to using #if/#elif/#endif it is not possible to include this in + // the ADC_PIN wrapper code. + const adc_oneshot_unit_init_cfg_t unit_config = + { + .unit_id = unit_, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,0) + // use default value from driver, no constant available thus using + // 0 with type cast. + .clk_src = (adc_oneshot_clk_src_t)0, +#endif // IDF v5.1+ + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + + // Check if we have been initialized previously. + if (handle()) + { + return; + } + + LOG(VERBOSE, "[Esp32ADCUnit] Initializing ADC Unit: %d", unit_); + ESP_ERROR_CHECK(adc_oneshot_new_unit(&unit_config, &handle_)); +} + +// Returns the handle to use for this ADC unit. +adc_oneshot_unit_handle_t Esp32ADCUnitManager::handle() +{ + return handle_; +} + +#endif // defined(ESP_PLATFORM) \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx b/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx index 8fb8b3b8b..3bb06229b 100644 --- a/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx +++ b/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx @@ -51,13 +51,13 @@ // Enable streaming support for the bootloader #define BOOTLOADER_STREAM + #ifndef WRITE_BUFFER_SIZE // Set the buffer size to half of the sector size to minimize the flash writes. #define WRITE_BUFFER_SIZE (CONFIG_WL_SECTOR_SIZE / 2) #endif // WRITE_BUFFER_SIZE #include - #include #include #include @@ -194,7 +194,7 @@ bool read_can_frame(struct can_frame *frame) { twai_message_t rx_msg; memset(&rx_msg, 0, sizeof(twai_message_t)); - if (twai_receive(&rx_msg, MAX_TWAI_WAIT) == ESP_OK) + if (twai_receive(&rx_msg, MAX_TWAI_WAIT_RX) == ESP_OK) { LOG(BOOTLOADER_TWAI_LOG_LEVEL, "[Bootloader] CAN_RX"); frame->can_id = rx_msg.identifier; @@ -578,4 +578,4 @@ void esp32_bootloader_run(uint64_t id, gpio_num_t rx, gpio_num_t tx, } } -#endif // _FREERTOS_DRIVERS_ESP32_ESP32BOOTLOADERHAL_HXX_ +#endif // _FREERTOS_DRIVERS_ESP32_ESP32BOOTLOADERHAL_HXX_ \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32CoreDumpUtil.hxx b/src/freertos_drivers/esp32/Esp32CoreDumpUtil.hxx index 8eaf609a9..6b834c651 100644 --- a/src/freertos_drivers/esp32/Esp32CoreDumpUtil.hxx +++ b/src/freertos_drivers/esp32/Esp32CoreDumpUtil.hxx @@ -47,14 +47,27 @@ namespace openmrn_arduino { +// Currently the core dump functionality here supports only the xtensa +// architecture and requires multiple core dump flags in sdkconfig which are +// not enabled by default. +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && \ + CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF && \ + CONFIG_IDF_TARGET_ARCH_XTENSA +#define CORE_DUMP_USABLE_XTENSA 1 +#endif + /// Utility class containing methods related to esp32 core dumps. /// -/// Usage of this class requires the enablement of -/// CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH in sdkconfig *AND* the creation of -/// a 64kb "coredump" partition in flash. +/// This class requires the following sdkconfig flags to be enabled and the +/// addition of a 64kb "coredump" partition in flash. +/// +/// Required sdkconfig flags: +/// * CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +/// * CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF +/// If not all three flags are enabled all methods will be treated as no-op. /// -/// When the CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH flag is not enabled all -/// methods in this class are treated as no-op. +/// At this time the RISC-V MCUs are not suported and may become supported in +/// the future. /// /// NOTE: ESP-IDF v4.4 or later is required for the display() method. class Esp32CoreDumpUtil @@ -64,10 +77,14 @@ public: /// @return true if a core dump is available, false otherwise. static bool is_present() { +#ifdef CORE_DUMP_USABLE_XTENSA esp_log_level_set("esp_core_dump_flash", ESP_LOG_NONE); esp_err_t res = esp_core_dump_image_check(); esp_log_level_set("esp_core_dump_flash", ESP_LOG_WARN); return res == ESP_OK; +#else // !CORE_DUMP_USABLE_XTENSA + return false; +#endif } /// Utility method that displays a core dump (if present). @@ -76,6 +93,7 @@ public: /// filesystem. static void display(const char *output_path = nullptr) { +#ifdef CORE_DUMP_USABLE_XTENSA if (is_present()) { esp_core_dump_summary_t details; @@ -83,21 +101,22 @@ public: { // Convert the core dump to a text file string core_dump_summary = - StringPrintf("Task:%s (%d) crashed at PC %08x\n", + StringPrintf("Task:%s (%" PRIu32 ") crashed at PC %08" PRIx32 "\n", details.exc_task, details.exc_tcb, details.exc_pc); core_dump_summary += StringPrintf("Registers:\n"); for (size_t idx = 0; idx < 16; idx += 4) { core_dump_summary += StringPrintf( - "A%02zu: 0x%08x A%02zu: 0x%08x A%02zu: 0x%08x A%02zu: 0x%08x\n", + "A%02zu: 0x%08" PRIx32 " A%02zu: 0x%08" PRIx32 + " A%02zu: 0x%08" PRIx32 " A%02zu: 0x%08" PRIx32 "\n", idx, details.ex_info.exc_a[idx], idx + 1, details.ex_info.exc_a[idx + 1], idx + 2, details.ex_info.exc_a[idx + 2], idx + 3, details.ex_info.exc_a[idx + 3]); } core_dump_summary += - StringPrintf("EXCCAUSE: %08x EXCVADDR: %08x\n", + StringPrintf("EXCCAUSE: %08" PRIx32 " EXCVADDR: %08" PRIx32 "\n", details.ex_info.exc_cause, details.ex_info.exc_vaddr); if (details.ex_info.epcx_reg_bits) { @@ -107,7 +126,8 @@ public: if (details.ex_info.epcx_reg_bits & BIT(idx)) { core_dump_summary += - StringPrintf("%zu:%08x ", idx, details.ex_info.epcx[idx]); + StringPrintf("%zu:%08" PRIx32 " ", idx, + details.ex_info.epcx[idx]); } } core_dump_summary += "\n"; @@ -116,7 +136,7 @@ public: for (size_t idx = 0; idx < details.exc_bt_info.depth; idx++) { core_dump_summary += - StringPrintf(" 0x%08x", details.exc_bt_info.bt[idx]); + StringPrintf(" 0x%08" PRIx32, details.exc_bt_info.bt[idx]); if (details.exc_bt_info.corrupted) { core_dump_summary += "(corrupted)"; @@ -130,15 +150,18 @@ public: } } } +#endif // CORE_DUMP_USABLE_XTENSA } /// Utility method to cleanup a core dump. static void cleanup() { +#ifdef CORE_DUMP_USABLE_XTENSA if (is_present()) { ESP_ERROR_CHECK_WITHOUT_ABORT(esp_core_dump_image_erase()); } +#endif // CORE_DUMP_USABLE_XTENSA } }; diff --git a/src/freertos_drivers/esp32/Esp32Gpio.hxx b/src/freertos_drivers/esp32/Esp32Gpio.hxx index afa2d4ac0..f051604ee 100644 --- a/src/freertos_drivers/esp32/Esp32Gpio.hxx +++ b/src/freertos_drivers/esp32/Esp32Gpio.hxx @@ -35,7 +35,14 @@ #ifndef _DRIVERS_ESP32GPIO_HXX_ #define _DRIVERS_ESP32GPIO_HXX_ +// TODO: clean this up as part of https://github.com/bakerstu/openmrn/issues/778 +// as this file should be coming in from common and not arduino ideally. +#if defined(__has_include) && \ + __has_include("freertos_drivers/common/GpioWrapper.hxx") #include "freertos_drivers/common/GpioWrapper.hxx" +#else +#include "freertos_drivers/arduino/GpioWrapper.hxx" +#endif #include "freertos_drivers/esp32/Esp32AdcOneShot.hxx" #include "os/Gpio.hxx" #include "utils/logging.h" @@ -43,7 +50,7 @@ #include #include - +#include #include #if defined(CONFIG_IDF_TARGET_ESP32C3) @@ -51,7 +58,8 @@ /// /// This is necessary since ESP-IDF does not expose gpio_get_direction(pin). #define IS_GPIO_OUTPUT(pin) (GPIO_IS_VALID_OUTPUT_GPIO(pin) && \ - GPIO.enable.data & BIT(pin & 25)) + GPIO.enable.data & \ + BIT(pin & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) #else // NOT ESP32-C3 /// Helper macro to test if a pin has been configured for output. /// @@ -346,8 +354,6 @@ public: gpio_config_t cfg; memset(&cfg, 0, sizeof(gpio_config_t)); cfg.pin_bit_mask = BIT64(PIN_NUM); - // using GPIO_MODE_INPUT_OUTPUT instead of GPIO_MODE_OUTPUT so that - // we can read the IO state cfg.mode = GPIO_MODE_INPUT; if (PUEN) { @@ -559,9 +565,8 @@ template struct GpioInputPUPD : public GpioInputPin #endif #include +#ifndef CONFIG_FREERTOS_UNICORE #include +#endif #include #include #include @@ -70,7 +73,14 @@ #include "can_frame.h" #include "can_ioctl.h" #include "executor/Notifiable.hxx" +// TODO: clean this up as part of https://github.com/bakerstu/openmrn/issues/778 +// as this file should be coming in from common and not arduino ideally. +#if defined(__has_include) && \ + __has_include("freertos_drivers/common/DeviceBuffer.hxx") #include "freertos_drivers/common/DeviceBuffer.hxx" +#else +#include "freertos_drivers/arduino/DeviceBuffer.hxx" +#endif #include "freertos_drivers/esp32/Esp32HardwareTwai.hxx" #include "utils/Atomic.hxx" #include "utils/logging.h" @@ -232,7 +242,7 @@ static inline void twai_purge_rx_queue() Notifiable* n = nullptr; { AtomicHolder h(&twai.buf_lock); - LOG(VERBOSE, "ESP-TWAI: purging RX-Q: %zu", twai.rx_buf->pending()); + LOG(VERBOSE, "ESP-TWAI: purging RX-Q:%zu", twai.rx_buf->pending()); twai.stats.rx_missed += twai.rx_buf->pending(); twai.rx_buf->flush(); std::swap(n, twai.readable_notify); @@ -258,8 +268,8 @@ static inline void twai_purge_tx_queue() Notifiable* n = nullptr; { AtomicHolder h(&twai.buf_lock); - LOG(VERBOSE, "ESP-TWAI: purging TX-Q: %zu", twai.tx_buf->pending()); - twai.stats.tx_failed += twai.tx_buf->pending(); + LOG(VERBOSE, "ESP-TWAI: purging TX-Q:%zu", twai.tx_buf->pending()); + twai.stats.tx_lost += twai.tx_buf->pending(); twai.tx_buf->flush(); std::swap(n, twai.writable_notify); } @@ -309,6 +319,20 @@ static ssize_t twai_vfs_write(int fd, const void *buf, size_t size) bus_error = true; break; } + else if (is_twai_err_passive()) + { + // When the TWAI driver is in an error passive state it is not + // possible to transmit additional frames, purge the TX queue and + // track remaining frames as failed. + // + // NOTE: we are tracking pending remaining frames as written to + // ensure the stack does not unnecessarily become blocked. + + sent += size; + twai.stats.tx_lost += size; + bus_error = true; + break; + } size_t frames_written = 0; { @@ -419,7 +443,7 @@ static int twai_vfs_open(const char *path, int flags, int mode) path++; twai.non_blocking = (flags & O_NONBLOCK); - LOG(INFO, "ESP-TWAI: Starting TWAI driver on:%s mode:%x (%s) fd:%d", + LOG(VERBOSE, "ESP-TWAI: Starting TWAI driver on:%s mode:%x (%s) fd:%d", path, mode, twai.non_blocking ? "non-blocking" : "blocking", TWAI_VFS_FD); twai_purge_rx_queue(); @@ -617,7 +641,7 @@ static inline uint32_t twai_rx_frames() { // DLC is longer than supported, discard the frame. twai.stats.rx_discard++; - ESP_EARLY_LOGE(TWAI_LOG_TAG, "rx-discard:%" PRIu32, + ESP_EARLY_LOGE(TWAI_LOG_TAG, "rx-discard:%zu", twai.stats.rx_discard); } else if (twai.rx_buf->data_write_pointer(&can_frame)) @@ -649,7 +673,7 @@ static inline uint32_t twai_rx_frames() else { twai.stats.rx_missed++; - ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-missed:%" PRIu32, + ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-missed:%zu", twai.stats.rx_missed); } } @@ -713,7 +737,7 @@ static void twai_isr(void *arg) { BaseType_t wakeup = pdFALSE; uint32_t events = twai_hal_get_events(&twai.context); - ESP_EARLY_LOGV(TWAI_LOG_TAG, "events: %04" PRIx32, events); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "events:%04x", events); #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || \ defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) @@ -746,7 +770,7 @@ static void twai_isr(void *arg) esp_vfs_select_triggered_isr(twai.select_sem, &wakeup); } #endif // CONFIG_VFS_SUPPORT_SELECT - // std::swap is not ISR safe so it is not used here. + // std::swap is not guaranteed to be in IRAM, so it is not used here. if (twai.readable_notify) { twai.readable_notify->notify_from_isr(); @@ -765,7 +789,7 @@ static void twai_isr(void *arg) esp_vfs_select_triggered_isr(twai.select_sem, &wakeup); } #endif // CONFIG_VFS_SUPPORT_SELECT - // std::swap is not ISR safe so it is not used here. + // std::swap is not guaranteed to be in IRAM, so it is not used here. if (twai.writable_notify) { twai.writable_notify->notify_from_isr(); @@ -782,19 +806,19 @@ static void twai_isr(void *arg) } // Bus error detected - if (events & TWAI_HAL_EVENT_BUS_ERR) + // NOTE: this will be raised even after entering error-passive state and + // should be excluded from the bus_error increment accordingly. + if (events & TWAI_HAL_EVENT_BUS_ERR && !is_twai_err_passive()) { twai.stats.bus_error++; - ESP_EARLY_LOGV(TWAI_LOG_TAG, "bus-error:%" PRIu32, - twai.stats.bus_error); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "bus-error:%zu", twai.stats.bus_error); } // Arbitration error detected if (events & TWAI_HAL_EVENT_ARB_LOST) { twai.stats.arb_loss++; - ESP_EARLY_LOGV(TWAI_LOG_TAG, "arb-lost:%" PRIu32, - twai.stats.arb_loss); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "arb-loss:%zu", twai.stats.arb_loss); } if (wakeup == pdTRUE) @@ -822,9 +846,10 @@ static void twai_isr(void *arg) /// a general failure in communicating with the CAN transceiver IC. void* twai_watchdog(void* param) { - LOG(INFO, "ESP-TWAI: Starting TWAI watchdog and reporting task"); + LOG(VERBOSE, "ESP-TWAI: Starting TWAI watchdog and reporting task"); size_t last_rx_pending = 0; size_t last_tx_pending = 0; + size_t last_tx_success = 0; uint32_t last_twai_state = 0; while (twai.active) @@ -862,30 +887,31 @@ void* twai_watchdog(void* param) } last_rx_pending = twai.rx_buf->pending(); - // If the TX queue has not changed since our last check, purge the RX + // If the TX queue has not changed since our last check and the TX + // success count has not changed since our last check, purge the TX // queue and track it as failed frames. - if (last_tx_pending && last_tx_pending == twai.tx_buf->pending()) + if (last_tx_pending && last_tx_pending == twai.tx_buf->pending() && + last_tx_success && last_tx_success == twai.stats.tx_success) { LOG_ERROR("ESP-TWAI: TX-Q appears stuck, purging TX-Q!"); twai_purge_tx_queue(); } last_tx_pending = twai.tx_buf->pending(); + last_tx_success = twai.stats.tx_success; if (twai.report_stats) { LOG(INFO, "ESP-TWAI: " - "RX:%" PRIu32 " (pending:%zu,overrun:%" PRIu32 - ",discard:%" PRIu32 ",missed:%" PRIu32 ",lost:%" PRIu32 ") " - "TX:%" PRIu32 " (pending:%zu,suc:%" PRIu32 - ",fail:%" PRIu32 ") " - "Bus (arb-err:%" PRIu32 ",err:%" PRIu32 ",state:%s)", + "RX:%zu (pending:%zu,overrun:%zu,discard:%zu,miss:%zu,lost:%zu) " + "TX:%zu (pending:%zu,suc:%zu,fail:%zu,lost:%zu) " + "Bus (arb-loss:%zu,err:%zu,state:%s)", twai.stats.rx_processed, twai.rx_buf->pending(), twai.stats.rx_overrun, twai.stats.rx_discard, twai.stats.rx_missed, twai.stats.rx_lost, twai.stats.tx_processed, twai.tx_buf->pending(), twai.stats.tx_success, twai.stats.tx_failed, - twai.stats.arb_loss, twai.stats.bus_error, + twai.stats.tx_lost, twai.stats.arb_loss, twai.stats.bus_error, is_twai_running() ? "Running" : is_twai_recovering() ? "Recovering" : is_twai_err_warn() ? "Err-Warn" : @@ -962,7 +988,7 @@ static void esp32_twai_isr_init(void *param) void Esp32HardwareTwai::hw_init() { - LOG(INFO, + LOG(VERBOSE, "ESP-TWAI: Configuring TWAI (TX:%d, RX:%d, EXT-CLK:%d, BUS-CTRL:%d)", txPin_, rxPin_, extClockPin_, busStatusPin_); gpio_set_pull_mode((gpio_num_t)txPin_, GPIO_FLOATING); @@ -1046,12 +1072,12 @@ void Esp32HardwareTwai::hw_init() twai_hal_configure(&twai.context, &timingCfg, &filterCfg, TWAI_DEFAULT_INTERRUPTS, 0); -#if SOC_CPU_CORES_NUM > 1 +#ifndef CONFIG_FREERTOS_UNICORE ESP_ERROR_CHECK( esp_ipc_call_blocking(preferredIsrCore_, esp32_twai_isr_init, nullptr)); #else esp32_twai_isr_init(nullptr); -#endif // SOC_CPU_CORES_NUM > 1 +#endif // CONFIG_FREERTOS_UNICORE twai.active = true; os_thread_create(&twai.wd_thread, "TWAI-WD", WATCHDOG_TASK_PRIORITY, diff --git a/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx b/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx index 957cb8aa2..40f60b152 100644 --- a/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx +++ b/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx @@ -55,37 +55,41 @@ typedef struct { /// Number of frames have been removed from @ref rx_buf and sent to the /// OpenMRN stack. - uint32_t rx_processed; + size_t rx_processed; /// Number of frames frames that could not be sent to @ref rx_buf. - uint32_t rx_missed; + size_t rx_missed; /// Number of frames that were discarded that had too large of a DLC count. - uint32_t rx_discard; + size_t rx_discard; /// Number of frames that were lost due to driver reset. - uint32_t rx_lost; + size_t rx_lost; /// Number of frames that were lost due to RX FIFO overrun. - uint32_t rx_overrun; + size_t rx_overrun; /// Number of frames that have been sent to the @ref twai_tx_queue by the /// OpenMRN stack successfully. - uint32_t tx_processed; + size_t tx_processed; /// Number of frames that have been transmitted successfully by the /// low-level TWAI driver. - uint32_t tx_success; + size_t tx_success; /// Number of frames that have been could not be transmitted successfully /// by the low-level TWAI driver. - uint32_t tx_failed; + size_t tx_failed; + + /// Number of frames that were lost due to the low-level TWAI driver being + /// in an error-passive state or the tx queue was purged. + size_t tx_lost; /// Number of arbitration losses that have been observed on the TWAI bus. - uint32_t arb_loss; + size_t arb_loss; /// Number of general bus errors that have been observed on the TWAI bus. - uint32_t bus_error; + size_t bus_error; } esp32_twai_stats_t; /// ESP32 Hardware TWAI (CAN) driver interface. @@ -216,4 +220,4 @@ private: using openmrn_arduino::esp32_twai_stats_t; using openmrn_arduino::Esp32HardwareTwai; -#endif // _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ +#endif // _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32Ledc.hxx b/src/freertos_drivers/esp32/Esp32Ledc.hxx index b57f1b6f9..7e163f952 100644 --- a/src/freertos_drivers/esp32/Esp32Ledc.hxx +++ b/src/freertos_drivers/esp32/Esp32Ledc.hxx @@ -35,12 +35,20 @@ #ifndef _DRIVERS_ESP32LEDC_HXX_ #define _DRIVERS_ESP32LEDC_HXX_ +// TODO: clean this up as part of https://github.com/bakerstu/openmrn/issues/778 +// as this file should be coming in from common and not arduino ideally. +#if defined(__has_include) && \ + __has_include("freertos_drivers/common/PWM.hxx") #include "freertos_drivers/common/PWM.hxx" +#else +#include "freertos_drivers/arduino/PWM.hxx" +#endif #include "utils/logging.h" #include "utils/macros.h" #include "utils/Uninitialized.hxx" #include +#include namespace openmrn_arduino { @@ -131,7 +139,7 @@ public: /// will generate a runtime failure. void hw_init() { - LOG(INFO, + LOG(VERBOSE, "[Esp32Ledc:%d] Configuring timer (resolution:%d, frequency:%" PRIu32 ")", timerConfig_.timer_num, @@ -145,7 +153,7 @@ public: ledc_channel_t led_channel = static_cast(firstChannel_ + count); - LOG(INFO, "[Esp32Ledc:%d] Configuring LEDC channel %d on GPIO %d", + LOG(VERBOSE, "[Esp32Ledc:%d] Configuring LEDC channel %d on GPIO %d", timerConfig_.timer_num, led_channel, pin); ledc_channel_config_t config; memset(&config, 0, sizeof(ledc_channel_config_t)); @@ -325,4 +333,4 @@ private: using openmrn_arduino::Esp32Ledc; -#endif // _DRIVERS_ESP32LEDC_HXX_ +#endif // _DRIVERS_ESP32LEDC_HXX_ \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32WiFiManager.cxx b/src/freertos_drivers/esp32/Esp32WiFiManager.cxx index 5471345f8..839baa3ba 100644 --- a/src/freertos_drivers/esp32/Esp32WiFiManager.cxx +++ b/src/freertos_drivers/esp32/Esp32WiFiManager.cxx @@ -1759,4 +1759,4 @@ const char *gai_strerror(int __ecode) } } -#endif // ESP32 +#endif // ESP_PLATFORM diff --git a/src/utils/Uninitialized.hxx b/src/utils/Uninitialized.hxx index 478ea9a50..20eb49e20 100644 --- a/src/utils/Uninitialized.hxx +++ b/src/utils/Uninitialized.hxx @@ -34,6 +34,7 @@ #ifndef _UTILS_UNINITIALIZED_HXX_ #define _UTILS_UNINITIALIZED_HXX_ +#include #include /// Template class that allows allocating storage for an object but not calling @@ -120,7 +121,14 @@ public: } private: +#if __cplusplus >= 202002L + // std::aligned_storage is deprecated in C++23 + // NOTE: not using 202302L above due to ESP-IDF v5.2 packaging GCC 13.2.0 + // which did not use this value for C++23. + alignas(T) std::array data; +#else typename std::aligned_storage::type data; +#endif /// @return the embedded object (mutable pointer) constexpr T *tptrm() const From 9b17be37942eb5b04cf375f414097d4024efe57b Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 07:25:49 -0800 Subject: [PATCH 02/10] Bump arduino-esp32 to v3.1.1 (IDF v5.3 based) --- .github/workflows/ArduinoBuild.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ArduinoBuild.yml b/.github/workflows/ArduinoBuild.yml index 301e2b1a5..666b75842 100644 --- a/.github/workflows/ArduinoBuild.yml +++ b/.github/workflows/ArduinoBuild.yml @@ -64,7 +64,7 @@ jobs: sketch-names: ESP32CanLoadTest.ino,ESP32IOBoard.ino,ESP32SerialBridge.ino,ESP32WifiCanBridge.ino debug-compile: true required-libraries: OpenMRNLite - arduino-platform: esp32:esp32@2.0.17 + arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32' }} - name: Compile ESP32-C3 examples @@ -75,7 +75,7 @@ jobs: sketch-names: ESP32C3CanLoadTest.ino,ESP32C3IOBoard.ino debug-compile: true required-libraries: OpenMRNLite - arduino-platform: esp32:esp32@2.0.17 + arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32c3' }} - name: Compile ESP32-S2 examples @@ -86,5 +86,5 @@ jobs: sketch-names: ESP32S2CanLoadTest.ino,ESP32S2IOBoard.ino debug-compile: true required-libraries: OpenMRNLite - arduino-platform: esp32:esp32@2.0.17 + arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32s2' }} From 28c28d86ec2301c518daa87138f97f5bd19aecd5 Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 07:39:29 -0800 Subject: [PATCH 03/10] Fixing CanLoadTest sketches for arduino-esp32 3.x --- .../ESP32C3CanLoadTest/ESP32C3CanLoadTest.ino | 12 +++---- .../ESP32CanLoadTest/ESP32CanLoadTest.ino | 34 +++++++++++++------ .../ESP32S2CanLoadTest/ESP32S2CanLoadTest.ino | 12 ++++--- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/arduino/examples/ESP32C3CanLoadTest/ESP32C3CanLoadTest.ino b/arduino/examples/ESP32C3CanLoadTest/ESP32C3CanLoadTest.ino index b6f4cc297..951dcff09 100644 --- a/arduino/examples/ESP32C3CanLoadTest/ESP32C3CanLoadTest.ino +++ b/arduino/examples/ESP32C3CanLoadTest/ESP32C3CanLoadTest.ino @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -219,15 +220,14 @@ void setup() // Register hardware timer zero to use a 1Mhz resolution and to count up // from zero when the timer triggers. - auto timer = timerBegin(0, 80, true); + auto timer = timerBegin(80); // Attach our callback function to be called when the timer is ready to - // fire. Note that the edge parameter is not used/supported on the - // ESP32-C3. - timerAttachInterrupt(timer, record_cpu_usage, true); + // fire. + timerAttachInterrupt(timer, &record_cpu_usage); // Configure the trigger point to be roughly 163 times per second. - timerAlarmWrite(timer, 1000000/163, true); + timerWrite(timer, 1000000/163); // Enable the timer. - timerAlarmEnable(timer); + timerStart(timer); // Initialize the SPIFFS filesystem as our persistence layer if (!SPIFFS.begin()) diff --git a/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino b/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino index 4017be7ed..31529d13d 100644 --- a/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino +++ b/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -197,17 +198,23 @@ namespace openlcb } CpuLoad cpu_load; -hw_timer_t * timer = nullptr; CpuLoadLog* cpu_log = nullptr; -void IRAM_ATTR onTimer() +// Callback function for the hardware timer configured to fire roughly 163 +// times per second. +void ARDUINO_ISR_ATTR record_cpu_usage() { - if (spi_flash_cache_enabled()) +#if CONFIG_ARDUINO_ISR_IRAM + // if the ISR is called with flash disabled we can not safely recored the + // cpu usage. + if (!spi_flash_cache_enabled()) { - // Retrieves the vtable pointer from the currently running executable. - unsigned *pp = (unsigned *)openmrn.stack()->executor()->current(); - cpuload_tick(pp ? pp[0] | 1 : 0); + return; } +#endif + // Retrieves the vtable pointer from the currently running executable. + unsigned *pp = (unsigned *)openmrn.stack()->executor()->current(); + cpuload_tick(pp ? pp[0] | 1 : 0); } void setup() @@ -217,11 +224,16 @@ void setup() #endif Serial.begin(115200L); - timer = timerBegin(3, 80, true); // timer_id = 3; divider=80; countUp = true; - timerAttachInterrupt(timer, &onTimer, true); // edge = true - // 1MHz clock, 163 ticks per second desired. - timerAlarmWrite(timer, 1000000/163, true); - timerAlarmEnable(timer); + // Register hardware timer zero to use a 1Mhz resolution and to count up + // from zero when the timer triggers. + auto timer = timerBegin(80); + // Attach our callback function to be called when the timer is ready to + // fire. + timerAttachInterrupt(timer, &record_cpu_usage); + // Configure the trigger point to be roughly 163 times per second. + timerWrite(timer, 1000000/163); + // Enable the timer. + timerStart(timer); // Initialize the SPIFFS filesystem as our persistence layer if (!SPIFFS.begin()) diff --git a/arduino/examples/ESP32S2CanLoadTest/ESP32S2CanLoadTest.ino b/arduino/examples/ESP32S2CanLoadTest/ESP32S2CanLoadTest.ino index 076519176..56e53a8a5 100644 --- a/arduino/examples/ESP32S2CanLoadTest/ESP32S2CanLoadTest.ino +++ b/arduino/examples/ESP32S2CanLoadTest/ESP32S2CanLoadTest.ino @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -267,13 +268,14 @@ void setup() // Register hardware timer zero to use a 1Mhz resolution and to count up // from zero when the timer triggers. - auto timer = timerBegin(0, 80, true); - // Attach our callback function to be called on the timer edge signal. - timerAttachInterrupt(timer, record_cpu_usage, true); + auto timer = timerBegin(80); + // Attach our callback function to be called when the timer is ready to + // fire. + timerAttachInterrupt(timer, &record_cpu_usage); // Configure the trigger point to be roughly 163 times per second. - timerAlarmWrite(timer, 1000000/163, true); + timerWrite(timer, 1000000/163); // Enable the timer. - timerAlarmEnable(timer); + timerStart(timer); // Initialize the SPIFFS filesystem as our persistence layer if (!SPIFFS.begin()) From 13cef2476566b91347f9c41c2bad1d3a878af4ca Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 07:48:41 -0800 Subject: [PATCH 04/10] Add pthread include for ledc --- src/freertos_drivers/esp32/Esp32Ledc.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/freertos_drivers/esp32/Esp32Ledc.cxx b/src/freertos_drivers/esp32/Esp32Ledc.cxx index 7dfa35b83..558147a43 100644 --- a/src/freertos_drivers/esp32/Esp32Ledc.cxx +++ b/src/freertos_drivers/esp32/Esp32Ledc.cxx @@ -36,6 +36,7 @@ #if defined(ESP_PLATFORM) #include "Esp32Ledc.hxx" +#include namespace openmrn_arduino { From e218933714b4b9246fe0d15c3fd3b4ad669d8be2 Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 08:18:39 -0800 Subject: [PATCH 05/10] Update arduino build - Switch to using custom version of library locally generated and moved into place on the runner to avoid cached copies. --- .github/workflows/ArduinoBuild.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ArduinoBuild.yml b/.github/workflows/ArduinoBuild.yml index 666b75842..9dcad37ef 100644 --- a/.github/workflows/ArduinoBuild.yml +++ b/.github/workflows/ArduinoBuild.yml @@ -25,8 +25,8 @@ jobs: - name: Generate OpenMRNLite library run: | - mkdir --parents $HOME/Arduino/libraries/OpenMRNLite - $GITHUB_WORKSPACE/arduino/libify.sh $HOME/Arduino/libraries/OpenMRNLite $GITHUB_WORKSPACE -f + mkdir --parents $GITHUB_WORKSPACE/CustomOpenMRNLite + $GITHUB_WORKSPACE/arduino/libify.sh $GITHUB_WORKSPACE/CustomOpenMRNLite $GITHUB_WORKSPACE -f rm -f $GITHUB_WORKSPACE/arduino/examples/Stm*/build_opt.h - name: Compile STM32 examples @@ -37,7 +37,6 @@ jobs: sketch-names: Stm32*.ino build-properties: '{ "All": "-DHAL_CAN_MODULE_ENABLED" }' debug-compile: true - required-libraries: OpenMRNLite build-esp32: name: Build Arduino ${{ matrix.target }} examples @@ -52,8 +51,8 @@ jobs: - name: Generate OpenMRNLite library run: | - mkdir --parents $HOME/Arduino/libraries/OpenMRNLite - $GITHUB_WORKSPACE/arduino/libify.sh $HOME/Arduino/libraries/OpenMRNLite $GITHUB_WORKSPACE -f + mkdir --parents $GITHUB_WORKSPACE/CustomOpenMRNLite + $GITHUB_WORKSPACE/arduino/libify.sh $GITHUB_WORKSPACE/CustomOpenMRNLite $GITHUB_WORKSPACE -f rm -f $GITHUB_WORKSPACE/arduino/examples/Stm*/build_opt.h - name: Compile ESP32 examples @@ -63,7 +62,6 @@ jobs: arduino-board-fqbn: esp32:esp32:node32s sketch-names: ESP32CanLoadTest.ino,ESP32IOBoard.ino,ESP32SerialBridge.ino,ESP32WifiCanBridge.ino debug-compile: true - required-libraries: OpenMRNLite arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32' }} @@ -74,7 +72,6 @@ jobs: arduino-board-fqbn: esp32:esp32:esp32c3 sketch-names: ESP32C3CanLoadTest.ino,ESP32C3IOBoard.ino debug-compile: true - required-libraries: OpenMRNLite arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32c3' }} @@ -85,6 +82,5 @@ jobs: arduino-board-fqbn: esp32:esp32:esp32s2 sketch-names: ESP32S2CanLoadTest.ino,ESP32S2IOBoard.ino debug-compile: true - required-libraries: OpenMRNLite arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32s2' }} From fb21cf324b0dbd79e10aa1a69d73ddb9d0f38b7f Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 08:23:29 -0800 Subject: [PATCH 06/10] adjust libify.sh to copy src/ble/* --- arduino/libify.sh | 2 ++ arduino/library.json | 1 + 2 files changed, 3 insertions(+) diff --git a/arduino/libify.sh b/arduino/libify.sh index 3b327b30f..a31081366 100755 --- a/arduino/libify.sh +++ b/arduino/libify.sh @@ -232,6 +232,8 @@ copy_file src/sys include/sys/tree.hxx copy_file src/utils src/utils/*.{cxx,hxx,c,h} +copy_file src/ble src/ble/*.{cxx,hxx} + rm -f ${TARGET_LIB_DIR}/src/utils/ReflashBootloader.cxx \ ${TARGET_LIB_DIR}/src/utils/AesCcmTestVectors.hxx \ ${TARGET_LIB_DIR}/src/utils/AesCcmTestVectorsEx.hxx \ diff --git a/arduino/library.json b/arduino/library.json index c6d1c12a6..c5a72321e 100644 --- a/arduino/library.json +++ b/arduino/library.json @@ -30,6 +30,7 @@ "-<.git/>", "-", "+<*>", + "+", "+", "+", "+", From 7f6b5e4967120adaa4eb5ae4f87b98aa6022d1b2 Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 08:29:11 -0800 Subject: [PATCH 07/10] Disable DirectHub.cxx compilation without BSD sockets and fix missing ) in Esp32Gpio.hxx --- src/freertos_drivers/esp32/Esp32Gpio.hxx | 2 +- src/utils/DirectHub.cxx | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/freertos_drivers/esp32/Esp32Gpio.hxx b/src/freertos_drivers/esp32/Esp32Gpio.hxx index f051604ee..b597d8eb4 100644 --- a/src/freertos_drivers/esp32/Esp32Gpio.hxx +++ b/src/freertos_drivers/esp32/Esp32Gpio.hxx @@ -59,7 +59,7 @@ /// This is necessary since ESP-IDF does not expose gpio_get_direction(pin). #define IS_GPIO_OUTPUT(pin) (GPIO_IS_VALID_OUTPUT_GPIO(pin) && \ GPIO.enable.data & \ - BIT(pin & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) + BIT(pin & SOC_GPIO_VALID_OUTPUT_GPIO_MASK)) #else // NOT ESP32-C3 /// Helper macro to test if a pin has been configured for output. /// diff --git a/src/utils/DirectHub.cxx b/src/utils/DirectHub.cxx index af1329b9f..1951ea70f 100644 --- a/src/utils/DirectHub.cxx +++ b/src/utils/DirectHub.cxx @@ -36,6 +36,10 @@ // #define LOGLEVEL VERBOSE +#include "openmrn_features.h" + +#if OPENMRN_FEATURE_BSD_SOCKETS + #include "utils/DirectHub.hxx" #include @@ -946,3 +950,5 @@ void create_direct_gc_tcp_hub(DirectHubInterface *hub, int port) { new DirectGcTcpHub(hub, port); } + +#endif // OPENMRN_FEATURE_BSD_SOCKETS \ No newline at end of file From a30ca921c80d7fc70e785ce986f6cffe45b7cb5b Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 08:53:00 -0800 Subject: [PATCH 08/10] Remove ESP32SerialBridge as it is no longer supported. including temporary fix for arduino build issue --- .github/workflows/ArduinoBuild.yml | 2 +- arduino/OpenMRNLite.cpp | 26 +++ arduino/examples/ESP32SerialBridge/.gitignore | 1 - .../ESP32SerialBridge/ESP32SerialBridge.ino | 175 ------------------ arduino/examples/ESP32SerialBridge/config.h | 60 ------ .../ESP32WifiCanBridge/ESP32WifiCanBridge.ino | 18 +- 6 files changed, 39 insertions(+), 243 deletions(-) delete mode 100644 arduino/examples/ESP32SerialBridge/.gitignore delete mode 100644 arduino/examples/ESP32SerialBridge/ESP32SerialBridge.ino delete mode 100644 arduino/examples/ESP32SerialBridge/config.h diff --git a/.github/workflows/ArduinoBuild.yml b/.github/workflows/ArduinoBuild.yml index 9dcad37ef..5018ffb01 100644 --- a/.github/workflows/ArduinoBuild.yml +++ b/.github/workflows/ArduinoBuild.yml @@ -60,7 +60,7 @@ jobs: with: platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json arduino-board-fqbn: esp32:esp32:node32s - sketch-names: ESP32CanLoadTest.ino,ESP32IOBoard.ino,ESP32SerialBridge.ino,ESP32WifiCanBridge.ino + sketch-names: ESP32CanLoadTest.ino,ESP32IOBoard.ino,ESP32WifiCanBridge.ino debug-compile: true arduino-platform: esp32:esp32@3.1.1 if: ${{ matrix.target == 'esp32' }} diff --git a/arduino/OpenMRNLite.cpp b/arduino/OpenMRNLite.cpp index d1968a199..834266123 100644 --- a/arduino/OpenMRNLite.cpp +++ b/arduino/OpenMRNLite.cpp @@ -65,3 +65,29 @@ ssize_t os_get_free_heap() #endif // ESP_PLATFORM } // namespace openmrn_arduino + +#ifdef ESP_PLATFORM + +/* +temporary hack due to: + +/home/runner/.cache/arduino/cores/esp32_esp32_esp32c3_e60f223ab547c7c01b5bb8c151783c5f/core.a @/home/runner/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/flags/ld_libs -Wl,--end-group -Wl,-EL -o /home/runner/.cache/arduino/sketches/1429F777C89502EA84E469B35328AFC7/ESP32C3IOBoard.ino.elf +/home/runner/.arduino15/packages/esp32/tools/esp-rv32/2405/bin/../lib/gcc/riscv32-esp-elf/13.2.0/../../../../riscv32-esp-elf/bin/ld: /home/runner/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/lib/liblwip.a(ip6.c.obj): in function `ip6_input': +/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/core/ipv6/ip6.c:1127:(.text.ip6_input+0xa2): undefined reference to `lwip_hook_ip6_input' +collect2: error: ld returned 1 exit status + +tracked as https://github.com/espressif/arduino-esp32/issues/10084 +below snippet was copied from the issue and must be placed in global namespace +*/ +extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) __attribute__((weak)); +extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) +{ + if (ip6_addr_isany_val(inp->ip6_addr[0].u_addr.ip6)) + { + // We don't have an LL address -> eat this packet here, so it won't get accepted on input netif + pbuf_free(p); + return 1; + } + return 0; +} +#endif diff --git a/arduino/examples/ESP32SerialBridge/.gitignore b/arduino/examples/ESP32SerialBridge/.gitignore deleted file mode 100644 index 70cfab999..000000000 --- a/arduino/examples/ESP32SerialBridge/.gitignore +++ /dev/null @@ -1 +0,0 @@ -wifi_params.cpp diff --git a/arduino/examples/ESP32SerialBridge/ESP32SerialBridge.ino b/arduino/examples/ESP32SerialBridge/ESP32SerialBridge.ino deleted file mode 100644 index 870457596..000000000 --- a/arduino/examples/ESP32SerialBridge/ESP32SerialBridge.ino +++ /dev/null @@ -1,175 +0,0 @@ -/** \copyright - * Copyright (c) 2019, Mike Dunston - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * \file ESP32SerialBridge.ino - * - * Example application for the ESP32 showing how to configure a Serial bridge - * GridConnect client adapter to the OpenMRN stack. - * - * @author Mike Dunston - * @date 13 January 2019 - */ - -#include -#include -#include - -#include "config.h" - -// uncomment the line below to have all packets printed to the Serial -// output. This is not recommended for production deployment. -//#define PRINT_PACKETS - -/// This is the speed used for UART0 AND UART1 on the ESP32. -/// UART0 is primarily for debug/log messages. -/// UART1 is used as the bridge device. -constexpr uint32_t SERIAL_BAUD = 115200L; - -/// This is the pin to use for UART1 RX, this can not be the same as the CAN_RX_PIN below. -/// Note: Any pin can be used for this other than 6-11 which are connected to -/// the onboard flash. -constexpr uint8_t SERIAL_RX_PIN = 16; - -/// This is the pin to use for UART1 TX, this can not be the same as the CAN_TX_PIN below. -/// Note: Any pin can be used for this other than 6-11 which are connected to -/// the onboard flash and 34-39 which are input only. -constexpr uint8_t SERIAL_TX_PIN = 17; - -/// This is the ESP32 pin connected to the SN65HVD23x/MCP2551 R (RX) pin. -/// Recommended pins: 4, 16, 21. -/// Note: Any pin can be used for this other than 6-11 which are connected to -/// the onboard flash. -constexpr gpio_num_t CAN_RX_PIN = GPIO_NUM_4; - -/// This is the ESP32 pin connected to the SN65HVD23x/MCP2551 D (TX) pin. -/// Recommended pins: 5, 17, 22. -/// Note: Any pin can be used for this other than 6-11 which are connected to -/// the onboard flash and 34-39 which are input only. -constexpr gpio_num_t CAN_TX_PIN = GPIO_NUM_5; - -/// This is the node id to assign to this device, this must be unique -/// on the CAN bus. -static constexpr uint64_t NODE_ID = UINT64_C(0x050101011829); - -/// This is the primary entrypoint for the OpenMRN/LCC stack. -OpenMRN openmrn(NODE_ID); - -// note the dummy string below is required due to a bug in the GCC compiler -// for the ESP32 -string dummystring("abcdef"); - -// ConfigDef comes from config.hxx and is specific to the particular device and -// target. It defines the layout of the configuration memory space and is also -// used to generate the cdi.xml file. Here we instantiate the configuration -// layout. The argument of offset zero is ignored and will be removed later. -static constexpr openlcb::ConfigDef cfg(0); - -class FactoryResetHelper : public DefaultConfigUpdateListener { -public: - UpdateAction apply_configuration(int fd, bool initial_load, - BarrierNotifiable *done) OVERRIDE { - AutoNotify n(done); - return UPDATED; - } - - void factory_reset(int fd) override - { - cfg.userinfo().name().write(fd, openlcb::SNIP_STATIC_DATA.model_name); - cfg.userinfo().description().write( - fd, "OpenLCB + Arduino-ESP32 on an ESP32."); - } -} factory_reset_helper; - -namespace openlcb { - // Name of CDI.xml to generate dynamically. - const char CDI_FILENAME[] = "/spiffs/cdi.xml"; - - // This will stop openlcb from exporting the CDI memory space upon start. - const char CDI_DATA[] = ""; - - // Path to where OpenMRN should persist general configuration data. - const char *const CONFIG_FILENAME = "/spiffs/openlcb_config"; - - // The size of the memory space to export over the above device. - const size_t CONFIG_FILE_SIZE = cfg.seg().size() + cfg.seg().offset(); - - // Default to store the dynamic SNIP data is stored in the same persistant - // data file as general configuration data. - const char *const SNIP_DYNAMIC_FILENAME = CONFIG_FILENAME; -} - -void setup() { - Serial.begin(SERIAL_BAUD); - Serial1.begin(SERIAL_BAUD, SERIAL_8N1, SERIAL_RX_PIN, SERIAL_TX_PIN); - - // Initialize the SPIFFS filesystem as our persistence layer - if(!SPIFFS.begin()) - { - printf("SPIFFS failed to mount, attempting to format and remount\n"); - if(!SPIFFS.begin(true)) - { - printf("SPIFFS mount failed even with format, giving up!\n"); - while(1) - { - // Unable to start SPIFFS successfully, give up and wait - // for WDT to kick in - } - } - } - - printf("\nSerial(rx:%d, tx:%d, speed:%d) is ready to exchange grid connect packets.\n", - SERIAL_TX_PIN, SERIAL_TX_PIN, SERIAL_BAUD); - - // Create the CDI.xml dynamically - openmrn.create_config_descriptor_xml(cfg, openlcb::CDI_FILENAME); - - // Create the default internal configuration file - openmrn.stack()->create_config_file_if_needed(cfg.seg().internal_config(), - openlcb::CANONICAL_VERSION, openlcb::CONFIG_FILE_SIZE); - - // Start the OpenMRN stack - openmrn.begin(); - -#if defined(PRINT_PACKETS) - // Dump all packets as they are sent/received. - // Note: This should not be enabled in deployed nodes as it will - // have performance impact. - openmrn.stack()->print_all_packets(); -#endif // PRINT_PACKETS - - // Add Serial1 as a bridge - openmrn.add_gridconnect_port(&Serial1); - - // Add the hardware CAN device as a bridge - openmrn.add_can_port( - new Esp32HardwareCan("esp32can", CAN_RX_PIN, CAN_TX_PIN)); -} - -void loop() { - // Call the OpenMRN executor, this needs to be done as often - // as possible from the loop() method. - openmrn.loop(); -} diff --git a/arduino/examples/ESP32SerialBridge/config.h b/arduino/examples/ESP32SerialBridge/config.h deleted file mode 100644 index 6df063fa3..000000000 --- a/arduino/examples/ESP32SerialBridge/config.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _ARDUINO_EXAMPLE_ESP32SERIALBRIDGE_CONFIG_H_ -#define _ARDUINO_EXAMPLE_ESP32SERIALBRIDGE_CONFIG_H_ - -#include "openlcb/ConfiguredConsumer.hxx" -#include "openlcb/ConfiguredProducer.hxx" -#include "openlcb/ConfigRepresentation.hxx" -#include "openlcb/MemoryConfig.hxx" - -namespace openlcb -{ - -/// Defines the identification information for the node. The arguments are: -/// -/// - 4 (version info, always 4 by the standard -/// - Manufacturer name -/// - Model name -/// - Hardware version -/// - Software version -/// -/// This data will be used for all purposes of the identification: -/// -/// - the generated cdi.xml will include this data -/// - the Simple Node Ident Info Protocol will return this data -/// - the ACDI memory space will contain this data. -extern const SimpleNodeStaticValues SNIP_STATIC_DATA = { - 4, - "OpenMRN", - "Arduino Serial Bridge", - ARDUINO_VARIANT, - "1.00"}; - -/// Modify this value every time the EEPROM needs to be cleared on the node -/// after an update. -static constexpr uint16_t CANONICAL_VERSION = 0x1009; - -/// Defines the main segment in the configuration CDI. This is laid out at -/// origin 128 to give space for the ACDI user data at the beginning. -CDI_GROUP(IoBoardSegment, Segment(MemoryConfigDefs::SPACE_CONFIG), Offset(128)); -/// Each entry declares the name of the current entry, then the type and then -/// optional arguments list. -CDI_GROUP_ENTRY(internal_config, InternalConfigData); -CDI_GROUP_END(); - -/// The main structure of the CDI. ConfigDef is the symbol we use in main.cxx -/// to refer to the configuration defined here. -CDI_GROUP(ConfigDef, MainCdi()); -/// Adds the tag with the values from SNIP_STATIC_DATA above. -CDI_GROUP_ENTRY(ident, Identification); -/// Adds an tag. -CDI_GROUP_ENTRY(acdi, Acdi); -/// Adds a segment for changing the values in the ACDI user-defined -/// space. UserInfoSegment is defined in the system header. -CDI_GROUP_ENTRY(userinfo, UserInfoSegment, Name("User Info")); -/// Adds the main configuration segment. -CDI_GROUP_ENTRY(seg, IoBoardSegment, Name("Settings")); -CDI_GROUP_END(); - -} // namespace openlcb - -#endif // _ARDUINO_EXAMPLE_ESP32SERIALBRIDGE_CONFIG_H_ diff --git a/arduino/examples/ESP32WifiCanBridge/ESP32WifiCanBridge.ino b/arduino/examples/ESP32WifiCanBridge/ESP32WifiCanBridge.ino index b09e94bbc..ab5138c50 100644 --- a/arduino/examples/ESP32WifiCanBridge/ESP32WifiCanBridge.ino +++ b/arduino/examples/ESP32WifiCanBridge/ESP32WifiCanBridge.ino @@ -106,10 +106,15 @@ static constexpr openlcb::ConfigDef cfg(0); Esp32WiFiManager wifi_mgr(ssid, password, openmrn.stack(), cfg.seg().wifi()); -class FactoryResetHelper : public DefaultConfigUpdateListener { +// @ref Esp32HardwareTwai instance to use for this node. +static Esp32HardwareTwai twai(CAN_RX_PIN, CAN_TX_PIN); + +class FactoryResetHelper : public DefaultConfigUpdateListener +{ public: UpdateAction apply_configuration(int fd, bool initial_load, - BarrierNotifiable *done) OVERRIDE { + BarrierNotifiable *done) OVERRIDE + { AutoNotify n(done); return UPDATED; } @@ -167,6 +172,11 @@ void setup() openmrn.stack()->create_config_file_if_needed(cfg.seg().internal_config(), openlcb::CANONICAL_VERSION, openlcb::CONFIG_FILE_SIZE); + // Initialize the TWAI peripheral + twai.hw_init(); + // Add TWAI port to the stack + openmrn.add_can_port_select("/dev/twai/twai0"); + // Start the OpenMRN stack openmrn.begin(); openmrn.start_executor_thread(); @@ -177,10 +187,6 @@ void setup() // have performance impact. openmrn.stack()->print_all_packets(); #endif // PRINT_PACKETS - - // Add the hardware CAN device as a bridge - openmrn.add_can_port( - new Esp32HardwareCan("esp32can", CAN_RX_PIN, CAN_TX_PIN)); } void loop() From f6dafb5f109aff02f73e2d5b343ad34193a31370 Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 08:57:33 -0800 Subject: [PATCH 09/10] add missing includes --- arduino/OpenMRNLite.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arduino/OpenMRNLite.cpp b/arduino/OpenMRNLite.cpp index 834266123..1ae29d70e 100644 --- a/arduino/OpenMRNLite.cpp +++ b/arduino/OpenMRNLite.cpp @@ -68,6 +68,10 @@ ssize_t os_get_free_heap() #ifdef ESP_PLATFORM +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" + /* temporary hack due to: From 33d7fb4a5fd765e516f288916b95946a1e761f37 Mon Sep 17 00:00:00 2001 From: Mike Dunston Date: Sun, 26 Jan 2025 09:07:16 -0800 Subject: [PATCH 10/10] drop remaining references to USE_CAN --- arduino/examples/ESP32C3CanLoadTest/config.h | 2 +- .../ESP32CanLoadTest/ESP32CanLoadTest.ino | 24 +++---------- .../examples/ESP32IOBoard/ESP32IOBoard.ino | 35 ++++++------------- arduino/examples/ESP32IOBoard/config.h | 10 ++---- arduino/examples/ESP32S2CanLoadTest/config.h | 2 +- .../ESP32S2IOBoard/ESP32S2IOBoard.ino | 11 +++--- 6 files changed, 24 insertions(+), 60 deletions(-) diff --git a/arduino/examples/ESP32C3CanLoadTest/config.h b/arduino/examples/ESP32C3CanLoadTest/config.h index 40913fdc6..967066535 100644 --- a/arduino/examples/ESP32C3CanLoadTest/config.h +++ b/arduino/examples/ESP32C3CanLoadTest/config.h @@ -10,7 +10,7 @@ // catch invalid configuration at compile time #if !defined(USE_TWAI) && !defined(USE_WIFI) -#error "Invalid configuration detected, USE_CAN or USE_WIFI must be defined." +#error "Invalid configuration detected, USE_TWAI or USE_WIFI must be defined." #endif namespace openlcb diff --git a/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino b/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino index 31529d13d..aa208e9eb 100644 --- a/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino +++ b/arduino/examples/ESP32CanLoadTest/ESP32CanLoadTest.ino @@ -46,20 +46,13 @@ // Pick an operating mode below, if you select USE_WIFI it will expose this -// node on WIFI. If USE_CAN / USE_TWAI / USE_TWAI_ASYNC are enabled the node +// node on WIFI. If USE_TWAI / USE_TWAI_ASYNC are enabled the node // will be available on CAN. // // Enabling both options will allow the ESP32 to be accessible from -// both WiFi and CAN interfaces. -// -// NOTE: USE_TWAI and USE_TWAI_ASYNC are similar to USE_CAN but utilize the -// new TWAI driver which offers both select() (default) or fnctl() (async) -// access. -// NOTE: USE_CAN is deprecated and no longer supported upstream by ESP-IDF as -// of v4.2 or arduino-esp32 as of v2.0.0. +// both WiFi and TWAI interfaces. #define USE_WIFI -//#define USE_CAN //#define USE_TWAI //#define USE_TWAI_ASYNC @@ -74,11 +67,6 @@ #define USE_TWAI #endif // USE_TWAI_ASYNC && !USE_TWAI -// Verify that both CAN and TWAI are not enabled. -#if defined(USE_CAN) && defined(USE_TWAI) -#error Enabling both USE_CAN and USE_TWAI is not supported. -#endif // USE_CAN && USE_TWAI - #if defined(USE_TWAI) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0) #error Esp32HardwareTwai is not supported on this version of arduino-esp32. #endif // USE_TWAI && IDF < v4.3 @@ -273,17 +261,13 @@ void setup() openmrn.stack()->print_all_packets(); #endif // PRINT_PACKETS -#if defined(USE_CAN) - // Add the hardware CAN device as a bridge - openmrn.add_can_port( - new Esp32HardwareCan("esp32can", CAN_RX_PIN, CAN_TX_PIN)); -#elif defined(USE_TWAI_ASYNC) +#if defined(USE_TWAI_ASYNC) // add TWAI driver with non-blocking usage openmrn.add_can_port_async("/dev/twai/twai0"); #elif defined(USE_TWAI) // add TWAI driver with select() usage openmrn.add_can_port_select("/dev/twai/twai0"); -#endif // USE_TWAI_SELECT / USE_TWAI +#endif // USE_TWAI_ASYNC / USE_TWAI } void loop() diff --git a/arduino/examples/ESP32IOBoard/ESP32IOBoard.ino b/arduino/examples/ESP32IOBoard/ESP32IOBoard.ino index c1d65a3e3..bd8bb0ea8 100644 --- a/arduino/examples/ESP32IOBoard/ESP32IOBoard.ino +++ b/arduino/examples/ESP32IOBoard/ESP32IOBoard.ino @@ -42,21 +42,14 @@ #include "utils/GpioInitializer.hxx" // Pick an operating mode below, if you select USE_WIFI it will expose this -// node on WIFI. If USE_CAN / USE_TWAI / USE_TWAI_ASYNC are enabled the node +// node on WIFI. If USE_TWAI / USE_TWAI_ASYNC are enabled the node // will be available on CAN. // // Enabling both options will allow the ESP32 to be accessible from // both WiFi and CAN interfaces. -// -// NOTE: USE_TWAI and USE_TWAI_ASYNC are similar to USE_CAN but utilize the -// new TWAI driver which offers both select() (default) or fnctl() (async) -// access. -// NOTE: USE_CAN is deprecated and no longer supported upstream by ESP-IDF as -// of v4.2 or arduino-esp32 as of v2.0.0. #define USE_WIFI -#define USE_CAN -//#define USE_TWAI +#define USE_TWAI //#define USE_TWAI_ASYNC // uncomment the line below to have all packets printed to the Serial @@ -85,10 +78,6 @@ #define USE_TWAI #endif // USE_TWAI_ASYNC && !USE_TWAI -#if defined(USE_CAN) && defined(USE_TWAI) -#error USE_CAN and USE_TWAI are mutually exclusive! -#endif - #if defined(FIRMWARE_UPDATE_BOOTLOADER) && !defined(USE_TWAI) #error FIRMWARE_UPDATE_BOOTLOADER requires USE_TWAI or USE_TWAI_SELECT. #endif @@ -129,7 +118,7 @@ const char *hostname = "esp32mrn"; #endif // USE_WIFI -#if defined(USE_CAN) || defined(USE_TWAI) +#if defined(USE_TWAI) /// This is the ESP32 pin connected to the SN65HVD23x/MCP2551 R (RX) pin. /// Recommended pins: 4, 16, 21. /// Note: Any pin can be used for this other than 6-11 which are connected to @@ -146,7 +135,7 @@ constexpr gpio_num_t CAN_RX_PIN = GPIO_NUM_4; /// the GPIO pin definitions for the outputs. constexpr gpio_num_t CAN_TX_PIN = GPIO_NUM_5; -#endif // USE_CAN || USE_TWAI +#endif // USE_TWAI #if defined(FIRMWARE_UPDATE_BOOTLOADER) // Include the Bootloader HAL implementation for the ESP32. This should only @@ -171,9 +160,9 @@ static constexpr openlcb::ConfigDef cfg(0); Esp32WiFiManager wifi_mgr(ssid, password, openmrn.stack(), cfg.seg().wifi()); #endif // USE_WIFI -#if defined(USE_TWAI) && !defined(USE_CAN) +#if defined(USE_TWAI) Esp32HardwareTwai twai(CAN_RX_PIN, CAN_TX_PIN); -#endif // USE_TWAI && !USE_CAN +#endif // USE_TWAI // Declare output pins // NOTE: pins 6-11 are connected to the onboard flash and can not be used for @@ -381,9 +370,9 @@ void setup() } #endif // FIRMWARE_UPDATE_BOOTLOADER -#if defined(USE_TWAI) && !defined(USE_CAN) +#if defined(USE_TWAI) twai.hw_init(); -#endif // USE_TWAI && !USE_CAN +#endif // USE_TWAI check_for_factory_reset(); @@ -410,15 +399,11 @@ void setup() openmrn.stack()->print_all_packets(); #endif // PRINT_PACKETS -#if defined(USE_CAN) - // Add the hardware CAN device as a bridge - openmrn.add_can_port( - new Esp32HardwareCan("esp32can", CAN_RX_PIN, CAN_TX_PIN)); -#elif defined(USE_TWAI_ASYNC) +#if defined(USE_TWAI_ASYNC) openmrn.add_can_port_async("/dev/twai/twai0"); #elif defined(USE_TWAI) openmrn.add_can_port_select("/dev/twai/twai0"); -#endif // USE_CAN +#endif // USE_TWAI_ASYNC openmrn.start_executor_thread(); } diff --git a/arduino/examples/ESP32IOBoard/config.h b/arduino/examples/ESP32IOBoard/config.h index b8e292a27..57fa4f923 100644 --- a/arduino/examples/ESP32IOBoard/config.h +++ b/arduino/examples/ESP32IOBoard/config.h @@ -9,8 +9,8 @@ #include "freertos_drivers/esp32/Esp32WiFiConfiguration.hxx" // catch invalid configuration at compile time -#if !defined(USE_CAN) && !defined(USE_WIFI) -#error "Invalid configuration detected, USE_CAN or USE_WIFI must be defined." +#if !defined(USE_TWAI) && !defined(USE_WIFI) +#error "Invalid configuration detected, USE_TWAI or USE_WIFI must be defined." #endif namespace openlcb @@ -32,14 +32,10 @@ namespace openlcb extern const SimpleNodeStaticValues SNIP_STATIC_DATA = { 4, "OpenMRN", -#if defined(USE_WIFI) && !defined(USE_CAN) && !defined(USE_TWAI) +#if defined(USE_WIFI) && !defined(USE_TWAI) "Arduino IO Board (WiFi)", -#elif defined(USE_CAN) && !defined(USE_WIFI) - "Arduino IO Board (CAN)", #elif defined(USE_TWAI) && !defined(USE_WIFI) "Arduino IO Board (TWAI)", -#elif defined(USE_CAN) && defined(USE_WIFI) - "Arduino IO Board (WiFi/CAN)", #elif defined(USE_TWAI) && defined(USE_WIFI) "Arduino IO Board (WiFi/TWAI)", #else diff --git a/arduino/examples/ESP32S2CanLoadTest/config.h b/arduino/examples/ESP32S2CanLoadTest/config.h index d79df9023..8042bde47 100644 --- a/arduino/examples/ESP32S2CanLoadTest/config.h +++ b/arduino/examples/ESP32S2CanLoadTest/config.h @@ -10,7 +10,7 @@ // catch invalid configuration at compile time #if !defined(USE_TWAI) && !defined(USE_WIFI) -#error "Invalid configuration detected, USE_CAN or USE_WIFI must be defined." +#error "Invalid configuration detected, USE_TWAI or USE_WIFI must be defined." #endif namespace openlcb diff --git a/arduino/examples/ESP32S2IOBoard/ESP32S2IOBoard.ino b/arduino/examples/ESP32S2IOBoard/ESP32S2IOBoard.ino index 9ff7c3681..8f670cf0b 100644 --- a/arduino/examples/ESP32S2IOBoard/ESP32S2IOBoard.ino +++ b/arduino/examples/ESP32S2IOBoard/ESP32S2IOBoard.ino @@ -44,15 +44,14 @@ #include "utils/GpioInitializer.hxx" // Pick an operating mode below, if you select USE_WIFI it will expose this -// node on WIFI. If USE_CAN / USE_TWAI / USE_TWAI_ASYNC are enabled the node -// will be available on CAN. +// node on WIFI. If USE_TWAI / USE_TWAI_ASYNC are enabled the node will be +// available on CAN. // // Enabling both options will allow the ESP32 to be accessible from // both WiFi and CAN interfaces. // -// NOTE: USE_TWAI and USE_TWAI_ASYNC are similar to USE_CAN but utilize the -// new TWAI driver which offers both select() (default) or fnctl() (async) -// access. +// NOTE: USE_TWAI and USE_TWAI_ASYNC utilize the new TWAI driver which offers +// both select() (default) or fnctl() (async) access. #define USE_WIFI //#define USE_TWAI @@ -157,7 +156,7 @@ constexpr gpio_num_t CAN_RX_PIN = GPIO_NUM_40; /// the GPIO pin definitions for the outputs. constexpr gpio_num_t CAN_TX_PIN = GPIO_NUM_41; -#endif // USE_CAN or USE_TWAI +#endif // USE_TWAI #if defined(FACTORY_RESET_GPIO_PIN) static constexpr uint8_t FACTORY_RESET_COUNTDOWN_SECS = 10;