From 8c7cc44f37a9eed58e981e43d10400ae744f7198 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sun, 4 Feb 2024 16:12:14 +0100 Subject: [PATCH] Upintegrate changes from the OpenMRNIDF repository (#771) This PR pulls in changes that happened in the OpenMRNIDF repository. Most of these were authored by Mike Dunston. - All instances of ifdef ESP32 replaced with ESP_PLATFORM - Removed conditional code for supporting ESP-IDF v3.x and v4.x Only ESP-IDF v5 and up is now supported - new feature for OPENMRN_HAVE_SOCKET_FSTAT - fixed compile errors around spiram - refactored definition of ADC pin into a separate class in a separate file - fixes around printf PRIu32. - do not print error messages on CAN overflow - fix to not start the hub twice in the wifi manager - Remove deprecated hardware can driver - Adds I2C driver written by Mike Dunston for the ESP32. === * Upintegrate changes from OpenMRNIDF. - All instances of ifdef ESP32 replaced with ESP_PLATFORM - Removed conditional code for supporting ESP-IDF v3.x and v4.x Only ESP-IDF v5 and up is now supported - new feature for OPENMRN_HAVE_SOCKET_FSTAT - fixed compile errors around spiram - refactored definition of ADC pin. - fixes around printf PRIu32. - do not print error messages on CAN overflow - fix to not start the hub twice in the wifi manager * Fix compile error. * Remove deprecated hardware can driver. * Adds I2C driver written by Mike Dunston for the ESP32. * Adds refactored Adc Input class. * Fix comments. --- arduino/OpenMRNLite.cpp | 4 +- arduino/OpenMRNLite.h | 14 +- arduino/libify.sh | 2 +- include/can_frame.h | 2 +- include/freertos/can_ioctl.h | 2 +- include/freertos/freertos_includes.h | 54 +- include/freertos/stropts.h | 4 +- include/i2c-dev.h | 2 +- include/i2c.h | 2 +- include/openmrn_features.h | 33 +- src/executor/Executor.hxx | 4 +- src/executor/StateFlow.hxx | 13 +- src/freertos_drivers/common/libatomic.c | 2 +- .../esp32/Esp32AdcOneShot.hxx | 217 ++++++++ .../esp32/Esp32BootloaderHal.hxx | 11 +- src/freertos_drivers/esp32/Esp32Gpio.hxx | 194 +------ .../esp32/Esp32HardwareCanAdapter.hxx | 428 --------------- .../esp32/Esp32HardwareI2C.cpp | 513 ++++++++++++++++++ .../esp32/Esp32HardwareI2C.hxx | 230 ++++++++ .../esp32/Esp32HardwareTwai.cxx | 81 ++- .../esp32/Esp32HardwareTwai.hxx | 8 +- src/freertos_drivers/esp32/Esp32Ledc.cxx | 10 +- src/freertos_drivers/esp32/Esp32Ledc.hxx | 8 +- src/freertos_drivers/esp32/Esp32SocInfo.cxx | 22 +- src/freertos_drivers/esp32/Esp32SocInfo.hxx | 11 +- .../esp32/Esp32WiFiManager.cxx | 199 ++----- .../esp32/Esp32WiFiManager.hxx | 29 +- src/openlcb/ApplicationChecksum.hxx | 2 +- src/openlcb/Bootloader.hxx | 2 +- src/openlcb/BroadcastTime.cxx | 2 +- src/openlcb/ServoConsumer.hxx | 2 +- src/os/OS.hxx | 2 +- src/os/OSSelectWakeup.cxx | 85 +-- src/os/OSSelectWakeup.hxx | 29 +- src/os/os.c | 4 +- src/utils/Atomic.hxx | 2 +- src/utils/HubDevice.hxx | 5 +- src/utils/SocketClient.cxx | 10 +- src/utils/SocketClient.hxx | 3 +- src/utils/constants.cxx | 4 +- src/utils/logging.cxx | 5 +- src/utils/logging.h | 2 +- src/utils/macros.h | 11 +- src/utils/socket_listener.cxx | 5 +- src/utils/stdio_logging.h | 2 +- 45 files changed, 1219 insertions(+), 1057 deletions(-) create mode 100644 src/freertos_drivers/esp32/Esp32AdcOneShot.hxx delete mode 100644 src/freertos_drivers/esp32/Esp32HardwareCanAdapter.hxx create mode 100644 src/freertos_drivers/esp32/Esp32HardwareI2C.cpp create mode 100644 src/freertos_drivers/esp32/Esp32HardwareI2C.hxx diff --git a/arduino/OpenMRNLite.cpp b/arduino/OpenMRNLite.cpp index d38f6b283..d1968a199 100644 --- a/arduino/OpenMRNLite.cpp +++ b/arduino/OpenMRNLite.cpp @@ -43,7 +43,7 @@ OpenMRN::OpenMRN(openlcb::NodeID node_id) init(node_id); } -#ifdef ESP32 +#ifdef ESP_PLATFORM extern "C" { #ifndef OPENMRN_EXCLUDE_REBOOT_IMPL @@ -62,6 +62,6 @@ ssize_t os_get_free_heap() #endif // OPENMRN_EXCLUDE_FREE_HEAP_IMPL } -#endif // ESP32 +#endif // ESP_PLATFORM } // namespace openmrn_arduino diff --git a/arduino/OpenMRNLite.h b/arduino/OpenMRNLite.h index 87bcdbf29..ee97336b1 100644 --- a/arduino/OpenMRNLite.h +++ b/arduino/OpenMRNLite.h @@ -48,9 +48,8 @@ #include "utils/logging.h" #include "utils/Uninitialized.hxx" -#if defined(ESP32) +#if defined(ESP_PLATFORM) -#include #include #include @@ -70,8 +69,6 @@ constexpr UBaseType_t OPENMRN_TASK_PRIORITY = ESP_TASK_TCPIP_PRIO - 1; #include "freertos_drivers/esp32/Esp32Gpio.hxx" #include "freertos_drivers/esp32/Esp32SocInfo.hxx" -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) - // If we are using ESP-IDF v4.3 (or later) enable the Esp32Ledc API. #include "freertos_drivers/esp32/Esp32Ledc.hxx" @@ -92,15 +89,6 @@ constexpr UBaseType_t OPENMRN_TASK_PRIORITY = ESP_TASK_TCPIP_PRIO - 1; #endif // NOT ESP32-H2 and NOT ESP32-C2 -#endif // IDF v4.3+ - -#if defined(CONFIG_IDF_TARGET_ESP32) -// Note: This code is deprecated in favor of the TWAI interface which exposes -// both select() and fnctl() interfaces. Support for this may be removed in the -// future. -#include "freertos_drivers/esp32/Esp32HardwareCanAdapter.hxx" -#endif // ESP32 only - #include "freertos_drivers/esp32/Esp32HardwareSerialAdapter.hxx" #include "freertos_drivers/esp32/Esp32WiFiManager.hxx" diff --git a/arduino/libify.sh b/arduino/libify.sh index 61ce6df88..9835914ba 100755 --- a/arduino/libify.sh +++ b/arduino/libify.sh @@ -134,7 +134,7 @@ copy_file . arduino/{library.json,library.properties,keywords.txt,README.md,LICE copy_dir . arduino/examples copy_file src arduino/OpenMRNLite.{h,cpp} arduino/CDIXMLGenerator.hxx \ - include/{can_frame.h,nmranet_config.h,openmrn_features.h} \ + include/{can_frame.h,nmranet_config.h,openmrn_features.h,i2c.h,i2c-dev.h} \ include/freertos/{bootloader_hal.h,can_ioctl.h,endian.h,freertos_includes.h,stropts.h} \ include/freertos_select/ifaddrs.h diff --git a/include/can_frame.h b/include/can_frame.h index 7ebef825b..4e0028e7f 100644 --- a/include/can_frame.h +++ b/include/can_frame.h @@ -65,7 +65,7 @@ #elif defined (__nuttx__) || defined (__FreeRTOS__) || defined (__MACH__) || \ defined (__WIN32__) || defined (__EMSCRIPTEN__) || defined (ESP_NONOS) || \ - defined (ARDUINO) || defined (ESP32) + defined (ARDUINO) || defined (ESP_PLATFORM) #include struct can_frame diff --git a/include/freertos/can_ioctl.h b/include/freertos/can_ioctl.h index 69f5d228e..8b79d1d38 100644 --- a/include/freertos/can_ioctl.h +++ b/include/freertos/can_ioctl.h @@ -37,7 +37,7 @@ #include #ifdef __FreeRTOS__ #include "freertos/stropts.h" -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) #include "stropts.h" #endif diff --git a/include/freertos/freertos_includes.h b/include/freertos/freertos_includes.h index 86d444ffc..7245c39d3 100644 --- a/include/freertos/freertos_includes.h +++ b/include/freertos/freertos_includes.h @@ -1,12 +1,64 @@ -#ifdef ESP32 +/** \copyright + * Copyright (c) 2019, Balazs Racz + * 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 freertos_includes.h + * This file simplifies the include path for FreeRTOS header files between + * platforms. + * + * @author Balazs Racz + * @date 2 March 2019 + */ + +#ifdef ESP_PLATFORM #include #include #include +#include "sdkconfig.h" #define NSEC_TO_TICK(ns) \ (((((long long)(ns)) / 1000 * configTICK_RATE_HZ) + 999999) / 1000000) +// IDF v5.0 has introduced a configuration option (disabled by default) which +// enables the usage of legacy FreeRTOS data types, if that configuration option +// is *NOT* selected *AND* IDF v5.0+ is in use we need to add compatibility +// defines in order to compile OpenMRN successfully. +#if !defined(CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY) + +// used in os/os.c and os/os.h +#define portTickType TickType_t +#define xTaskHandle TaskHandle_t +#define xQueueHandle QueueHandle_t +#define xSemaphoreHandle SemaphoreHandle_t + +// used in freertos_drivers/arduino/CpuLoad.hxx and os/os.c +#define pcTaskGetTaskName pcTaskGetName + +#endif // IDF v5.0+ and !CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY + #else #include diff --git a/include/freertos/stropts.h b/include/freertos/stropts.h index b0df5ab77..2ec587fd9 100644 --- a/include/freertos/stropts.h +++ b/include/freertos/stropts.h @@ -38,7 +38,7 @@ extern "C" { #endif -#if defined(ESP32) +#if defined(ESP_PLATFORM) #include #else /** Request and ioctl transaction @@ -47,7 +47,7 @@ extern "C" { * @param ... key data (as a pointer or unsigned long type) */ int ioctl(int fd, unsigned long int key, ...); -#endif // ESP32 +#endif // ESP_PLATFORM /** ioctl key value for operation (not read or write) */ #define IOC_NONE 0U diff --git a/include/i2c-dev.h b/include/i2c-dev.h index 6b20e9c1e..ea208b434 100644 --- a/include/i2c-dev.h +++ b/include/i2c-dev.h @@ -36,7 +36,7 @@ #if defined (__linux__) #include -#elif defined (__FreeRTOS__) +#elif defined (__FreeRTOS__) || defined(ESP_PLATFORM) #include /** magic number for this driver's ioctl calls */ #define I2C_MAGIC ('i') diff --git a/include/i2c.h b/include/i2c.h index 8bf8af1e0..bf6253108 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -36,7 +36,7 @@ #if defined (__linux__) #include -#elif defined (__FreeRTOS__) +#elif defined (__FreeRTOS__) || defined(ESP_PLATFORM) #include /** Used in @ref i2c_rdwr_ioctl_data to describe a transaction segment. */ struct i2c_msg diff --git a/include/openmrn_features.h b/include/openmrn_features.h index 810e34cfd..79c5e2ba3 100644 --- a/include/openmrn_features.h +++ b/include/openmrn_features.h @@ -37,12 +37,12 @@ #ifndef _INCLUDE_OPENMRN_FEATURES_ #define _INCLUDE_OPENMRN_FEATURES_ -#ifdef ESP32 +#ifdef ESP_PLATFORM #include #else #define ESP_IDF_VERSION 0 #define ESP_IDF_VERSION_VAL(a,b,c) 1 -#endif // ESP32 +#endif // ESP_PLATFORM #ifdef __FreeRTOS__ /// Compiles the FreeRTOS event group based ::select() implementation. @@ -54,43 +54,50 @@ #define OPENMRN_FEATURE_REENT 1 #endif -#if defined(__FreeRTOS__) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) +#if defined(__FreeRTOS__) || defined(ESP_PLATFORM) // Note: this is not using OPENMRN_FEATURE_DEVICE_SELECT due to other usages // of that macro which may conflict with the ESP32 version of this feature. /// Adds support for FD based CAN interfaces. #define OPENMRN_FEATURE_FD_CAN_DEVICE 1 #endif -#if defined(__linux__) || defined(__MACH__) || defined(__WINNT__) || defined(ESP32) || defined(OPENMRN_FEATURE_DEVTAB) +#if defined(__linux__) || defined(__MACH__) || defined(__WINNT__) || \ + defined(ESP_PLATFORM) || defined(OPENMRN_FEATURE_DEVTAB) /// Enables the code using ::open ::close ::read ::write for non-volatile /// storage, FileMemorySpace for the configuration space, and /// SNIP_DYNAMIC_FILE_NAME for node names. #define OPENMRN_HAVE_POSIX_FD 1 #endif +#if !defined(ESP_PLATFORM) +/// Enables the code using ::fstat to confirm if the file handle is a socket. +#define OPENMRN_HAVE_SOCKET_FSTAT 1 +#endif + /// @todo this should probably be a whitelist: __linux__ || __MACH__. -#if !defined(__FreeRTOS__) && !defined(__WINNT__) && !defined(ESP32) && \ +#if !defined(__FreeRTOS__) && !defined(__WINNT__) && !defined(ESP_PLATFORM) && \ !defined(ARDUINO) && !defined(ESP_NONOS) /// Uses ::pselect in the Executor for sleep and pkill for waking up. #define OPENMRN_HAVE_PSELECT 1 #endif -#if defined(__WINNT__) || defined(ESP32) || defined(ESP_NONOS) +#if defined(__WINNT__) || defined(ESP_PLATFORM) || defined(ESP_NONOS) /// Uses ::select in the executor to sleep (unsure how wakeup is handled) #define OPENMRN_HAVE_SELECT 1 #endif -#if defined(OPENMRN_HAVE_SELECT) || defined(OPENMRN_HAVE_PSELECT) || defined(OPENMRN_FEATURE_DEVICE_SELECT) +#if defined(OPENMRN_HAVE_SELECT) || defined(OPENMRN_HAVE_PSELECT) || \ + defined(OPENMRN_FEATURE_DEVICE_SELECT) #define OPENMRN_FEATURE_EXECUTOR_SELECT 1 #endif -#if (defined(ARDUINO) && !defined(ESP32)) || defined(ESP_NONOS) || \ +#if (defined(ARDUINO) && !defined(ESP_PLATFORM)) || defined(ESP_NONOS) || \ defined(__EMSCRIPTEN__) /// A loop() function is calling the executor in the single-threaded OS context. #define OPENMRN_FEATURE_SINGLE_THREADED 1 #endif -#if defined(__FreeRTOS__) || defined(ESP32) +#if defined(__FreeRTOS__) || defined(ESP_PLATFORM) /// Use os_mutex_... implementation based on FreeRTOS mutex and semaphores. #define OPENMRN_FEATURE_MUTEX_FREERTOS 1 @@ -111,7 +118,7 @@ #define OPENMRN_FEATURE_SEM_TIMEDWAIT 1 #endif -#if defined(__FreeRTOS__) || defined(ESP32) +#if defined(__FreeRTOS__) || defined(ESP_PLATFORM) /// Use FreeRTOS implementation for os_thread_create and keeping a list of live /// threads. #define OPENMRN_FEATURE_THREAD_FREERTOS 1 @@ -131,7 +138,7 @@ #endif #if defined(__linux__) || defined(__MACH__) || defined(__FreeRTOS__) || \ - defined(ESP32) + defined(ESP_PLATFORM) /// Compiles support for BSD sockets API. #define OPENMRN_FEATURE_BSD_SOCKETS 1 @@ -141,7 +148,7 @@ #define OPENMRN_HAVE_BSD_SOCKETS_RX_TIMEOUT 1 #endif -#if !defined(__FreeRTOS__) && !defined(ESP32) +#if !defined(__FreeRTOS__) && !defined(ESP_PLATFORM) /// Compiles support for calling getsockname when binding a socket to a port /// when listening for incoming connections. #define OPENMRN_HAVE_BSD_SOCKETS_GETSOCKNAME 1 @@ -157,7 +164,7 @@ #define OPENMRN_FEATURE_BSD_SOCKETS_IGNORE_SIGPIPE 1 #endif -#if defined(__linux__) || defined(__MACH__) || defined(ESP32) +#if defined(__linux__) || defined(__MACH__) || defined(ESP_PLATFORM) /// Compiles support for reporting EOF as an error for read/write. #define OPENMRN_FEATURE_BSD_SOCKETS_REPORT_EOF_ERROR 1 #endif diff --git a/src/executor/Executor.hxx b/src/executor/Executor.hxx index 39cd8c57f..a760a56b3 100644 --- a/src/executor/Executor.hxx +++ b/src/executor/Executor.hxx @@ -323,7 +323,7 @@ public: */ void add_from_isr(Executable *msg, unsigned priority = UINT_MAX) override { -#ifdef ESP32 +#ifdef ESP_PLATFORM // On the ESP32 we need to call insert instead of insert_locked to // ensure that all code paths lock the queue for consistency since // this code path is not guaranteed to be protected by a critical @@ -333,7 +333,7 @@ public: #else queue_.insert_locked( msg, priority >= NUM_PRIO ? NUM_PRIO - 1 : priority); -#endif // ESP32 +#endif // ESP_PLATFORM selectHelper_.wakeup_from_isr(); } #endif // OPENMRN_FEATURE_RTOS_FROM_ISR diff --git a/src/executor/StateFlow.hxx b/src/executor/StateFlow.hxx index d52b83817..7cffff065 100644 --- a/src/executor/StateFlow.hxx +++ b/src/executor/StateFlow.hxx @@ -355,6 +355,7 @@ protected: Pool *p = pool; if (!p) { + HASSERT(target_flow != nullptr); p = target_flow->pool(); } LOG(VERBOSE, "allocate from pool %p, main pool %p", p, mainBufferPool); @@ -736,14 +737,12 @@ protected: */ Action listen_and_call(StateFlowSelectHelper *helper, int fd, Callback c) { -// ESP-IDF does not implement fstat for the LwIP VFS layer -// https://github.com/espressif/esp-idf/issues/7198 -#ifndef ESP32 +#if OPENMRN_HAVE_SOCKET_FSTAT // verify that the fd is a socket struct stat stat; fstat(fd, &stat); HASSERT(S_ISSOCK(stat.st_mode)); -#endif // ESP32 +#endif // OPENMRN_HAVE_SOCKET_FSTAT helper->reset(Selectable::READ, fd, Selectable::MAX_PRIO); helper->set_wakeup(this); @@ -759,14 +758,12 @@ protected: */ Action connect_and_call(StateFlowSelectHelper *helper, int fd, Callback c) { -// ESP-IDF does not implement fstat for the LwIP VFS layer -// https://github.com/espressif/esp-idf/issues/7198 -#ifndef ESP32 +#if OPENMRN_HAVE_SOCKET_FSTAT // verify that the fd is a socket struct stat stat; fstat(fd, &stat); HASSERT(S_ISSOCK(stat.st_mode)); -#endif // ESP32 +#endif // OPENMRN_HAVE_SOCKET_FSTAT helper->reset(Selectable::WRITE, fd, Selectable::MAX_PRIO); helper->set_wakeup(this); diff --git a/src/freertos_drivers/common/libatomic.c b/src/freertos_drivers/common/libatomic.c index bd7db1f98..094286e52 100644 --- a/src/freertos_drivers/common/libatomic.c +++ b/src/freertos_drivers/common/libatomic.c @@ -35,7 +35,7 @@ #include -#if defined(STM32F0xx) || (!defined(ARDUINO) && !defined(ESP32)) +#if defined(STM32F0xx) || (!defined(ARDUINO) && !defined(ESP_PLATFORM)) // On Cortex-M0 the only way to do atomic operation is to disable interrupts. /// Disables interrupts and saves the interrupt enable flag in a register. diff --git a/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx b/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx new file mode 100644 index 000000000..8af0ee0a3 --- /dev/null +++ b/src/freertos_drivers/esp32/Esp32AdcOneShot.hxx @@ -0,0 +1,217 @@ +/** \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 Esp32AdcOneShot.hxx + * + * Helper declarations for using ADC pins via the ADC One-Shot ESP-IDF APIs. + * + * @author Mike Dunston + * @date 8 Feburary 2023 + */ + +#ifndef _DRIVERS_ESP32ADCONESHOT_HXX_ +#define _DRIVERS_ESP32ADCONESHOT_HXX_ + +#include "utils/logging.h" + +#if defined(__has_include) +#if __has_include() +// include legacy types so ADC1_CHANNEL_0 and ADC1_CHANNEL_0_GPIO_NUM etc are +// defined, soc/adc_channel.h includes references to these on release/v5.0 +// branch and is planned for update in later relase versions. +#include +#endif // __has_include driver/adc_types_legacy.h +#endif // has_include + +#include +#include + +/// Defines an ADC input pin. +/// +/// Do not use this class directly. Use @ref ADC_PIN instead. +template struct Esp32ADCInput : public Defs +{ +public: + using Defs::ATTEN; + using Defs::BITS; + using Defs::CHANNEL; + using Defs::PIN; + using Defs::HANDLE; + + 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, +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + .unit_id = PIN <= 10 ? ADC_UNIT_1 : ADC_UNIT_2, +#elif CONFIG_IDF_TARGET_ESP32C3 + .unit_id = PIN <= 4 ? ADC_UNIT_1 : ADC_UNIT_2, +#endif + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + const adc_oneshot_chan_cfg_t channel_config = + { + .atten = ATTEN, + .bitwidth = BITS, + }; + + 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)); + ESP_ERROR_CHECK( + adc_oneshot_config_channel(HANDLE, CHANNEL, &channel_config)); + } + + /// NO-OP + static void hw_set_to_safe() + { + // NO-OP + } + + /// NO-OP + static void set(bool value) + { + // NO-OP + } + + static int sample() + { + int value = 0; + ESP_ERROR_CHECK(adc_oneshot_read(HANDLE, CHANNEL, &value)); + return value; + } +}; + +/// Helper macro for an ADC GPIO input on the ESP32. +/// +/// @param NAME is the basename of the declaration. For NAME==FOO the macro +/// declared FOO_Pin as a structure on which the read-write functions will be +/// available. +/// @param ADC_CHANNEL is the ADC channel to configure. +/// @param ATTENUATION is the voltage range for the ADC input. +/// @param BIT_RANGE is the bit range to configure the ADC to use. +/// +/// Supported ATTENUATION values and voltage ranges: +/// ADC_ATTEN_DB_0 - 0dB attenuaton gives full-scale voltage 1.1V +/// ADC_ATTEN_DB_2_5 - 2.5dB attenuation gives full-scale voltage 1.5V +/// ADC_ATTEN_DB_6 - 6dB attenuation gives full-scale voltage 2.2V +/// ADC_ATTEN_DB_11 - 11dB attenuation gives full-scale voltage 3.9V +/// +/// Supported BIT_RANGE values and ADC sample values: +/// ADC_WIDTH_BIT_9 - 0-511 +/// ADC_WIDTH_BIT_10 - 0-1023 +/// ADC_WIDTH_BIT_11 - 0-2047 +/// ADC_WIDTH_BIT_12 - 0-4065 +/// ADC_WIDTH_BIT_13 - 0-8191 -- Only valid on the ESP32-S2 and ESP32-S3. +/// NOTE: When using ADC1_CHANNEL_X this bit range will be applied to all +/// ADC1 channels, it is not recommended to mix values for ADC1 channels. +/// +/// Supported ADC_CHANNEL values and pin assignments for the ESP32: +/// ADC1_CHANNEL_0 : 36 +/// ADC1_CHANNEL_1 : 37 -- NOTE: Not recommended for use, see note below. +/// ADC1_CHANNEL_2 : 38 -- NOTE: Not recommended for use, see note below. +/// ADC1_CHANNEL_3 : 39 +/// ADC1_CHANNEL_4 : 32 +/// ADC1_CHANNEL_5 : 33 +/// ADC1_CHANNEL_6 : 34 +/// ADC1_CHANNEL_7 : 35 +/// ADC2_CHANNEL_0 : 4 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_1 : 0 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_2 : 2 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_3 : 15 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_4 : 13 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_5 : 12 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_6 : 14 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_7 : 27 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_8 : 25 -- NOTE: Not usable when WiFi is active. +/// ADC2_CHANNEL_9 : 29 -- NOTE: Not usable when WiFi is active. +/// NOTE: ADC1_CHANNEL_1 and ADC1_CHANNEL_2 typically have a capacitor which +/// connects to ADC1_CHANNEL_0 or ADC1_CHANNEL_3. The only known exception to +/// this is for some ESP32-PICO-D4/ESP32-PICO-V3 based boards, confirm on the +/// board schematic before using these pins. +/// +/// Supported ADC_CHANNEL values and pin assignments for the ESP32-S2/ESP32-S3: +/// ADC1_CHANNEL_0 : 1 +/// ADC1_CHANNEL_1 : 2 +/// ADC1_CHANNEL_2 : 3 +/// ADC1_CHANNEL_3 : 4 +/// ADC1_CHANNEL_4 : 5 +/// ADC1_CHANNEL_5 : 6 +/// ADC1_CHANNEL_6 : 7 +/// ADC1_CHANNEL_7 : 8 +/// ADC1_CHANNEL_8 : 9 +/// ADC1_CHANNEL_9 : 10 +/// ADC2_CHANNEL_0 : 11 +/// ADC2_CHANNEL_1 : 12 +/// ADC2_CHANNEL_2 : 13 +/// ADC2_CHANNEL_3 : 14 +/// ADC2_CHANNEL_4 : 15 +/// ADC2_CHANNEL_5 : 16 +/// ADC2_CHANNEL_6 : 17 +/// ADC2_CHANNEL_7 : 18 +/// ADC2_CHANNEL_8 : 19 -- NOTE: This pin is also used for USB PHY (D-). +/// ADC2_CHANNEL_9 : 20 -- NOTE: This pin is also used for USB PHY (D+). +/// +/// Supported ADC_CHANNEL values and pin assignments for the ESP32-C3: +/// ADC1_CHANNEL_0 : 0 +/// ADC1_CHANNEL_1 : 1 +/// ADC1_CHANNEL_2 : 2 +/// ADC1_CHANNEL_3 : 3 +/// ADC1_CHANNEL_4 : 4 +/// ADC2_CHANNEL_0 : 5 +/// +/// Example: +/// ADC_PIN(SENSE, ADC1_CHANNEL_0, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12); +/// ... +/// int level = SENSE_Pin::sample(); +#define ADC_PIN(NAME, ADC_CHANNEL, ATTENUATION, BIT_RANGE) \ + struct NAME##Defs \ + { \ + static const adc_channel_t CHANNEL = (adc_channel_t)ADC_CHANNEL; \ + 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; \ + public: \ + static const gpio_num_t pin() \ + { \ + return PIN; \ + } \ + static const adc_channel_t channel() \ + { \ + return CHANNEL; \ + } \ + }; \ + adc_oneshot_unit_handle_t NAME##Defs::HANDLE; \ + typedef Esp32ADCInput NAME##_Pin + +#endif // _DRIVERS_ESP32ADCONESHOT_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx b/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx index a1303f06b..8fb8b3b8b 100644 --- a/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx +++ b/src/freertos_drivers/esp32/Esp32BootloaderHal.hxx @@ -40,11 +40,6 @@ #define _FREERTOS_DRIVERS_ESP32_ESP32BOOTLOADERHAL_HXX_ #include "sdkconfig.h" -#include - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0) -#error ESP32 Bootloader is only supported with ESP-IDF v4.3+ -#endif // IDF v4.3+ #ifndef BOOTLOADER_LOG_LEVEL #define BOOTLOADER_LOG_LEVEL VERBOSE @@ -56,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 +// 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 @@ -583,4 +578,4 @@ void esp32_bootloader_run(uint64_t id, gpio_num_t rx, gpio_num_t tx, } } -#endif // _FREERTOS_DRIVERS_ESP32_ESP32BOOTLOADERHAL_HXX_ \ No newline at end of file +#endif // _FREERTOS_DRIVERS_ESP32_ESP32BOOTLOADERHAL_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32Gpio.hxx b/src/freertos_drivers/esp32/Esp32Gpio.hxx index 3a58f8fcc..f60a44cfc 100644 --- a/src/freertos_drivers/esp32/Esp32Gpio.hxx +++ b/src/freertos_drivers/esp32/Esp32Gpio.hxx @@ -36,27 +36,15 @@ #define _DRIVERS_ESP32GPIO_HXX_ #include "freertos_drivers/arduino/GpioWrapper.hxx" +#include "freertos_drivers/esp32/Esp32AdcOneShot.hxx" #include "os/Gpio.hxx" #include "utils/logging.h" #include "utils/macros.h" -#include #include -#include -#include - -// esp_rom_gpio.h is a target agnostic replacement for esp32/rom/gpio.h -#if __has_include() #include -#elif ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,0,0) -#include -#else -#include -#endif -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0) #include -#endif #if defined(CONFIG_IDF_TARGET_ESP32C3) /// Helper macro to test if a pin has been configured for output. @@ -94,7 +82,7 @@ public: static_assert(!(PIN_NUM >= 26 && PIN_NUM <= 32) , "Pin is reserved for flash usage."); #if defined(CONFIG_SPIRAM_MODE_OCT) || defined(CONFIG_ESPTOOLPY_OCT_FLASH) - static_assert(!(PIN_NUM >= 33 && PIN_NUM <= 37)), + static_assert(!(PIN_NUM >= 33 && PIN_NUM <= 37), "Pin is not available when Octal SPI mode is enabled."); #endif // ESP32S3 with Octal SPI #elif CONFIG_IDF_TARGET_ESP32S2 @@ -126,7 +114,7 @@ public: #else static_assert(!(PIN_NUM >= 6 && PIN_NUM <= 11) , "Pin is reserved for flash usage."); -#if defined(BOARD_HAS_PSRAM) +#if defined(BOARD_HAS_PSRAM) || defined(CONFIG_SPIRAM_SUPPORT) static_assert(PIN_NUM != 16 && PIN_NUM != 17 , "Pin is reserved for PSRAM usage."); #endif // BOARD_HAS_PSRAM @@ -238,11 +226,7 @@ public: LOG(VERBOSE, "[Esp32Gpio] Configuring output pin %d, default value: %d", PIN_NUM, SAFE_VALUE); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0) - gpio_pad_select_gpio(PIN_NUM); -#else // IDF v4.4 (or later) esp_rom_gpio_pad_select_gpio(PIN_NUM); -#endif // IDF v4.3 (or earlier) gpio_config_t cfg; memset(&cfg, 0, sizeof(gpio_config_t)); cfg.pin_bit_mask = BIT64(PIN_NUM); @@ -358,11 +342,7 @@ public: { LOG(VERBOSE, "[Esp32Gpio] Configuring input pin %d, PUEN: %d, PDEN: %d", PIN_NUM, PUEN, PDEN); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0) - gpio_pad_select_gpio(PIN_NUM); -#else // IDF v4.4 (or later) esp_rom_gpio_pad_select_gpio(PIN_NUM); -#endif // IDF v4.3 (or earlier) gpio_config_t cfg; memset(&cfg, 0, sizeof(gpio_config_t)); cfg.pin_bit_mask = BIT64(PIN_NUM); @@ -420,71 +400,6 @@ template struct GpioInputPUPD : public GpioInputPin struct Esp32ADCInput : public Defs -{ -public: - using Defs::CHANNEL; - using Defs::PIN; - using Defs::ATTEN; - using Defs::BITS; -#if CONFIG_IDF_TARGET_ESP32 - static const adc_unit_t UNIT = PIN >= 30 ? ADC_UNIT_1 : ADC_UNIT_2; -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - static const adc_unit_t UNIT = PIN <= 10 ? ADC_UNIT_1 : ADC_UNIT_2; -#elif CONFIG_IDF_TARGET_ESP32C3 - static const adc_unit_t UNIT = PIN <= 4 ? ADC_UNIT_1 : ADC_UNIT_2; -#endif - static void hw_init() - { - LOG(VERBOSE, - "[Esp32ADCInput] Configuring ADC%d:%d input pin %d, " - "attenuation %d, bits %d", - UNIT + 1, CHANNEL, PIN, ATTEN, BITS); - - if (UNIT == ADC_UNIT_1) - { - ESP_ERROR_CHECK(adc1_config_width(BITS)); - ESP_ERROR_CHECK( - adc1_config_channel_atten((adc1_channel_t)CHANNEL, ATTEN)); - } - else - { - ESP_ERROR_CHECK( - adc2_config_channel_atten((adc2_channel_t)CHANNEL, ATTEN)); - } - } - - /// NO-OP - static void hw_set_to_safe() - { - // NO-OP - } - - /// NO-OP - static void set(bool value) - { - // NO-OP - } - - static int sample() - { - int value = 0; - if (UNIT == ADC_UNIT_1) - { - value = adc1_get_raw((adc1_channel_t)CHANNEL); - } - else - { - ESP_ERROR_CHECK( - adc2_get_raw((adc2_channel_t)CHANNEL, BITS, &value)); - } - return value; - } -}; - /// Helper macro for defining GPIO pins on the ESP32. /// /// @param NAME is the basename of the declaration. For NAME==FOO the macro @@ -645,105 +560,4 @@ public: }; \ typedef BaseClass NAME##_Pin -/// Helper macro for an ADC GPIO input on the ESP32. -/// -/// @param NAME is the basename of the declaration. For NAME==FOO the macro -/// declared FOO_Pin as a structure on which the read-write functions will be -/// available. -/// @param ADC_CHANNEL is the ADC channel to configure. -/// @param ATTENUATION is the voltage range for the ADC input. -/// @param BIT_RANGE is the bit range to configure the ADC to use. -/// -/// Supported ATTENUATION values and voltage ranges: -/// ADC_ATTEN_DB_0 - 0dB attenuaton gives full-scale voltage 1.1V -/// ADC_ATTEN_DB_2_5 - 2.5dB attenuation gives full-scale voltage 1.5V -/// ADC_ATTEN_DB_6 - 6dB attenuation gives full-scale voltage 2.2V -/// ADC_ATTEN_DB_11 - 11dB attenuation gives full-scale voltage 3.9V -/// -/// Supported BIT_RANGE values and ADC sample values: -/// ADC_WIDTH_BIT_9 - 0-511 -/// ADC_WIDTH_BIT_10 - 0-1023 -/// ADC_WIDTH_BIT_11 - 0-2047 -/// ADC_WIDTH_BIT_12 - 0-4065 -/// ADC_WIDTH_BIT_13 - 0-8191 -- Only valid on the ESP32-S2 and ESP32-S3. -/// NOTE: When using ADC1_CHANNEL_X this bit range will be applied to all -/// ADC1 channels, it is not recommended to mix values for ADC1 channels. -/// -/// Supported ADC_CHANNEL values and pin assignments for the ESP32: -/// ADC1_CHANNEL_0 : 36 -/// ADC1_CHANNEL_1 : 37 -- NOTE: Not recommended for use, see note below. -/// ADC1_CHANNEL_2 : 38 -- NOTE: Not recommended for use, see note below. -/// ADC1_CHANNEL_3 : 39 -/// ADC1_CHANNEL_4 : 32 -/// ADC1_CHANNEL_5 : 33 -/// ADC1_CHANNEL_6 : 34 -/// ADC1_CHANNEL_7 : 35 -/// ADC2_CHANNEL_0 : 4 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_1 : 0 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_2 : 2 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_3 : 15 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_4 : 13 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_5 : 12 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_6 : 14 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_7 : 27 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_8 : 25 -- NOTE: Not usable when WiFi is active. -/// ADC2_CHANNEL_9 : 29 -- NOTE: Not usable when WiFi is active. -/// NOTE: ADC1_CHANNEL_1 and ADC1_CHANNEL_2 typically have a capacitor which -/// connects to ADC1_CHANNEL_0 or ADC1_CHANNEL_3. The only known exception to -/// this is for some ESP32-PICO-D4/ESP32-PICO-V3 based boards, confirm on the -/// board schematic before using these pins. -/// -/// Supported ADC_CHANNEL values and pin assignments for the ESP32-S2/ESP32-S3: -/// ADC1_CHANNEL_0 : 1 -/// ADC1_CHANNEL_1 : 2 -/// ADC1_CHANNEL_2 : 3 -/// ADC1_CHANNEL_3 : 4 -/// ADC1_CHANNEL_4 : 5 -/// ADC1_CHANNEL_5 : 6 -/// ADC1_CHANNEL_6 : 7 -/// ADC1_CHANNEL_7 : 8 -/// ADC1_CHANNEL_8 : 9 -/// ADC1_CHANNEL_9 : 10 -/// ADC2_CHANNEL_0 : 11 -/// ADC2_CHANNEL_1 : 12 -/// ADC2_CHANNEL_2 : 13 -/// ADC2_CHANNEL_3 : 14 -/// ADC2_CHANNEL_4 : 15 -/// ADC2_CHANNEL_5 : 16 -/// ADC2_CHANNEL_6 : 17 -/// ADC2_CHANNEL_7 : 18 -/// ADC2_CHANNEL_8 : 19 -- NOTE: This pin is also used for USB PHY (D-). -/// ADC2_CHANNEL_9 : 20 -- NOTE: This pin is also used for USB PHY (D+). -/// -/// Supported ADC_CHANNEL values and pin assignments for the ESP32-C3: -/// ADC1_CHANNEL_0 : 0 -/// ADC1_CHANNEL_1 : 1 -/// ADC1_CHANNEL_2 : 2 -/// ADC1_CHANNEL_3 : 3 -/// ADC1_CHANNEL_4 : 4 -/// ADC2_CHANNEL_0 : 5 -/// -/// Example: -/// ADC_PIN(SENSE, ADC1_CHANNEL_0, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12); -/// ... -/// int level = SENSE_Pin::sample(); -#define ADC_PIN(NAME, ADC_CHANNEL, ATTENUATION, BIT_RANGE) \ - struct NAME##Defs \ - { \ - static const adc_channel_t CHANNEL = (adc_channel_t)ADC_CHANNEL; \ - 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_bits_width_t BITS = (adc_bits_width_t)BIT_RANGE; \ - public: \ - static const gpio_num_t pin() \ - { \ - return PIN; \ - } \ - static const adc_channel_t channel() \ - { \ - return CHANNEL; \ - } \ - }; \ - typedef Esp32ADCInput NAME##_Pin - -#endif // _DRIVERS_ESP32GPIO_HXX_ \ No newline at end of file +#endif // _DRIVERS_ESP32GPIO_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32HardwareCanAdapter.hxx b/src/freertos_drivers/esp32/Esp32HardwareCanAdapter.hxx deleted file mode 100644 index 3a2123289..000000000 --- a/src/freertos_drivers/esp32/Esp32HardwareCanAdapter.hxx +++ /dev/null @@ -1,428 +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 Esp32HardwareCanAdapter.hxx - * - * ESP32 Hardware CAN adapter code. This utilizes the built in CAN controller - * to translate the can_frame in OpenMRN to the ESP32 can_message_t used by the - * ESP-IDF CAN controller code. The ESP32 will still require an external CAN - * transceiver (MCP2551 or SN65HVD230 as example). - * - * @author Mike Dunston - * @date 19 January 2019 - */ - -#ifndef _FREERTOS_DRIVERS_ESP32_ESP32HWCAN_HXX_ -#define _FREERTOS_DRIVERS_ESP32_ESP32HWCAN_HXX_ - -namespace openmrn_arduino -{ - -#include "freertos_drivers/arduino/Can.hxx" -#include - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,2,0) -#include -#else // NOT IDF v4.2+ -#include - -// The following types and APIs are created as aliases as a compatibility for -// IDF v4.3+ which breaks due to driver/twai.h on the ESP32 attempting to -// override a few types created as part of can_ioctl.h -typedef can_timing_config_t twai_timing_config_t; -typedef can_filter_config_t twai_filter_config_t; -typedef can_general_config_t twai_general_config_t; -typedef can_status_info_t twai_status_info_t; -typedef can_message_t twai_message_t; -#define TWAI_TIMING_CONFIG_125KBITS CAN_TIMING_CONFIG_125KBITS -#define TWAI_FILTER_CONFIG_ACCEPT_ALL CAN_FILTER_CONFIG_ACCEPT_ALL -#define TWAI_MODE_NORMAL CAN_MODE_NORMAL -#define TWAI_IO_UNUSED CAN_IO_UNUSED -#define TWAI_ALERT_NONE CAN_ALERT_NONE -#define TWAI_STATE_BUS_OFF CAN_STATE_BUS_OFF -#define TWAI_STATE_RECOVERING CAN_STATE_RECOVERING -#define TWAI_MSG_FLAG_NONE CAN_MSG_FLAG_NONE -#define TWAI_MSG_FLAG_EXTD CAN_MSG_FLAG_EXTD -#define TWAI_MSG_FLAG_RTR CAN_MSG_FLAG_RTR -#define TWAI_MSG_FLAG_DLC_NON_COMP CAN_MSG_FLAG_DLC_NON_COMP - -#define twai_driver_install can_driver_install -#define twai_start can_start -#define twai_stop can_stop -#define twai_get_status_info can_get_status_info -#define twai_initiate_recovery can_initiate_recovery -#define twai_transmit can_transmit -#define twai_receive can_receive - -#endif // IDF v4.2+ - -#include -#include - -/// ESP32 CAN bus status strings, used for periodic status reporting -static const char *ESP32_CAN_STATUS_STRINGS[] = -{ - "STOPPED", // CAN_STATE_STOPPED - "RUNNING", // CAN_STATE_RUNNING - "OFF / RECOVERY NEEDED", // CAN_STATE_BUS_OFF - "RECOVERY UNDERWAY" // CAN_STATE_RECOVERING -}; - -class Esp32HardwareCanDeprecated : public Can -{ -public: - /// Constructor. - /// - /// @param name is the name for the CAN adapter, this is currently not used. - /// @param rxPin is the ESP32 pin that is connected to the external - /// transceiver RX. - /// @param txPin is the ESP32 pin that is connected to the external - /// transceiver TX. - Esp32HardwareCanDeprecated(const char *name, gpio_num_t rxPin, - gpio_num_t txPin, bool reportStats = true) - : Can(name) - , reportStats_(reportStats) - , overrunWarningPrinted_(false) - { - // Configure the ESP32 CAN driver to use 125kbps. - twai_timing_config_t can_timing_config = TWAI_TIMING_CONFIG_125KBITS(); - // By default we accept all CAN frames. - twai_filter_config_t can_filter_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); - // Note: not using the CAN_GENERAL_CONFIG_DEFAULT macro due to a missing - // cast for CAN_IO_UNUSED. - twai_general_config_t can_general_config = {.mode = TWAI_MODE_NORMAL, - .tx_io = txPin, - .rx_io = rxPin, - .clkout_io = (gpio_num_t)TWAI_IO_UNUSED, - .bus_off_io = (gpio_num_t)TWAI_IO_UNUSED, - .tx_queue_len = (uint32_t)config_can_tx_buffer_size() / 2, - .rx_queue_len = (uint32_t)config_can_rx_buffer_size() / 2, - .alerts_enabled = TWAI_ALERT_NONE, - .clkout_divider = 0}; - - LOG(VERBOSE, - "ESP32-CAN driver configured using RX: %d, TX: %d, RX-Q: %d, " - "TX-Q: %d", - can_general_config.rx_io, can_general_config.tx_io, - can_general_config.rx_queue_len, can_general_config.tx_queue_len); - - ESP_ERROR_CHECK(twai_driver_install( - &can_general_config, &can_timing_config, &can_filter_config)); - - xTaskCreatePinnedToCore(rx_task, "ESP32-CAN RX", OPENMRN_STACK_SIZE, - this, RX_TASK_PRIORITY, &rxTaskHandle_, tskNO_AFFINITY); - xTaskCreatePinnedToCore(tx_task, "ESP32-CAN TX", OPENMRN_STACK_SIZE, - this, TX_TASK_PRIORITY, &txTaskHandle_, tskNO_AFFINITY); - } - - ~Esp32HardwareCanDeprecated() - { - } - - /// Enables the ESP32 CAN driver - virtual void enable() - { - ESP_ERROR_CHECK(twai_start()); - LOG(VERBOSE, "ESP32-CAN driver enabled"); - } - - /// Disables the ESP32 CAN driver - virtual void disable() - { - ESP_ERROR_CHECK(twai_stop()); - LOG(VERBOSE, "ESP32-CAN driver disabled"); - } - -protected: - /// function to try and transmit a message - void tx_msg() override - { - // wake up the tx_task so it can consume any can_frames from txBuf. - xTaskNotifyGive(txTaskHandle_); - } - -private: - /// Default constructor. - Esp32HardwareCanDeprecated(); - - /// Enables/Disables the periodic reporting of CAN bus statistics to the - /// default serial stream. - bool reportStats_ : 1; - /// Set to true if the 'frame dropped' warning is printed. - bool overrunWarningPrinted_ : 1; - - /// Handle for the tx_task that converts and transmits can_frame to the - /// native can driver. - TaskHandle_t txTaskHandle_; - - /// Handle for the rx_task that receives and converts the native can driver - /// frames to can_frame. - TaskHandle_t rxTaskHandle_; - - /// Interval at which to print the ESP32 CAN bus status. - static constexpr TickType_t STATUS_PRINT_INTERVAL = pdMS_TO_TICKS(10000); - - /// Interval to wait between iterations when the bus is recovering, a - /// transmit failure or there is nothing to transmit. - static constexpr TickType_t TX_DEFAULT_DELAY = pdMS_TO_TICKS(250); - - /// Priority to use for the rx_task. This needs to be higher than the - /// tx_task and lower than @ref OPENMRN_TASK_PRIORITY. - static constexpr UBaseType_t RX_TASK_PRIORITY = ESP_TASK_TCPIP_PRIO - 2; - - /// Priority to use for the tx_task. This should be lower than - /// @ref RX_TASK_PRIORITY and @ref OPENMRN_TASK_PRIORITY. - static constexpr UBaseType_t TX_TASK_PRIORITY = ESP_TASK_TCPIP_PRIO - 3; - - /// Background task that takes care of the conversion of the @ref can_frame - /// provided by the @ref txBuf into an ESP32 can_message_t which can be - /// processed by the native CAN driver. This task also covers the periodic - /// status reporting and BUS recovery when necessary. - static void tx_task(void *can) - { - /// Get handle to our parent Esp32HardwareCanDeprecated object to - /// access the txBuf. - Esp32HardwareCanDeprecated *parent = - reinterpret_cast(can); - -#if CONFIG_TASK_WDT - // Add this task to the WDT - esp_task_wdt_add(parent->txTaskHandle_); -#endif // CONFIG_TASK_WDT - - /// Tracks the last time that we displayed the CAN driver status. - TickType_t next_status_display_tick_count = 0; - - while (true) - { -#if CONFIG_TASK_WDT - // Feed the watchdog so it doesn't reset the ESP32 - esp_task_wdt_reset(); -#endif // CONFIG_TASK_WDT - - // periodic CAN driver monitoring and reporting, this takes care of - // bus recovery when the CAN driver disables the bus due to error - // conditions exceeding thresholds. - twai_status_info_t status; - twai_get_status_info(&status); - auto current_tick_count = xTaskGetTickCount(); - if (next_status_display_tick_count == 0 || - current_tick_count >= next_status_display_tick_count) - { - next_status_display_tick_count = - current_tick_count + STATUS_PRINT_INTERVAL; - if (parent->reportStats_) - { - LOG(INFO, - "ESP32-CAN: %s rx-q:%d, tx-q:%d, rx-err:%d, tx-err:%d, " - "ovr:%d arb-lost:%d, bus-err:%d, state: %s", - parent->overrunWarningPrinted_ ? "!!OVERRUN!! " : "", - status.msgs_to_rx, status.msgs_to_tx, - status.rx_error_counter, status.tx_error_counter, - parent->overrunCount, status.arb_lost_count, - status.bus_error_count, - ESP32_CAN_STATUS_STRINGS[status.state]); - } - parent->overrunWarningPrinted_ = false; - } - if (status.state == TWAI_STATE_BUS_OFF) - { - // When the bus is OFF we need to initiate recovery, transmit is - // not possible when in this state. - LOG(WARNING, "ESP32-CAN: initiating recovery"); - twai_initiate_recovery(); - continue; - } - else if (status.state == TWAI_STATE_RECOVERING) - { - // when the bus is in recovery mode transmit is not possible. - vTaskDelay(TX_DEFAULT_DELAY); - continue; - } - - // check txBuf for any message to transmit. - unsigned count; - struct can_frame *can_frame = nullptr; - { - AtomicHolder h(parent); - count = parent->txBuf->data_read_pointer(&can_frame); - } - if (!count || !can_frame) - { - // tx Buf empty; wait for tx_msg to be called. - ulTaskNotifyTake(pdTRUE, // clear on exit - TX_DEFAULT_DELAY); - continue; - } - - /// ESP32 native CAN driver frame - twai_message_t msg; - memset(&msg, 0, sizeof(twai_message_t)); - - msg.flags = TWAI_MSG_FLAG_NONE; - msg.identifier = can_frame->can_id; - msg.data_length_code = can_frame->can_dlc; - for (int i = 0; i < can_frame->can_dlc; i++) - { - msg.data[i] = can_frame->data[i]; - } - if (IS_CAN_FRAME_EFF(*can_frame)) - { - msg.flags |= TWAI_MSG_FLAG_EXTD; - } - if (IS_CAN_FRAME_RTR(*can_frame)) - { - msg.flags |= TWAI_MSG_FLAG_RTR; - } - - // Pass the converted CAN frame to the native driver - // for transmit, if the TX queue is full this will - // return ESP_ERR_TIMEOUT which will result in the - // the message being left in txBuf for the next iteration. - // if this call returns ESP_OK we consider the frame as - // transmitted by the driver and remove it from txBuf. - esp_err_t tx_res = twai_transmit(&msg, pdMS_TO_TICKS(100)); - if (tx_res == ESP_OK) - { - LOG(VERBOSE, - "ESP32-CAN-TX OK id:%08x, flags:%04x, dlc:%02d, " - "data:%02x%02x%02x%02x%02x%02x%02x%02x", - msg.identifier, msg.flags, msg.data_length_code, - msg.data[0], msg.data[1], msg.data[2], msg.data[3], - msg.data[4], msg.data[5], msg.data[6], msg.data[7]); - AtomicHolder h(parent); - parent->txBuf->consume(1); - parent->txBuf->signal_condition(); - } - else if (tx_res != ESP_ERR_TIMEOUT) - { - LOG(WARNING, "ESP32-CAN-TX: %s", esp_err_to_name(tx_res)); - vTaskDelay(TX_DEFAULT_DELAY); - } - } // loop on task - } - - /// Background task that takes care of receiving can_message_t objects from - /// the ESP32 native CAN driver, when they are available, converting them to - /// a @ref can_frame and pushing them to the @ref rxBuf. - static void rx_task(void *can) - { - /// Get handle to our parent Esp32HardwareCanDeprecated object to access - /// the rxBuf. - Esp32HardwareCanDeprecated *parent = - reinterpret_cast(can); - -#if CONFIG_TASK_WDT - // Add this task to the WDT - esp_task_wdt_add(parent->rxTaskHandle_); -#endif // CONFIG_TASK_WDT - - while (true) - { -#if CONFIG_TASK_WDT - // Feed the watchdog so it doesn't reset the ESP32 - esp_task_wdt_reset(); -#endif // CONFIG_TASK_WDT - - /// ESP32 native CAN driver frame - twai_message_t msg; - memset(&msg, 0, sizeof(twai_message_t)); - if (twai_receive(&msg, pdMS_TO_TICKS(250)) != ESP_OK) - { - // native CAN driver did not give us a frame. - continue; - } - // we have received a frame from the native CAN driver, verify if - // it is a standard frame, if not we drop it. - if (msg.flags & TWAI_MSG_FLAG_DLC_NON_COMP) - { - LOG(WARNING, - "ESP32-CAN-RX: received non-compliant CAN frame, frame " - "dropped!"); - continue; - } - LOG(VERBOSE, - "ESP32-CAN-RX id:%08x, flags:%04x, dlc:%02d, " - "data:%02x%02x%02x%02x%02x%02x%02x%02x", - msg.identifier, msg.flags, msg.data_length_code, - msg.data[0], msg.data[1], msg.data[2], msg.data[3], - msg.data[4], msg.data[5], msg.data[6], msg.data[7]); - AtomicHolder h(parent); - struct can_frame *can_frame = nullptr; - // verify if we have space in the rxBuf, if not drop the frame and - // record the overrun. - if (!parent->rxBuf->data_write_pointer(&can_frame) || - can_frame == nullptr) - { - if (!parent->overrunWarningPrinted_) - { - parent->overrunWarningPrinted_ = true; - LOG(WARNING, - "ESP32-CAN-RX: buffer overrun, frame dropped!"); - } - parent->overrunCount++; - continue; - } - // we have space in the rxBuf, start conversion - LOG(VERBOSE, "ESP32-CAN-RX: converting to can_frame"); - memset(can_frame, 0, sizeof(struct can_frame)); - can_frame->can_id = msg.identifier; - can_frame->can_dlc = msg.data_length_code; - for (int i = 0; i < msg.data_length_code; i++) - { - can_frame->data[i] = msg.data[i]; - } - if (msg.flags & TWAI_MSG_FLAG_EXTD) - { - SET_CAN_FRAME_EFF(*can_frame); - } - if (msg.flags & TWAI_MSG_FLAG_RTR) - { - SET_CAN_FRAME_RTR(*can_frame); - } - parent->rxBuf->advance(1); - parent->rxBuf->signal_condition(); - } - } - DISALLOW_COPY_AND_ASSIGN(Esp32HardwareCanDeprecated); -}; - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) -/// Esp32HardwareCan has been deprecated due to lack of portability beyond the -/// ESP32. -/// @deprecated Use @ref Esp32HardwareTwai instead. -typedef Esp32HardwareCanDeprecated Esp32HardwareCan __attribute__ (( - deprecated("Esp32HardwareCan has been replaced with Esp32HardwareTwai."))); -#else -/// Esp32HardwareCan will be deprecated once arduino-esp32 2.0.0 has released. -typedef Esp32HardwareCanDeprecated Esp32HardwareCan; -#endif // IDF v4.3+ - -} // namespace openmrn_arduino - -using openmrn_arduino::Esp32HardwareCan; - -#endif /* _FREERTOS_DRIVERS_ARDUINO_ESP32HWCAN_HXX_ */ diff --git a/src/freertos_drivers/esp32/Esp32HardwareI2C.cpp b/src/freertos_drivers/esp32/Esp32HardwareI2C.cpp new file mode 100644 index 000000000..58600b553 --- /dev/null +++ b/src/freertos_drivers/esp32/Esp32HardwareI2C.cpp @@ -0,0 +1,513 @@ +/** \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 Esp32HardwareI2C.cxx + * + * I2C driver implementation for OpenMRN. This leverages the ESP-IDF I2C driver + * implementation and provides a VFS interface. + * + * @author Mike Dunston + * @date 8 Feb 2023 + */ + +#if defined(ESP_PLATFORM) + +#include "Esp32HardwareI2C.hxx" +// stropts.h must be included before i2c.h +#include "stropts.h" +#include "i2c.h" +#include "i2c-dev.h" +#include "sdkconfig.h" +#include "utils/format_utils.hxx" +#include "utils/logging.h" +#include "utils/StringPrintf.hxx" + +#include + +#if CONFIG_VFS_SUPPORT_TERMIOS +// remove defines added by arduino-esp32 core/esp32/binary.h which are +// duplicated in sys/termios.h which may be included by esp_vfs.h +#undef B110 +#undef B1000000 +#endif // CONFIG_VFS_SUPPORT_TERMIOS +#include +#include +#include + +namespace openmrn_arduino +{ + +extern "C" +{ + +/// VFS adapter for write(fd, buf, size) +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param fd is the file descriptor being written to. +/// @param buf is the buffer containing the data to be written. +/// @param size is the size of the buffer. +/// @return number of bytes written or -1 if there is the write would be a +/// blocking operation. +static ssize_t i2c_vfs_write(void *ctx, int fd, const void *buf, size_t size) +{ + HASSERT(ctx != NULL); + Esp32HardwareI2C *i2c = reinterpret_cast(ctx); + return i2c->write(fd, buf, size); +} + +/// VFS adapter for read(fd, buf, size) +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param fd is the file descriptor being read from. +/// @param buf is the buffer to write into. +/// @param size is the size of the buffer. +/// @return number of bytes read or -1 if there is the read would be a +/// blocking operation. +static ssize_t i2c_vfs_read(void *ctx, int fd, void *buf, size_t size) +{ + HASSERT(ctx != NULL); + Esp32HardwareI2C *i2c = reinterpret_cast(ctx); + return i2c->read(fd, buf, size); +} + +/// VFS adapter for open(path, flags, mode). +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param path is the path to the file being opened. +/// @param flags are the flags to use for opened file. +/// @param mode is the mode to use for the opened file. +/// +/// When this method is invoked it will enable the TWAI driver and start the +/// periodic timer used for RX/TX of frame data. +/// +/// @return 0 upon success, -1 upon failure with errno containing the cause. +static int i2c_vfs_open(void *ctx, const char *path, int flags, int mode) +{ + HASSERT(ctx != NULL); + Esp32HardwareI2C *i2c = reinterpret_cast(ctx); + return i2c->open(path, flags, mode); +} + +/// VFS adapter for close(fd). +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param fd is the file descriptor to close. +/// +/// When this method is invoked it will disable the TWAI driver and stop the +/// periodic timer used for RX/TX of frame data if it is running. +/// +/// @return zero upon success, negative value with errno for failure. +static int i2c_vfs_close(void *ctx, int fd) +{ + HASSERT(ctx != NULL); + Esp32HardwareI2C *i2c = reinterpret_cast(ctx); + return i2c->close(fd); +} + +/// VFS adapter for ioctl. +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param fd is the file descriptor to operate on. +/// @param cmd is the command to execute. +/// @param args is the args for the command. +/// +/// @return zero upon success, negative value with errno for failure. +static int i2c_vfs_ioctl(void *ctx, int fd, int cmd, va_list args) +{ + HASSERT(ctx != NULL); + Esp32HardwareI2C *i2c = reinterpret_cast(ctx); + return i2c->ioctl(fd, cmd, args); +} + +/// VFS adapter for fcntl(fd, cmd, arg). +/// +/// @param ctx is the @ref Esp32HardwareI2C instance to invoke. +/// @param fd to operate on. +/// @param cmd to be executed. +/// @param arg arg to be used for the operation. +/// +/// This method is currently a NO-OP. +/// +/// @return zero upon success, negative value with errno for failure. +static int i2c_vfs_fcntl(void *ctx, int fd, int cmd, int arg) +{ + HASSERT(ctx != NULL); + return 0; +} + +} // extern "C" + +Esp32HardwareI2C::Esp32HardwareI2C(const char * const path) + : path_(path) +{ +} + +Esp32HardwareI2C::~Esp32HardwareI2C() +{ + for (size_t idx = 0; idx < SOC_I2C_NUM; idx++) + { + if (i2cInitialized_[idx]) + { + ESP_ERROR_CHECK(i2c_driver_delete(static_cast(idx))); + } + } + if (vfsInitialized_) + { + ESP_ERROR_CHECK(esp_vfs_unregister(path_)); + } +} + +void Esp32HardwareI2C::hw_init(const gpio_num_t sda, const gpio_num_t scl, + const uint32_t bus_speed, const i2c_port_t port) +{ + if (!i2cInitialized_[port]) + { + i2c_config_t i2c_config = {}; + i2c_config.mode = I2C_MODE_MASTER; + i2c_config.sda_io_num = sda; + i2c_config.sda_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.scl_io_num = scl; + i2c_config.scl_pullup_en = GPIO_PULLUP_ENABLE; + i2c_config.master.clk_speed = bus_speed; + + LOG(INFO, + "[I2C] Initializing I2C%d using SDA:%d, SCL:%d, " + "bus-speed: %" PRIu32, port, sda, scl, bus_speed); + ESP_ERROR_CHECK(i2c_param_config(port, &i2c_config)); + ESP_ERROR_CHECK(i2c_driver_install(port, I2C_MODE_MASTER, + I2C_SLAVE_RX_BUF_SIZE, I2C_SLAVE_TX_BUF_SIZE, I2C_ISR_FLAGS)); + + i2cInitialized_[port] = true; + } + else + { + LOG_ERROR("[I2C] I2C%d has already been initialized!", port); + } + + if (!vfsInitialized_) + { + esp_vfs_t vfs = {}; + vfs.write_p = i2c_vfs_write; + vfs.read_p = i2c_vfs_read; + vfs.open_p = i2c_vfs_open; + vfs.close_p = i2c_vfs_close; + vfs.fcntl_p = i2c_vfs_fcntl; + vfs.ioctl_p = i2c_vfs_ioctl; + vfs.flags = ESP_VFS_FLAG_CONTEXT_PTR; + ESP_ERROR_CHECK(esp_vfs_register(path_, &vfs, this)); + + vfsInitialized_ = true; + } +} + +void Esp32HardwareI2C::scan(const i2c_port_t port) +{ + // Scan the I2C bus and dump the output of devices that respond + std::string scanresults = + " 0 1 2 3 4 5 6 7 8 9 a b c d e f\n" + "00: "; + scanresults.reserve(256); + + HASSERT(i2cInitialized_[port]); + for (uint8_t addr = 0; addr < 0x7F; addr++) + { + if (addr % 16 == 0) + { + scanresults.append(StringPrintf("\n%02x: ", addr)); + } + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(port, cmd, I2C_SCAN_TIMEOUT); + i2c_cmd_link_delete(cmd); + if (ret == ESP_OK) + { + scanresults.append(StringPrintf("%02x ", addr)); + } + else if (ret == ESP_ERR_TIMEOUT) + { + scanresults.append("?? "); + } + else + { + scanresults.append("-- "); + } + } + LOG(INFO, scanresults.c_str()); +} + +ssize_t Esp32HardwareI2C::write(int fd, const void *buf, size_t size) +{ + uint8_t address; + i2c_port_t port; + + { + AtomicHolder h(this); + + auto entry = std::find_if(devices_.begin(), devices_.end(), + [fd](const auto &device) + { + return device.fd == fd; + }); + + if (entry == devices_.end()) + { + // file handle not found, return an error. + errno = EBADF; + return -EBADF; + } + else if (entry->fd < 0) + { + // no address has been defined for this file handle, return an error. + errno = EINVAL; + return -EINVAL; + } + address = entry->address; + port = entry->port; + } + + esp_err_t res = ESP_ERROR_CHECK_WITHOUT_ABORT( + i2c_master_write_to_device(port, address, (uint8_t *)buf, size, + I2C_OP_TIMEOUT)); + + if (res == ESP_ERR_TIMEOUT) + { + return -ETIMEDOUT; + } + else if (res == ESP_ERR_INVALID_STATE) + { + return -EINVAL; + } + else if (res != ESP_OK) + { + return -EIO; + } + return size; +} + +ssize_t Esp32HardwareI2C::read(int fd, void *buf, size_t size) +{ + uint8_t address; + i2c_port_t port; + + { + AtomicHolder h(this); + + auto entry = std::find_if(devices_.begin(), devices_.end(), + [fd](const auto &device) + { + return device.fd == fd; + }); + + if (entry == devices_.end()) + { + // file handle not found, return an error. + errno = EBADF; + return -EBADF; + } + else if (entry->fd < 0) + { + // no address has been defined for this file handle, return an error. + errno = EINVAL; + return -EINVAL; + } + address = entry->address; + port = entry->port; + } + + + esp_err_t res = ESP_ERROR_CHECK_WITHOUT_ABORT( + i2c_master_read_from_device(port, address, (uint8_t *)buf, size, + I2C_OP_TIMEOUT)); + + if (res == ESP_ERR_TIMEOUT) + { + return -ETIMEDOUT; + } + else if (res == ESP_ERR_INVALID_STATE) + { + return -EINVAL; + } + else if (res != ESP_OK) + { + return -EIO; + } + return size; +} + +int Esp32HardwareI2C::open(const char *path, int flags, int mode) +{ + std::string path_str = path; + i2c_device_t new_dev = + { + .port = I2C_NUM_0, + .address = -1, + .fd = 0, + }; + + if (path_str.back() == '0') + { + new_dev.port = I2C_NUM_0; + } +#if SOC_I2C_NUM > 1 + else if (path_str.back() == '1') + { + new_dev.port = I2C_NUM_1; + } +#endif // SOC_I2C_NUM > 1 + else + { + LOG_ERROR("[I2C] Unsupported I2C path: %s", path); + return -ENOENT; + } + + if (!i2cInitialized_[new_dev.port]) + { + LOG_ERROR("[I2C] Uninitialized I2C path: %s", path); + return -ENODEV; + } + + // scan existing devices to find a unique file handle number to return to + // the caller. The file handle starts with zero and is set to the maximum + // file handle found plus one. When a file handle is reclaimed there will + // be gaps in the file handles which will not be reclaimed until entries + // with higher file handle numbers are also reclaimed. + { + AtomicHolder h(this); + + if (!devices_.empty()) + { + for (auto &entry: devices_) + { + if (entry.fd >= new_dev.fd) + { + new_dev.fd = entry.fd + 1; + } + } + } + devices_.push_back(new_dev); + } + + LOG(INFO, "[I2C] Using fd: %d (I2C%d) for %s", new_dev.fd, new_dev.port, + path); + + return new_dev.fd; +} + +int Esp32HardwareI2C::close(int fd) +{ + AtomicHolder h(this); + + auto entry = std::find_if(devices_.begin(), devices_.end(), + [fd](const auto &device) + { + return device.fd == fd; + }); + + // only delete the device if has been found. + if (entry != devices_.end()) + { + devices_.erase(entry); + } + return 0; +} + +int Esp32HardwareI2C::ioctl(int fd, int cmd, va_list args) +{ + AtomicHolder h(this); + HASSERT(IOC_TYPE(cmd) == I2C_MAGIC); + + auto entry = std::find_if(devices_.begin(), devices_.end(), + [fd](const auto &device) + { + return device.fd == fd; + }); + if (entry == devices_.end()) + { + errno = EBADF; + return -EBADF; + } + + switch(static_cast(cmd)) + { + default: + return -EINVAL; + case I2C_SLAVE: + entry->address = static_cast(va_arg(args, int)); + return 0; + case I2C_RDWR: + struct i2c_rdwr_ioctl_data *data = + reinterpret_cast(va_arg(args, uintptr_t)); + return transfer_messages(entry->port, data->msgs, data->nmsgs); + } + + return 0; +} + +int Esp32HardwareI2C::transfer_messages( + const i2c_port_t port, struct i2c_msg *msgs, int num) +{ + int total_len = 0; + esp_err_t res = ESP_OK; + + for (int idx = 0; idx < num; idx++) + { + struct i2c_msg *msg = msgs + idx; + if (msg->flags & I2C_M_RD) + { + res = + ESP_ERROR_CHECK_WITHOUT_ABORT( + i2c_master_read_from_device(port, msg->addr, + (uint8_t *)msg->buf, msg->len, I2C_OP_TIMEOUT)); + } + else + { + res = + ESP_ERROR_CHECK_WITHOUT_ABORT( + i2c_master_write_to_device(port, msg->addr, + (uint8_t *)msg->buf, msg->len, I2C_OP_TIMEOUT)); + } + if (res == ESP_ERR_TIMEOUT) + { + return -ETIMEDOUT; + } + else if (res == ESP_ERR_INVALID_STATE) + { + return -EINVAL; + } + else if (res != ESP_OK) + { + return -EIO; + } + total_len += msg->len; + } + return total_len; +} + +} // namespace openmrn_arduino + +#endif // ESP_PLATFORM \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32HardwareI2C.hxx b/src/freertos_drivers/esp32/Esp32HardwareI2C.hxx new file mode 100644 index 000000000..e4d47b2c1 --- /dev/null +++ b/src/freertos_drivers/esp32/Esp32HardwareI2C.hxx @@ -0,0 +1,230 @@ + +/** \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 Esp32HardwareI2C.hxx + * + * I2C driver implementation for OpenMRN. This leverages the ESP-IDF I2C driver + * implementation and provides a VFS interface. + * + * @author Mike Dunston + * @date 8 Feb 2023 + */ + +#ifndef _FREERTOS_DRIVERS_ESP32_ESP32HARDWAREI2C_HXX_ +#define _FREERTOS_DRIVERS_ESP32_ESP32HARDWAREI2C_HXX_ + +#include "i2c.h" +#include "i2c-dev.h" +#include +#include +#include +#include +#include + +namespace openmrn_arduino +{ + +/// The ESP32 has one or two built-in I2C controller interfaces, this code +/// provides a VFS interface that can be used by various I2C drivers to access +/// the underlying I2C bus. +/// +/// Only one instance of this class should be created in the application so the +/// memory overhead is minimized and performance of the VFS minimally impacted. +/// +/// Example usage (scanner): +/// ``` +/// Esp32HardwareI2C i2c; +/// +/// void setup() +/// { +/// i2c.hw_init(GPIO_NUM_4, GPIO_NUM_5, 100000); +/// i2c.scan(); +/// } +/// ``` +/// +/// Example usage (MCP23017): +/// ``` +/// Esp32HardwareI2C i2c; +/// Executor<1> io_executor("io_thread", 1, 1300); +/// MCP23017 expander(&io_executor, 0, 0, 0); +/// +/// void setup() +/// { +/// i2c.hw_init(GPIO_NUM_4, GPIO_NUM_5, 100000); +/// expander.init("/dev/i2c/0"); +/// ... +/// } +/// ``` +class Esp32HardwareI2C : private Atomic +{ +public: + /// Constructor. + /// + /// @param path Base path to use for I2C drivers. + Esp32HardwareI2C(const char * const path = "/dev/i2c"); + + /// Destructor. + ~Esp32HardwareI2C(); + + /// Initializes the underlying I2C controller hardware and VFS interface. + /// + /// @param sda GPIO pin to use for I2C data transfer. + /// @param scl GPIO pin to use for I2C clock. + /// @param bus_speed I2C clock frequency. + /// @param port I2C controller to initialize. + /// + /// For hardware which supports more than one I2C controller, this method + /// must be called once per controller being used. + /// + /// NOTE: This must be called prior to usage of any other methods for each + /// I2C controller being used. + void hw_init(const gpio_num_t sda, const gpio_num_t scl, + const uint32_t bus_speed, const i2c_port_t port = I2C_NUM_0); + + /// Utility method to perform a scan for all devices on the I2C bus. + /// + /// @param port Hardware I2C controller port number to use for scanning. + /// + /// NOTE: The hw_init method must have been run prior to calling this + /// method. Failure to do so will result in an error being raised. + void scan(const i2c_port_t port); + + /// VFS implementation of write(fd, buf, size) + /// + /// @param fd is the file descriptor being written to. + /// @param buf is the buffer containing the data to be written. + /// @param size is the size of the buffer. + /// + /// @return number of bytes written or -1 if there is the write would be a + /// blocking operation. + /// + /// NOTE: The provided fd is used internally to determine which I2C + /// controller should be used and the address to write the data to. If the + /// address has not been set + ssize_t write(int fd, const void *buf, size_t size); + + /// VFS implementation of read(fd, buf, size) + /// + /// @param fd is the file descriptor being read from. + /// @param buf is the buffer to write into. + /// @param size is the size of the buffer. + /// @return number of bytes read or -1 if there is the read would be a + /// blocking operation. + ssize_t read(int fd, void *buf, size_t size); + + /// VFS implementation of open(path, flags, mode). + /// + /// @param path is the path to the file being opened. + /// @param flags are the flags to use for opened file. + /// @param mode is the mode to use for the opened file. + /// + /// When this method is invoked it will enable the TWAI driver and start the + /// periodic timer used for RX/TX of frame data. + /// + /// @return 0 upon success, -1 upon failure with errno containing the cause. + int open(const char *path, int flags, int mode); + + /// VFS implementation of close(fd). + /// + /// @param fd is the file descriptor to close. + /// + /// When this method is invoked it will disable the TWAI driver and stop the + /// periodic timer used for RX/TX of frame data if it is running. + /// + /// @return zero upon success, negative value with errno for failure. + int close(int fd); + + /// VFS implementation of ioctl. + /// + /// @param fd is the file descriptor to operate on. + /// @param cmd is the command to execute. + /// @param args is the args for the command. + /// + /// @return zero upon success, negative value with errno for failure. + int ioctl(int fd, int cmd, va_list args); + +private: + /// VFS Mount point. + const char * const path_; + + /// Timeout to use for I2C operations, default is one second. + static constexpr TickType_t I2C_OP_TIMEOUT = pdMS_TO_TICKS(1000); + + /// Timeout to use for I2C device scanning, default is 50 milliseconds. + static constexpr TickType_t I2C_SCAN_TIMEOUT = pdMS_TO_TICKS(50); + + /// ISR flags to use for I2C, this defaults to allowing usage of a shared + /// interupt. + static constexpr int I2C_ISR_FLAGS = ESP_INTR_FLAG_SHARED; + + /// I2C "Slave" RX buffer size, we do not use/support this feature so it is + /// set to zero. + static constexpr size_t I2C_SLAVE_RX_BUF_SIZE = 0; + + /// I2C "Slave" TX buffer size, we do not use/support this feature so it is + /// set to zero. + static constexpr size_t I2C_SLAVE_TX_BUF_SIZE = 0; + + /// Tracking structure used to map file handles to an I2C controller and + /// address. + struct i2c_device_t + { + /// I2C Controller that this file handle will use. + i2c_port_t port; + + /// I2C address to use when greater than zero. + int address; + + /// Assigned file handle for this entry. + int fd; + }; + + /// Collection of I2C devices that have been opened. + std::vector devices_; + + /// Internal tracking for initialization of the underlying I2C hardware. + bool i2cInitialized_[SOC_I2C_NUM]; + + /// Internal tracking for the VFS adapter layer. + bool vfsInitialized_{false}; + + /// Transfers multiple payloads to I2C devices. + /// + /// @param port Underlying @ref i2c_port_t to use for I2C transfers. + /// @param msgs payloads to be transfered, this includes the target device + /// address, data to transfer and length of data. + /// @param num Number of transfers to perform. + /// + /// @return Number of bytes transfered or an error code (negative value). + int transfer_messages(const i2c_port_t port, struct i2c_msg *msgs, int num); +}; + +}; // namespace openmrn_arduino + +using openmrn_arduino::Esp32HardwareI2C; + +#endif // _FREERTOS_DRIVERS_ESP32_ESP32HARDWAREI2C_HXX_ \ No newline at end of file diff --git a/src/freertos_drivers/esp32/Esp32HardwareTwai.cxx b/src/freertos_drivers/esp32/Esp32HardwareTwai.cxx index 029af31c0..04d5c6b82 100644 --- a/src/freertos_drivers/esp32/Esp32HardwareTwai.cxx +++ b/src/freertos_drivers/esp32/Esp32HardwareTwai.cxx @@ -35,14 +35,10 @@ * @date 1 May 2021 */ -// Ensure we only compile this code for the ESP32 family of MCUs and that the -// ESP-IDF version is supported for this code. -#if defined(ESP32) +// Ensure we only compile this code for the ESP32 family of MCUs. +#if defined(ESP_PLATFORM) #include "sdkconfig.h" -#include - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) #if CONFIG_VFS_SUPPORT_TERMIOS // remove defines added by arduino-esp32 core/esp32/binary.h which are @@ -53,11 +49,12 @@ #include #include -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0) +#include + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,0) +#include +#endif #include -#else // IDF v4.x (or earlier) -#include -#endif // IDF v5+ #include #include #include @@ -610,7 +607,7 @@ static inline uint32_t twai_rx_frames() uint32_t rx_ready_count = twai_hal_get_rx_msg_count(&twai.context); struct can_frame *can_frame = nullptr; uint32_t rx_count = 0; - ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-ready-count: %d", rx_ready_count); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-ready-count: %" PRIu32, rx_ready_count); for (uint32_t idx = 0; idx < rx_ready_count; idx++) { twai_hal_frame_t frame; @@ -620,7 +617,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:%d", + ESP_EARLY_LOGE(TWAI_LOG_TAG, "rx-discard:%" PRIu32, twai.stats.rx_discard); } else if (twai.rx_buf->data_write_pointer(&can_frame)) @@ -652,13 +649,13 @@ static inline uint32_t twai_rx_frames() else { twai.stats.rx_missed++; - ESP_EARLY_LOGE(TWAI_LOG_TAG, "rx-missed:%d", + ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-missed:%" PRIu32, twai.stats.rx_missed); } } else { - ESP_EARLY_LOGE(TWAI_LOG_TAG, "rx-overrun"); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "rx-overrun"); // If the SOC does not support automatic clearing of the RX FIFO we need to // handle it here and break out of the loop. #ifndef SOC_TWAI_SUPPORTS_RX_STATUS @@ -716,7 +713,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: %08x", events); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "events: %04" PRIx32, events); #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || \ defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) @@ -788,14 +785,16 @@ static void twai_isr(void *arg) if (events & TWAI_HAL_EVENT_BUS_ERR) { twai.stats.bus_error++; - ESP_EARLY_LOGV(TWAI_LOG_TAG, "bus-error:%d", twai.stats.bus_error); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "bus-error:%" PRIu32, + 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:%d", twai.stats.arb_loss); + ESP_EARLY_LOGV(TWAI_LOG_TAG, "arb-lost:%" PRIu32, + twai.stats.arb_loss); } if (wakeup == pdTRUE) @@ -876,9 +875,11 @@ void* twai_watchdog(void* param) { LOG(INFO, "ESP-TWAI: " - "RX:%d (pending:%zu,overrun:%d,discard:%d,missed:%d,lost:%d) " - "TX:%d (pending:%zu,suc:%d,fail:%d) " - "Bus (arb-loss:%d,err:%d,state:%s)", + "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)", twai.stats.rx_processed, twai.rx_buf->pending(), twai.stats.rx_overrun, twai.stats.rx_discard, twai.stats.rx_missed, twai.stats.rx_lost, @@ -1005,12 +1006,46 @@ void Esp32HardwareTwai::hw_init() periph_module_reset(PERIPH_TWAI_MODULE); periph_module_enable(PERIPH_TWAI_MODULE); - HASSERT(twai_hal_init(&twai.context)); + twai_timing_config_t timingCfg = TWAI_TIMING_CONFIG_125KBITS(); twai_filter_config_t filterCfg = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,0) + // default clock source if not specified in config. + if (timingCfg.clk_src == 0) + { + timingCfg.clk_src = TWAI_CLK_SRC_DEFAULT; + } + twai_hal_config_t twai_hal_cfg = + { + .controller_id = 0, + .clock_source_hz = 0, + }; + + // retrieve the clock frequency from the SoC + esp_clk_tree_src_get_freq_hz((soc_module_clk_t)timingCfg.clk_src, + ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &twai_hal_cfg.clock_source_hz); + + // BRP validations + uint32_t brp = timingCfg.brp; + if (timingCfg.quanta_resolution_hz) + { + HASSERT(twai_hal_cfg.clock_source_hz % timingCfg.quanta_resolution_hz == 0); + brp = twai_hal_cfg.clock_source_hz / timingCfg.quanta_resolution_hz; + } + HASSERT(twai_ll_check_brp_validation(brp)); + + // Initialize the low level HAL APIs + HASSERT(twai_hal_init(&twai.context, &twai_hal_cfg)); +#else + // Initialize the low level HAL APIs + HASSERT(twai_hal_init(&twai.context)); +#endif // IDF v5.1+ + LOG(VERBOSE, "ESP-TWAI: Initiailizing peripheral"); twai_hal_configure(&twai.context, &timingCfg, &filterCfg, TWAI_DEFAULT_INTERRUPTS, 0); + #if SOC_CPU_CORES_NUM > 1 ESP_ERROR_CHECK( esp_ipc_call_blocking(preferredIsrCore_, esp32_twai_isr_init, nullptr)); @@ -1031,6 +1066,4 @@ void Esp32HardwareTwai::get_driver_stats(esp32_twai_stats_t *stats) } // namespace openmrn_arduino -#endif // IDF v4.3+ - -#endif // ESP32 +#endif // ESP_PLATFORM diff --git a/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx b/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx index b934d6125..957cb8aa2 100644 --- a/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx +++ b/src/freertos_drivers/esp32/Esp32HardwareTwai.hxx @@ -37,12 +37,6 @@ #ifndef _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ #define _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ -#include - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0) -#error Esp32HardwareTwai is only supported on ESP-IDF v4.3 and above. -#endif // IDF v4.3+ - #include #include #include @@ -222,4 +216,4 @@ private: using openmrn_arduino::esp32_twai_stats_t; using openmrn_arduino::Esp32HardwareTwai; -#endif // _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ \ No newline at end of file +#endif // _FREERTOS_DRIVERS_ESP32_ESP32HARDWARETWAI_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32Ledc.cxx b/src/freertos_drivers/esp32/Esp32Ledc.cxx index 0d856f064..7dfa35b83 100644 --- a/src/freertos_drivers/esp32/Esp32Ledc.cxx +++ b/src/freertos_drivers/esp32/Esp32Ledc.cxx @@ -33,11 +33,7 @@ */ // Ensure we only compile this code for the ESP32 family of MCUs. -#if defined(ESP32) - -#include - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) +#if defined(ESP_PLATFORM) #include "Esp32Ledc.hxx" @@ -48,6 +44,4 @@ pthread_once_t Esp32Ledc::ledcFadeOnce_ = PTHREAD_ONCE_INIT; } // namespace openmrn_arduino -#endif // IDF v4.3+ - -#endif // ESP32 \ No newline at end of file +#endif // ESP_PLATFORM diff --git a/src/freertos_drivers/esp32/Esp32Ledc.hxx b/src/freertos_drivers/esp32/Esp32Ledc.hxx index 72d5bfff8..a3fb991a8 100644 --- a/src/freertos_drivers/esp32/Esp32Ledc.hxx +++ b/src/freertos_drivers/esp32/Esp32Ledc.hxx @@ -111,7 +111,7 @@ public: { // Ensure the pin count is valid and within range of usable channels. HASSERT(pins_.size() > 0 && - pins_.size() < (LEDC_CHANNEL_MAX - first_channel)); + pins_.size() <= (LEDC_CHANNEL_MAX - first_channel)); memset(&timerConfig_, 0, sizeof(ledc_timer_config_t)); // timerConfig_.speed_mode will be assigned the SOC default mode, which // is either HIGH speed or LOW speed depending on the hardware support. @@ -132,7 +132,8 @@ public: void hw_init() { LOG(INFO, - "[Esp32Ledc:%d] Configuring timer (resolution:%d, frequency:%d)", + "[Esp32Ledc:%d] Configuring timer (resolution:%d, frequency:%" + PRIu32 ")", timerConfig_.timer_num, (1 << (uint8_t)timerConfig_.duty_resolution) - 1, timerConfig_.freq_hz); @@ -277,6 +278,7 @@ private: { ESP_ERROR_CHECK( ledc_set_duty(timerConfig_.speed_mode, channel, counts)); + ESP_ERROR_CHECK(ledc_update_duty(timerConfig_.speed_mode, channel)); } /// Gets the duty cycle. @@ -323,4 +325,4 @@ private: using openmrn_arduino::Esp32Ledc; -#endif // _DRIVERS_ESP32LEDC_HXX_ \ No newline at end of file +#endif // _DRIVERS_ESP32LEDC_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32SocInfo.cxx b/src/freertos_drivers/esp32/Esp32SocInfo.cxx index e56611b2f..db4a50cc6 100644 --- a/src/freertos_drivers/esp32/Esp32SocInfo.cxx +++ b/src/freertos_drivers/esp32/Esp32SocInfo.cxx @@ -33,16 +33,13 @@ * @date 4 May 2021 */ -#if defined(ESP32) +#if defined(ESP_PLATFORM) #include "freertos_drivers/esp32/Esp32SocInfo.hxx" #include "utils/logging.h" #include - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,4,0) #include -#endif // IDF v4.4+ namespace openmrn_arduino { @@ -265,25 +262,14 @@ uint8_t Esp32SocInfo::print_soc_info() chip_info.features & CHIP_FEATURE_BLE ? "Yes" : "No", chip_info.features & CHIP_FEATURE_BT ? "Yes" : "No"); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) LOG(INFO, "[SoC] Heap: %.2fkB / %.2fkB", heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024.0f, heap_caps_get_total_size(MALLOC_CAP_INTERNAL) / 1024.0f); -#if CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM +#if CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM || CONFIG_SPIRAM LOG(INFO, "[SoC] PSRAM: %.2fkB / %.2fkB", heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024.0f, heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024.0f); -#endif // CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM - -#else // NOT IDF v4.3+ - LOG(INFO, "[SoC] Free Heap: %.2fkB", - heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024.0f); -#if CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM - LOG(INFO, "[SoC] Free PSRAM: %.2fkB", - heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024.0f); -#endif // CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM - -#endif // IDF v4.3+ +#endif // CONFIG_SPIRAM_SUPPORT || BOARD_HAS_PSRAM || CONFIG_SPIRAM LOG(INFO, "[SoC] App running from partition: %s", esp_ota_get_running_partition()->label); @@ -297,4 +283,4 @@ uint8_t Esp32SocInfo::print_soc_info() } // namespace openmrn_arduino -#endif // ESP32 \ No newline at end of file +#endif // ESP_PLATFORM diff --git a/src/freertos_drivers/esp32/Esp32SocInfo.hxx b/src/freertos_drivers/esp32/Esp32SocInfo.hxx index e683b8309..f55cc260b 100644 --- a/src/freertos_drivers/esp32/Esp32SocInfo.hxx +++ b/src/freertos_drivers/esp32/Esp32SocInfo.hxx @@ -36,17 +36,12 @@ #include -#if defined(ESP32) +#if defined(ESP_PLATFORM) #include "sdkconfig.h" -#include #if defined(CONFIG_IDF_TARGET_ESP32) -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) #include -#else -#include -#endif // IDF v4.3+ #elif defined(CONFIG_IDF_TARGET_ESP32S2) #include #elif defined(CONFIG_IDF_TARGET_ESP32S3) @@ -76,6 +71,6 @@ public: using openmrn_arduino::Esp32SocInfo; -#endif // ESP32 +#endif // ESP_PLATFORM -#endif // _FREERTOS_DRIVERS_ESP32_ESP32SOCINFO_HXX_ \ No newline at end of file +#endif // _FREERTOS_DRIVERS_ESP32_ESP32SOCINFO_HXX_ diff --git a/src/freertos_drivers/esp32/Esp32WiFiManager.cxx b/src/freertos_drivers/esp32/Esp32WiFiManager.cxx index 9bd94acbd..5471345f8 100644 --- a/src/freertos_drivers/esp32/Esp32WiFiManager.cxx +++ b/src/freertos_drivers/esp32/Esp32WiFiManager.cxx @@ -32,8 +32,8 @@ * @date 4 February 2019 */ -// Ensure we only compile this code on ESP32 MCUs -#ifdef ESP32 +// Ensure we only compile this code for ESP32 MCUs +#ifdef ESP_PLATFORM #include "Esp32WiFiManager.hxx" #include "openlcb/SimpleStack.hxx" @@ -53,7 +53,6 @@ // ESP-IDF v4+ has a slightly different directory structure to previous // versions. -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) #include #include #include @@ -67,12 +66,7 @@ #else // default to ESP32 #include #endif // CONFIG_IDF_TARGET - -#else // ESP-IDF v3.x -#include -#include -#include -#endif // ESP_IDF_VERSION +#include using openlcb::NodeID; using openlcb::SimpleCanStackBase; @@ -317,17 +311,12 @@ Esp32WiFiManager::Esp32WiFiManager(const char *station_ssid Esp32WiFiManager::~Esp32WiFiManager() { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) // Remove our event listeners from the event loop, note that we do not stop // the event loop. esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID , &Esp32WiFiManager::process_idf_event); esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID , &Esp32WiFiManager::process_idf_event); -#else - // Disconnect from the event loop to prevent a possible null deref. - esp_event_loop_set_cb(nullptr, nullptr); -#endif // IDF v4.1+ // shutdown the background task executor. executor_.shutdown(); @@ -405,8 +394,8 @@ ConfigUpdateListener::UpdateAction Esp32WiFiManager::apply_configuration( // Calculate CRC32 from the loaded buffer. uint32_t configCrc32 = crc32_le(0, crcbuf.get(), cfg_.size()); - LOG(VERBOSE, "existing config CRC32:%d, new CRC32:%d", configCrc32_, - configCrc32); + LOG(VERBOSE, "existing config CRC32:%" PRIu32 ", new CRC32:%" PRIu32, + configCrc32_, configCrc32); if (initial_load) { @@ -480,12 +469,11 @@ void Esp32WiFiManager::factory_reset(int fd) CDI_FACTORY_RESET(cfg_.uplink().reconnect); } -#if defined(ESP_IDF_VERSION) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) void Esp32WiFiManager::process_idf_event(void *arg, esp_event_base_t event_base , int32_t event_id, void *event_data) { - LOG(VERBOSE, "Esp32WiFiManager::process_idf_event(%s, %d, %p)", event_base - , event_id, event_data); + LOG(VERBOSE, "Esp32WiFiManager::process_idf_event(%s, %" PRIi32 ", %p)", + event_base, event_id, event_data); if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { Singleton::instance()->on_station_started(); @@ -540,65 +528,6 @@ void Esp32WiFiManager::process_idf_event(void *arg, esp_event_base_t event_base } } -#else -// Processes a WiFi system event -esp_err_t Esp32WiFiManager::process_wifi_event(void *ctx, system_event_t *event) -{ - LOG(VERBOSE, "Esp32WiFiManager::process_wifi_event(%d)", event->event_id); - - if (event->event_id == SYSTEM_EVENT_STA_START) - { - Singleton::instance()->on_station_started(); - } - else if (event->event_id == SYSTEM_EVENT_STA_CONNECTED) - { - Singleton::instance()->on_station_connected(); - } - else if (event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) - { - Singleton::instance()->on_station_disconnected( - event->event_info.disconnected.reason); - } - else if (event->event_id == SYSTEM_EVENT_STA_GOT_IP) - { - Singleton::instance()->on_station_ip_assigned( - htonl(event->event_info.got_ip.ip_info.ip.addr)); - } - else if (event->event_id == SYSTEM_EVENT_STA_LOST_IP) - { - Singleton::instance()->on_station_ip_lost(); - } - else if (event->event_id == SYSTEM_EVENT_AP_START) - { - Singleton::instance()->on_softap_start(); - } - else if (event->event_id == SYSTEM_EVENT_AP_STOP) - { - Singleton::instance()->on_softap_stop(); - } - else if (event->event_id == SYSTEM_EVENT_AP_STACONNECTED) - { - auto sta_data = event->event_info.sta_connected; - Singleton::instance()->on_softap_station_connected( - sta_data.mac, sta_data.aid); - } - else if (event->event_id == SYSTEM_EVENT_AP_STADISCONNECTED) - { - auto sta_data = event->event_info.sta_connected; - Singleton::instance()->on_softap_station_disconnected( - sta_data.mac, sta_data.aid); - } - else if (event->event_id == SYSTEM_EVENT_SCAN_DONE) - { - auto scan_data = event->event_info.scan_done; - Singleton::instance()->on_wifi_scan_completed( - scan_data.status, scan_data.number); - } - - return ESP_OK; -} -#endif - // Adds a callback which will be called when the network is up. void Esp32WiFiManager::register_network_up_callback( esp_network_up_callback_t callback) @@ -641,20 +570,27 @@ void Esp32WiFiManager::stop_hub() // Creates a hub listener for this node after loading configuration details. void Esp32WiFiManager::start_hub() { - hubServiceName_ = cfg_.hub().service_name().read(configFd_); - uint16_t hub_port = CDI_READ_TRIMMED(cfg_.hub().port, configFd_); - - LOG(INFO, "[Hub] Starting TCP/IP listener on port %d", hub_port); auto stack = static_cast(stack_); - stack->start_tcp_hub_server(hub_port); - auto hub = stack->get_tcp_hub_server(); - // wait for the hub to complete it's startup tasks - while (!hub->is_started()) + // Check if we already have a GC Hub running or not, if we do then we can + // skip creating one. It would be best to validate the port in use but it + // is not exposed by GcHub at this time. + if (!stack->get_tcp_hub_server()) { - usleep(HUB_STARTUP_DELAY_USEC); + hubServiceName_ = cfg_.hub().service_name().read(configFd_); + uint16_t hub_port = CDI_READ_TRIMMED(cfg_.hub().port, configFd_); + LOG(INFO, "[HUB] Starting TCP/IP listener on port %" PRIu16, hub_port); + stack->start_tcp_hub_server(hub_port); + auto hub = stack->get_tcp_hub_server(); + + // wait for the hub to complete it's startup tasks + while (!hub->is_started()) + { + usleep(HUB_STARTUP_DELAY_USEC); + } + + mdns_publish(hubServiceName_, hub_port); } - mdns_publish(hubServiceName_, hub_port); } // Disconnects and shuts down the uplink connector socket if running. @@ -807,9 +743,9 @@ void Esp32WiFiManager::mdns_publish(string service, const uint16_t port) split_mdns_service_name(&service_name, &protocol_name); esp_err_t res = mdns_service_add( NULL, service_name.c_str(), protocol_name.c_str(), port, NULL, 0); - LOG(VERBOSE, "[mDNS] mdns_service_add(%s.%s:%d):%s." - , service_name.c_str(), protocol_name.c_str(), port - , esp_err_to_name(res)); + LOG(VERBOSE, "[mDNS] mdns_service_add(%s.%s:%" PRIu16 "):%s.", + service_name.c_str(), protocol_name.c_str(), port, + esp_err_to_name(res)); // ESP_FAIL will be triggered if there is a timeout during publish of // the new mDNS entry. The mDNS task runs at a very low priority on the // PRO_CPU which is also where the OpenMRN Executor runs from which can @@ -833,12 +769,12 @@ void Esp32WiFiManager::mdns_publish(string service, const uint16_t port) } else if (res == ESP_OK) { - LOG(INFO, "[mDNS] Advertising %s.%s:%d.", service_name.c_str(), - protocol_name.c_str(), port); + LOG(INFO, "[mDNS] Advertising %s.%s:%" PRIu16 ".", + service_name.c_str(), protocol_name.c_str(), port); } else { - LOG_ERROR("[mDNS] Failed to publish:%s.%s:%d", + LOG_ERROR("[mDNS] Failed to publish:%s.%s:%" PRIu16, service_name.c_str(), protocol_name.c_str(), port); Singleton::instance()->mdns_publish( service, port); @@ -913,12 +849,7 @@ void Esp32WiFiManager::on_station_started() // so that it shows up with the generated hostname instead of // the default "Espressif". LOG(INFO, "[Station] Setting hostname to \"%s\".", hostname_.c_str()); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) esp_netif_set_hostname(espNetIfaces_[STATION_INTERFACE], hostname_.c_str()); -#else - ESP_ERROR_CHECK(tcpip_adapter_set_hostname( - TCPIP_ADAPTER_IF_STA, hostname_.c_str())); -#endif uint8_t mac[6]; esp_wifi_get_mac(WIFI_IF_STA, mac); LOG(INFO, "[Station] MAC Address:%s", mac_to_string(mac).c_str()); @@ -926,11 +857,7 @@ void Esp32WiFiManager::on_station_started() // Start the DHCP service before connecting so it hooks into // the flow early and provisions the IP automatically. LOG(INFO, "[Station] Starting DHCP Client."); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) ESP_ERROR_CHECK(esp_netif_dhcpc_start(espNetIfaces_[STATION_INTERFACE])); -#else - ESP_ERROR_CHECK(tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA)); -#endif // IDF v4.1+ LOG(INFO, "[Station] Connecting to SSID:%s.", ssid_.c_str()); // Start the SSID connection process. @@ -1077,7 +1004,6 @@ void Esp32WiFiManager::on_softap_start() esp_wifi_get_mac(WIFI_IF_AP, mac); LOG(INFO, "[SoftAP] MAC Address:%s", mac_to_string(mac).c_str()); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) // Set the generated hostname prior to connecting to the SSID // so that it shows up with the generated hostname instead of // the default "Espressif". @@ -1091,21 +1017,6 @@ void Esp32WiFiManager::on_softap_start() ESP_ERROR_CHECK( esp_netif_get_ip_info(espNetIfaces_[SOFTAP_INTERFACE], &ip_info)); ip_address = ntohl(ip4_addr_get_u32(&ip_info.ip)); -#else - // Set the generated hostname prior to connecting to the SSID - // so that it shows up with the generated hostname instead of - // the default "Espressif". - LOG(INFO, "[SoftAP] Setting hostname to \"%s\".", hostname_.c_str()); - ESP_ERROR_CHECK(tcpip_adapter_set_hostname( - TCPIP_ADAPTER_IF_AP, hostname_.c_str())); - - // fetch the IP address from the adapter since it defaults to - // 192.168.4.1 but can be altered via sdkconfig. - tcpip_adapter_ip_info_t ip_info; - ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP - , &ip_info)); - ip_address = ntohl(ip4_addr_get_u32(&ip_info.ip)); -#endif // IDF v4.1+ LOG(INFO, "[SoftAP] IP address:%s", ipv4_to_string(ip_address).c_str()); @@ -1180,7 +1091,7 @@ void Esp32WiFiManager::on_wifi_scan_completed(uint32_t status, uint8_t count) { uint16_t num_found = count; esp_wifi_scan_get_ap_num(&num_found); - LOG(VERBOSE, "[WiFi] %d SSIDs found via scan", num_found); + LOG(VERBOSE, "[WiFi] %" PRIu16 " SSIDs found via scan", num_found); ssidScanResults_.resize(num_found); esp_wifi_scan_get_ap_records(&num_found, ssidScanResults_.data()); #if LOGLEVEL >= VERBOSE @@ -1226,12 +1137,17 @@ void Esp32WiFiManager::configure_sntp() { sntpConfigured_ = true; LOG(INFO, "[SNTP] Polling %s for time updates", sntpServer_.c_str()); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,0) + esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); + esp_sntp_setservername(0, sntpServer_.c_str()); + sntp_set_time_sync_notification_cb(sntp_update_received); + esp_sntp_init(); +#else sntp_setoperatingmode(SNTP_OPMODE_POLL); - // IDF v3.3 does not offer const correctness so we need to drop const - // when setting the hostname for SNTP. - sntp_setservername(0, const_cast(sntpServer_.c_str())); + sntp_setservername(0, sntpServer_.c_str()); sntp_set_time_sync_notification_cb(sntp_update_received); sntp_init(); +#endif // IDF v5.1+ if (!timeZone_.empty()) { @@ -1296,7 +1212,6 @@ StateFlowBase::Action Esp32WiFiManager::WiFiStackFlow::noop() StateFlowBase::Action Esp32WiFiManager::WiFiStackFlow::init_interface() { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) // create default interfaces for station and SoftAP, ethernet is not used // today. ESP_ERROR_CHECK(esp_netif_init()); @@ -1323,15 +1238,6 @@ StateFlowBase::Action Esp32WiFiManager::WiFiStackFlow::init_interface() &Esp32WiFiManager::process_idf_event, nullptr); esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &Esp32WiFiManager::process_idf_event, nullptr); -#else // NOT IDF v4.1+ - // Initialize the TCP/IP adapter stack. - LOG(INFO, "[WiFi] Starting TCP/IP stack"); - tcpip_adapter_init(); - - // Install event loop handler. - ESP_ERROR_CHECK( - esp_event_loop_init(&Esp32WiFiManager::process_wifi_event, nullptr)); -#endif // IDF v4.1+ return yield_and_call(STATE(init_wifi)); } @@ -1419,11 +1325,7 @@ StateFlowBase::Action Esp32WiFiManager::WiFiStackFlow::configure_station() } LOG(INFO, "[WiFi] Configuring Station (SSID:%s)", conf.sta.ssid); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &conf)); -#else - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &conf)); -#endif // IDF v4.0+ return yield_and_call(STATE(start_wifi)); } @@ -1478,11 +1380,7 @@ StateFlowBase::Action Esp32WiFiManager::WiFiStackFlow::configure_softap() } LOG(INFO, "[WiFi] Configuring SoftAP (SSID:%s)", conf.ap.ssid); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,3,0) ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &conf)); -#else - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &conf)); -#endif // IDF v4.0+ // If we are only enabling the SoftAP we can transition to starting the // WiFi stack. @@ -1722,7 +1620,7 @@ int mdns_lookup( if (ipaddr->addr.type == IPADDR_TYPE_V4) { LOG(ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL, - "[mDNS] Found %s as providing service:%s on port %d.", + "[mDNS] Found %s providing service:%s on port %" PRIu16, res->hostname, service, res->port); inet_addr_from_ip4addr( &sa_in->sin_addr, &ipaddr->addr.u_addr.ip4); @@ -1762,17 +1660,11 @@ int mdns_lookup( /// @return zero for success, -1 for failure. int getifaddrs(struct ifaddrs **ifap) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0) esp_netif_ip_info_t ip_info; -#else - tcpip_adapter_ip_info_t ip_info; -#endif // IDF v5+ /* start with something "safe" in case we bail out early */ *ifap = nullptr; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0) - // Lookup the interface by it's internal name assigned by ESP-IDF. esp_netif_t *iface = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); if (iface == nullptr) @@ -1789,17 +1681,6 @@ int getifaddrs(struct ifaddrs **ifap) errno = EADDRNOTAVAIL; return -1; } -#else // IDF v4 (or lower) - if (!tcpip_adapter_is_netif_up(TCPIP_ADAPTER_IF_STA)) - { - // Station TCP/IP interface is not up - errno = ENODEV; - return -1; - } - - // retrieve TCP/IP address from the interface - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info); -#endif // IDF v5+ // allocate memory for various pieces of ifaddrs std::unique_ptr ia(new struct ifaddrs); diff --git a/src/freertos_drivers/esp32/Esp32WiFiManager.hxx b/src/freertos_drivers/esp32/Esp32WiFiManager.hxx index a1996c8dd..7ad7dae6d 100644 --- a/src/freertos_drivers/esp32/Esp32WiFiManager.hxx +++ b/src/freertos_drivers/esp32/Esp32WiFiManager.hxx @@ -46,16 +46,10 @@ #include "utils/macros.h" #include "utils/Singleton.hxx" -#include -#include #include -#include - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) #include -#else -#include -#endif // IDF v4.1+ +#include +#include namespace openlcb { @@ -220,7 +214,6 @@ public: /// @param fd is the file descriptor used for the configuration settings. void factory_reset(int fd) override; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) /// Processes an event coming from the ESP-IDF default event loop. /// /// @param ctx context parameter (unused). @@ -231,22 +224,6 @@ public: /// NOTE: This is not intended to be called by the user. static void process_idf_event(void *ctx, esp_event_base_t event_base , int32_t event_id, void *event_data); -#else - /// Processes an ESP-IDF WiFi event based on the event raised by the - /// ESP-IDF event loop processor. This should be used when the - /// Esp32WiFiManager is not managing the WiFi or MDNS systems so that - /// it can react to WiFi events to cleanup or recreate the hub or uplink - /// connections as required. When Esp32WiFiManager is managing the WiFi - /// connection this method will be called automatically from the - /// esp_event_loop. Note that ESP-IDF only supports one callback being - /// registered. - /// - /// @param ctx context parameter (unused). - /// @param event is the system_event_t raised by ESP-IDF. - /// - /// NOTE: This is not intended to be called by the user. - static esp_err_t process_wifi_event(void *ctx, system_event_t *event); -#endif /// If called, sets the ESP32 wifi stack to log verbose information to the /// console. @@ -601,13 +578,11 @@ private: /// Constant used to determine if the Hub mode should be enabled. static constexpr uint8_t CONN_MODE_HUB_BIT = BIT(1); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,1,0) /// Network interfaces that are managed by Esp32WiFiManager. esp_netif_t *espNetIfaces_[MAX_NETWORK_INTERFACES] { nullptr, nullptr }; -#endif // IDF v4.1+ /// This class provides a proxy for the @ref Notifiable provided by the /// @ref SocketClient. This is necessary to ensure the @ref Notifiable does diff --git a/src/openlcb/ApplicationChecksum.hxx b/src/openlcb/ApplicationChecksum.hxx index 0512a966a..4b6e390bb 100644 --- a/src/openlcb/ApplicationChecksum.hxx +++ b/src/openlcb/ApplicationChecksum.hxx @@ -35,7 +35,7 @@ #ifndef _OPENLCB_APPLICATIONCHECKSUM_HXX_ #define _OPENLCB_APPLICATIONCHECKSUM_HXX_ -#ifdef ESP32 +#ifdef ESP_PLATFORM #include "bootloader_hal.h" #else #include "freertos/bootloader_hal.h" diff --git a/src/openlcb/Bootloader.hxx b/src/openlcb/Bootloader.hxx index 8bc426faa..713e86d05 100644 --- a/src/openlcb/Bootloader.hxx +++ b/src/openlcb/Bootloader.hxx @@ -43,7 +43,7 @@ #include #include -#ifdef ESP32 +#ifdef ESP_PLATFORM #include "bootloader_hal.h" #else #include "freertos/bootloader_hal.h" diff --git a/src/openlcb/BroadcastTime.cxx b/src/openlcb/BroadcastTime.cxx index e74e57103..11444f107 100644 --- a/src/openlcb/BroadcastTime.cxx +++ b/src/openlcb/BroadcastTime.cxx @@ -46,7 +46,7 @@ namespace openlcb // void BroadcastTime::clear_timezone() { -#ifndef ESP32 +#ifndef ESP_PLATFORM setenv("TZ", "GMT0", 1); tzset(); #endif diff --git a/src/openlcb/ServoConsumer.hxx b/src/openlcb/ServoConsumer.hxx index 9783244c6..29771cbb9 100644 --- a/src/openlcb/ServoConsumer.hxx +++ b/src/openlcb/ServoConsumer.hxx @@ -1,7 +1,7 @@ #ifndef _OPENLCB_SERVOCONSUMER_HXX_ #define _OPENLCB_SERVOCONSUMER_HXX_ -#if defined(ARDUINO) || defined(ESP32) +#if defined(ARDUINO) || defined(ESP_PLATFORM) #include "freertos_drivers/arduino/DummyGPIO.hxx" #include "freertos_drivers/arduino/PWM.hxx" #else diff --git a/src/os/OS.hxx b/src/os/OS.hxx index 50edeb6cd..bbf752c8d 100644 --- a/src/os/OS.hxx +++ b/src/os/OS.hxx @@ -733,7 +733,7 @@ private: /** handle to event object */ EventGroupHandle_t event; }; -#elif defined(ARDUINO) && !defined(ESP32) +#elif defined(ARDUINO) && !defined(ESP_PLATFORM) typedef uint32_t OSEventType; diff --git a/src/os/OSSelectWakeup.cxx b/src/os/OSSelectWakeup.cxx index 6d1fef562..2ee394bff 100644 --- a/src/os/OSSelectWakeup.cxx +++ b/src/os/OSSelectWakeup.cxx @@ -74,7 +74,7 @@ int OSSelectWakeup::select(int nfds, fd_set *readfds, int ret = ::pselect(nfds, readfds, writefds, exceptfds, &timeout, &origMask_); #elif OPENMRN_HAVE_SELECT -#ifdef ESP32 +#ifdef ESP_PLATFORM fd_set newexcept; if (!exceptfds) { @@ -86,7 +86,7 @@ int OSSelectWakeup::select(int nfds, fd_set *readfds, { nfds = vfsFd_ + 1; } -#endif //ESP32 +#endif // ESP_PLATFORM struct timeval timeout; // divide in two steps to avoid overflow on ESP32 timeout.tv_sec = (deadline_nsec / 1000) / 1000000LL; @@ -107,12 +107,13 @@ int OSSelectWakeup::select(int nfds, fd_set *readfds, return ret; } -#ifdef ESP32 +#ifdef ESP_PLATFORM #include "freertos_includes.h" #include #include #include +#include #include #include @@ -129,16 +130,7 @@ static pthread_key_t select_wakeup_key; /// all VFS APIs we implement. static constexpr int WAKEUP_VFS_FD = 0; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,0,0) -extern "C" -{ - void *sys_thread_sem_get(); - void sys_sem_signal(void *); - void sys_sem_signal_isr(void *); -} -#endif // NOT IDF v4.0+ -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) /// This function is called by the ESP32's select implementation. It is passed /// in as a function pointer to the VFS API. /// @param nfds see standard select API @@ -150,31 +142,12 @@ extern "C" /// @param end_select_args are the arguments to pass to end_select upon wakeup. static esp_err_t esp_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t signal_sem, void **end_select_args) -#else // NOT IDF v4.0+ -/// This function is called by the ESP32's select implementation. It is passed -/// in as a function pointer to the VFS API. -/// @param nfds see standard select API -/// @param readfds see standard select API -/// @param writefds see standard select API -/// @param exceptfds see standard select API -/// @param signal_sem if non-NULL, the select can be woken up by notifying this -/// semaphore. If NULL, the select can be woken up by notifying the LWIP -/// semaphore. By the API contract this pointer needs to be passed into -/// esp_vfs_select_triggered. -static esp_err_t esp_start_select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, esp_vfs_select_sem_t signal_sem) -#endif // IDF v4.0+ { OSSelectWakeup *parent = (OSSelectWakeup *)pthread_getspecific(select_wakeup_key); HASSERT(parent); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) LOG(VERBOSE, "esp start select %p (thr %p parent %p)", signal_sem.sem, os_thread_self(), parent); -#else // NOT IDF v4.0+ - LOG(VERBOSE, "esp start select %p (thr %p parent %p)", signal_sem, - os_thread_self(), parent); -#endif // IDF v4.0+ // Check if our VFS FD is included in exceptfds before tracking that we // should possibly wake up early. @@ -185,7 +158,6 @@ static esp_err_t esp_start_select(int nfds, fd_set *readfds, fd_set *writefds, return ESP_OK; } -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) /// This function is called inline from the ESP32's select implementation. It is /// passed in as a function pointer to the VFS API. /// @@ -193,11 +165,6 @@ static esp_err_t esp_start_select(int nfds, fd_set *readfds, fd_set *writefds, /// is not used today. /// @return always returns ESP_OK as this is a no-op. static esp_err_t esp_end_select(void *arg) -#else // NOT IDF v4.0+ -/// This function is called inline from the ESP32's select implementation. It is -/// passed in as a function pointer to the VFS API. -static void esp_end_select() -#endif // IDF v4.0+ { OSSelectWakeup *parent = (OSSelectWakeup *)pthread_getspecific(select_wakeup_key); @@ -205,9 +172,7 @@ static void esp_end_select() LOG(VERBOSE, "esp end select (thr %p parent %p)", os_thread_self(), parent); parent->esp_end_select(); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) return ESP_OK; -#endif // IDF v4.0+ } /// This function is called by the ESP32's select implementation. @@ -253,28 +218,8 @@ void OSSelectWakeup::esp_wakeup() // and not the system global FD. FD_SET(WAKEUP_VFS_FD, exceptFds_); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) LOG(VERBOSE, "wakeup es %p %u", espSem_.sem, *(unsigned*)espSem_.sem); esp_vfs_select_triggered(espSem_); -#else // IDF v3.x - LOG(VERBOSE, "wakeup es %p %u", espSem_, *(unsigned*)espSem_); - if (espSem_) - { - // Mark the VFS implementation FD for the wakeup call. Note that this - // should not use vfsFd_ since the fd_set will contain the VFS specific - // FD and not the system global FD. - esp_vfs_select_triggered(espSem_); - } - else - { - // Works around a bug in the implementation of - // esp_vfs_select_triggered, which internally calls - // sys_sem_signal(sys_thread_sem_get()); This is buggy because - // sys_thread_sem_get() will get the semaphore that belongs to the - // calling thread, not the target thread to wake up. - sys_sem_signal(lwipSem_); - } -#endif // IDF >= v4.0 } /// This function will trigger the ESP32 to wake up from any pending select() @@ -295,23 +240,7 @@ void OSSelectWakeup::esp_wakeup_from_isr() // and not the system global FD. FD_SET(WAKEUP_VFS_FD, exceptFds_); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4,0,0) esp_vfs_select_triggered_isr(espSem_, &woken); -#else // IDF v3.x - if (espSem_) - { - esp_vfs_select_triggered_isr(espSem_, &woken); - } - else - { - // Works around a bug in the implementation of - // esp_vfs_select_triggered, which internally calls - // sys_sem_signal(sys_thread_sem_get()); This is buggy because - // sys_thread_sem_get() will get the semaphore that belongs to the - // calling thread, not the target thread to wake up. - sys_sem_signal_isr(lwipSem_); - } -#endif // IDF >= v4.0 if (woken == pdTRUE) { @@ -339,10 +268,6 @@ static void esp_vfs_init() void OSSelectWakeup::esp_allocate_vfs_fd() { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,0,0) - lwipSem_ = sys_thread_sem_get(); -#endif // IDF < v4.0 - HASSERT(0 == pthread_once(&vfs_init_once, &esp_vfs_init)); vfsFd_ = ::open("/dev/wakeup/0", 0, 0); HASSERT(vfsFd_ >= 0); @@ -360,4 +285,4 @@ void OSSelectWakeup::esp_deallocate_vfs_fd() vfsFd_ = -1; } -#endif // ESP32 +#endif // ESP_PLATFORM diff --git a/src/os/OSSelectWakeup.hxx b/src/os/OSSelectWakeup.hxx index a6c8aca87..57f1ea708 100644 --- a/src/os/OSSelectWakeup.hxx +++ b/src/os/OSSelectWakeup.hxx @@ -54,7 +54,7 @@ #include #endif -#ifdef ESP32 +#ifdef ESP_PLATFORM #include "sdkconfig.h" #ifdef CONFIG_VFS_SUPPORT_TERMIOS @@ -65,16 +65,8 @@ #endif // CONFIG_VFS_SUPPORT_TERMIOS #include -#include -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,0,0) -// SemaphoreHandle_t is defined by inclusion of esp_vfs.h so no additional -// includes are necessary. -/// Alias for the internal data type used by ESP-IDF select() calls. -typedef SemaphoreHandle_t * esp_vfs_select_sem_t; -#endif // IDF v3.x - -#endif // ESP32 +#endif // ESP_PLATFORM /// Signal handler that does nothing. @param sig ignored. void empty_signal_handler(int sig); @@ -91,7 +83,7 @@ public: ~OSSelectWakeup() { -#ifdef ESP32 +#ifdef ESP_PLATFORM esp_deallocate_vfs_fd(); #endif } @@ -108,7 +100,7 @@ public: { // Gets the current thread. thread_ = os_thread_self(); -#ifdef ESP32 +#ifdef ESP_PLATFORM esp_allocate_vfs_fd(); #endif #if OPENMRN_FEATURE_DEVICE_SELECT @@ -149,7 +141,7 @@ public: Device::select_wakeup(©); #elif OPENMRN_HAVE_PSELECT pthread_kill(thread_, WAKEUP_SIG); -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) esp_wakeup(); #elif !defined(OPENMRN_FEATURE_SINGLE_THREADED) DIE("need wakeup code"); @@ -179,7 +171,7 @@ public: // TODO: confirm if pthread_kill is ISR safe //#elif OPENMRN_HAVE_PSELECT // pthread_kill(thread_, WAKEUP_SIG); -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) esp_wakeup_from_isr(); #else DIE("need wakeup code"); @@ -206,7 +198,7 @@ public: long long deadline_nsec); private: -#ifdef ESP32 +#ifdef ESP_PLATFORM void esp_allocate_vfs_fd(); void esp_deallocate_vfs_fd(); void esp_wakeup(); @@ -233,12 +225,7 @@ private: /// when waking up early from select(). fd_set exceptFdsOrig_; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,0,0) - /// Semaphore for waking up LwIP select. - void* lwipSem_{nullptr}; -#endif // IDF < v4.0 - -#endif // ESP32 +#endif // ESP_PLATFORM #if OPENMRN_HAVE_PSELECT /** This signal is used for the wakeup kill in a pthreads OS. */ diff --git a/src/os/os.c b/src/os/os.c index d51c51f60..fa67a7e9f 100644 --- a/src/os/os.c +++ b/src/os/os.c @@ -925,7 +925,7 @@ int ignore_fn(void) return 0; } -#if !defined(ARDUINO) && !defined(ESP32) +#if !defined(ARDUINO) && !defined(ESP_PLATFORM) #if !defined (__MINGW32__) int main(int argc, char *argv[]) __attribute__ ((weak)); @@ -984,7 +984,7 @@ int main(int argc, char *argv[]) #endif } -#endif // ESP32 +#endif // ESP_PLATFORM #if defined(ARDUINO) unsigned critical_nesting; diff --git a/src/utils/Atomic.hxx b/src/utils/Atomic.hxx index 05d0f71aa..fe9b5b41d 100644 --- a/src/utils/Atomic.hxx +++ b/src/utils/Atomic.hxx @@ -71,7 +71,7 @@ public: } }; -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) #include "freertos_includes.h" diff --git a/src/utils/HubDevice.hxx b/src/utils/HubDevice.hxx index 345fe6b79..2bd712759 100644 --- a/src/utils/HubDevice.hxx +++ b/src/utils/HubDevice.hxx @@ -48,7 +48,8 @@ template class FdHubWriteFlow; class FdHubPortBase : public FdHubPortInterface, private Atomic { public: -#ifdef ESP32 // TODO: shrink these if possible +#ifdef ESP_PLATFORM + // TODO: shrink these if possible /// How many bytes of stack should we allocate to the write thread's stack. static const int kWriteThreadStackSize = 2048; /// How many bytes of stack should we allocate to the read thread's stack. @@ -58,7 +59,7 @@ public: static const int kWriteThreadStackSize = 1000; /// How many bytes of stack should we allocate to the read thread's stack. static const int kReadThreadStackSize = 1000; -#endif // ESP32 +#endif // ESP_PLATFORM /// Constructor. /// @param fd is the filedes to read/write diff --git a/src/utils/SocketClient.cxx b/src/utils/SocketClient.cxx index cb349346b..706f858cf 100644 --- a/src/utils/SocketClient.cxx +++ b/src/utils/SocketClient.cxx @@ -42,10 +42,11 @@ #include #include -#ifndef ESP32 // these don't exist on the ESP32 with LWiP +#ifndef ESP_PLATFORM +// these don't exist on the ESP32 with LWiP #include #include -#endif // ESP32 +#endif // ESP_PLATFORM #include #include #include @@ -57,9 +58,10 @@ #include "utils/macros.h" #include "utils/logging.h" -#ifdef ESP32 // this is not declared in netdb.h on ESP32 +#ifdef ESP_PLATFORM +// this is not declared in netdb.h on ESP32 const char *gai_strerror (int __ecode); -#endif // ESP32 +#endif // ESP_PLATFORM int ConnectSocket(const char *host, int port) { diff --git a/src/utils/SocketClient.hxx b/src/utils/SocketClient.hxx index f3c7d494a..3b46b5b31 100644 --- a/src/utils/SocketClient.hxx +++ b/src/utils/SocketClient.hxx @@ -37,7 +37,8 @@ #include #include -#ifndef ESP32 // this doesn't exist on the ESP32 with LWiP +#ifndef ESP_PLATFORM +// this doesn't exist on the ESP32 with LWiP #include #endif #include diff --git a/src/utils/constants.cxx b/src/utils/constants.cxx index 1d70bbd49..a6553a47c 100644 --- a/src/utils/constants.cxx +++ b/src/utils/constants.cxx @@ -159,7 +159,7 @@ DEFAULT_CONST(gridconnect_tcp_notsent_lowat_buffer_size, 1); DEFAULT_CONST_FALSE(gridconnect_tcp_use_select); -#ifdef ESP32 +#ifdef ESP_PLATFORM /// Use a stack size of 3kb for SocketListener tasks. DEFAULT_CONST(socket_listener_stack_size, 3072); /// Allow one socket to be pending for accept() in SocketListener. @@ -169,4 +169,4 @@ DEFAULT_CONST(socket_listener_backlog, 1); DEFAULT_CONST(socket_listener_stack_size, 1000); /// Allow up to five sockets to be pending for accept() in SocketListener. DEFAULT_CONST(socket_listener_backlog, 5); -#endif +#endif // ESP_PLATFORM diff --git a/src/utils/logging.cxx b/src/utils/logging.cxx index 9cc01b3eb..ca433da3b 100644 --- a/src/utils/logging.cxx +++ b/src/utils/logging.cxx @@ -35,7 +35,7 @@ #if defined(__linux__) || defined(__MACH__) char logbuffer[4096]; -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) char logbuffer[1024]; #else /// Temporary buffer to sprintf() the log lines into. @@ -56,7 +56,8 @@ void log_output(char* buf, int size) { send_stdio_serial_message(buf); } -#elif defined(__linux__) || defined(__MACH__) || defined(__EMSCRIPTEN__) || defined(ESP32) +#elif defined(__linux__) || defined(__MACH__) || defined(__EMSCRIPTEN__) || \ + defined(ESP_PLATFORM) #include "utils/stdio_logging.h" diff --git a/src/utils/logging.h b/src/utils/logging.h index ddee34ecb..0e9bf62b7 100644 --- a/src/utils/logging.h +++ b/src/utils/logging.h @@ -125,7 +125,7 @@ extern os_mutex_t g_log_mutex; #if defined(__linux__) || defined(__MACH__) extern char logbuffer[4096]; -#elif defined(ESP32) +#elif defined(ESP_PLATFORM) extern char logbuffer[1024]; #else /// Temporary buffer to sprintf() the log lines into. diff --git a/src/utils/macros.h b/src/utils/macros.h index 58a48e01c..f06cbf763 100644 --- a/src/utils/macros.h +++ b/src/utils/macros.h @@ -91,12 +91,6 @@ extern const char* g_death_file; #elif defined(ESP32) -// For the ESP32 family or MCUs use ets_printf() instead of printf() to bypass -// internal locking within the newlib implementation which has been observed -// crashing the MCU at times when printf() is used in conjuction with ISRs on -// the same core. The call to assert() internally uses ets_printf() as well -// prior to "hanging" the MCU core in a busy loop. - #include #include @@ -113,6 +107,11 @@ extern const char* g_death_file; #error Unknown/Unsupported ESP32 variant. #endif // CONFIG_IDF_TARGET_ESP32 +// For the ESP32 we are using ets_printf() instead of printf() to avoid the +// internal locking within the newlib implementation. This locking can cause +// difficult to parse backtraces when an ISR is on the same core as the code +// that crashes due to these two macros. + #define HASSERT(x) do { if (!(x)) { ets_printf("Assertion failed in file " __FILE__ " line %d: assert(%s)\n", __LINE__, #x); g_death_file = __FILE__; g_death_lineno = __LINE__; assert(0); abort();} } while(0) #define DIE(MSG) do { ets_printf("Crashed in file " __FILE__ " line %d: " MSG "\n", __LINE__); assert(0); abort(); } while(0) diff --git a/src/utils/socket_listener.cxx b/src/utils/socket_listener.cxx index 5dfa241f4..98fd5341a 100644 --- a/src/utils/socket_listener.cxx +++ b/src/utils/socket_listener.cxx @@ -46,11 +46,12 @@ #include "utils/logging.h" #include "utils/macros.h" -#ifndef ESP32 // these don't exist on the ESP32 with LWiP +#ifndef ESP_PLATFORM +// these don't exist on the ESP32 with LWiP #include #include #include -#endif // ESP32 +#endif // ESP_PLATFORM #include #include #include diff --git a/src/utils/stdio_logging.h b/src/utils/stdio_logging.h index 22db9acd2..1c29e1759 100644 --- a/src/utils/stdio_logging.h +++ b/src/utils/stdio_logging.h @@ -7,7 +7,7 @@ extern "C" { #endif #if defined(__linux__) || defined(__MACH__) || defined(__EMSCRIPTEN__) || \ - defined(ESP32) + defined(ESP_PLATFORM) #define LOGWEAK __attribute__((weak)) #else #define LOGWEAK