diff --git a/vehicle/OVMS.V3/components/esp32egpio/CMakeLists.txt b/vehicle/OVMS.V3/components/esp32egpio/CMakeLists.txt new file mode 100644 index 000000000..8a0210e8c --- /dev/null +++ b/vehicle/OVMS.V3/components/esp32egpio/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs) +set(include_dirs) + +if (CONFIG_OVMS_COMP_ESP32EGPIO) + list(APPEND srcs "src/esp32egpio.cpp") + list(APPEND include_dirs "src") +endif () + +# requirements can't depend on config +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES "main" + WHOLE_ARCHIVE) diff --git a/vehicle/OVMS.V3/components/esp32egpio/component.mk b/vehicle/OVMS.V3/components/esp32egpio/component.mk new file mode 100644 index 000000000..fe19a3e1a --- /dev/null +++ b/vehicle/OVMS.V3/components/esp32egpio/component.mk @@ -0,0 +1,14 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +ifdef CONFIG_OVMS_COMP_ESP32EGPIO +COMPONENT_SRCDIRS := src +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +endif diff --git a/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.cpp b/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.cpp new file mode 100644 index 000000000..986cdfc8a --- /dev/null +++ b/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.cpp @@ -0,0 +1,619 @@ +/* +; Project: Open Vehicle Monitor System +; Date: November 2025 +; +; Changes: +; 1.0 Initial release +; +; (C) 2025 Carsten Schmiemann +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +; THE SOFTWARE. +*/ + +#include "ovms_log.h" +static const char *TAG = "esp32egpio"; + +#include +#include +#include "esp32egpio.h" +#include "ovms_command.h" +#include "ovms_peripherals.h" +#include "ovms_config.h" +#include "ovms_events.h" +#include "metrics_standard.h" + +// Default monitoring interval in milliseconds +#ifndef CONFIG_OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL +#define CONFIG_OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL 50 +#endif + +/** + * Constructor + * @param name Component name (e.g., "egpio") + * @param gpio_map Array of 10 GPIO pin numbers (-1 = unused port) + */ +esp32egpio::esp32egpio(const char* name, const int8_t* gpio_map) + : pcp(name) + { + // Copy GPIO mapping + memcpy(m_gpio_map, gpio_map, sizeof(m_gpio_map)); + + // Initialize all configured GPIOs to input mode (high impedance) + for (uint8_t port = 0; port < 10; port++) + { + if (IsConfiguredPort(port)) + { + InitializeGPIO(port, GPIO_MODE_INPUT); + ESP_LOGI(TAG, "Port %d mapped to GPIO %d", port, m_gpio_map[port]); + } + } + + m_monitor_task = NULL; + m_monitor_timer = NULL; + m_inputstate = ~0; // all inputs default to high + m_outputstate = ~0; // power up: all ports in input mode (high) + m_monitor_ports = 0; + + // Initialize standard metrics + *StdMetrics.ms_m_egpio_input = m_inputstate; + *StdMetrics.ms_m_egpio_output = m_outputstate; + *StdMetrics.ms_m_egpio_monitor = m_monitor_ports; + } + +esp32egpio::~esp32egpio() + { + if (m_monitor_timer) + delete m_monitor_timer; + if (m_monitor_task) + vTaskDelete(m_monitor_task); + } + +/** + * Helper: Check if port number is valid (0-9) + */ +bool esp32egpio::IsValidPort(uint8_t port) + { + return (port < 10); + } + +/** + * Helper: Check if port is configured (has a valid GPIO mapping) + */ +bool esp32egpio::IsConfiguredPort(uint8_t port) + { + return IsValidPort(port) && (m_gpio_map[port] >= 0); + } + +/** + * Helper: Initialize GPIO pin with specified mode + */ +void esp32egpio::InitializeGPIO(uint8_t port, gpio_mode_t mode) + { + if (!IsConfiguredPort(port)) + return; + + gpio_config_t io_conf = {}; + io_conf.pin_bit_mask = (1ULL << m_gpio_map[port]); + io_conf.mode = mode; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&io_conf); + } + +/** + * Output: set specific port output state + */ +void esp32egpio::Output(uint8_t port, uint8_t level) + { + if (!IsConfiguredPort(port)) + { + ESP_LOGW(TAG, "Output: port %d not configured", port); + return; + } + + // Set GPIO to output mode if not already + InitializeGPIO(port, GPIO_MODE_OUTPUT); + + // Set output level + gpio_set_level((gpio_num_t)m_gpio_map[port], level ? 1 : 0); + + // Update state & framework (emit events on state changes) + if (!m_outputstate[port] && level) + { + m_outputstate[port] = true; + *StdMetrics.ms_m_egpio_output = m_outputstate; + std::string event = "egpio.output."; + event.append(1, '0'+port); + event.append(".high"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + else if (m_outputstate[port] && !level) + { + m_outputstate[port] = false; + *StdMetrics.ms_m_egpio_output = m_outputstate; + std::string event = "egpio.output."; + event.append(1, '0'+port); + event.append(".low"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + } + +std::bitset<10> esp32egpio::OutputState() + { + return m_outputstate; + } + +/** + * Input: read specific port input state + */ +uint8_t esp32egpio::Input(uint8_t port) + { + if (!IsConfiguredPort(port)) + { + ESP_LOGW(TAG, "Input: port %d not configured", port); + return 0; + } + + OvmsMutexLock lock(&m_monitor_mutex); + + // Set GPIO to input mode if not already + InitializeGPIO(port, GPIO_MODE_INPUT); + + // Read GPIO level + uint8_t level = gpio_get_level((gpio_num_t)m_gpio_map[port]); + + // Update state & framework (emit events on state changes) + if (m_inputstate[port] != level) + { + std::bitset<10> pstate = m_inputstate; + pstate[port] = level; + *StdMetrics.ms_m_egpio_input = pstate; + if (!m_inputstate[port] && level) + { + std::string event = "egpio.input."; + event.append(1, '0'+port); + event.append(".high"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + else if (m_inputstate[port] && !level) + { + std::string event = "egpio.input."; + event.append(1, '0'+port); + event.append(".low"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + m_inputstate[port] = level; + } + + return level; + } + +/** + * Inputs: read input states for a set of ports + */ +std::bitset<10> esp32egpio::Inputs(std::bitset<10> ports) + { + std::bitset<10> pstate; + if (ports.none()) + return pstate; + + OvmsMutexLock lock(&m_monitor_mutex); + + // Read all requested ports + for (uint8_t port = 0; port < 10; port++) + { + if (ports[port] && IsConfiguredPort(port)) + { + // Set to input mode if needed + InitializeGPIO(port, GPIO_MODE_INPUT); + // Read level + pstate[port] = gpio_get_level((gpio_num_t)m_gpio_map[port]); + } + } + + // Update state & framework (emit events on state changes) + pstate &= ports; + pstate |= (m_inputstate & ~ports); + for (uint8_t port = 0; port < 10; port++) + { + if (ports[port] && (m_inputstate[port] != pstate[port])) + { + if (!m_inputstate[port] && pstate[port]) + { + std::string event = "egpio.input."; + event.append(1, '0'+port); + event.append(".high"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + else if (m_inputstate[port] && !pstate[port]) + { + std::string event = "egpio.input."; + event.append(1, '0'+port); + event.append(".low"); + ESP_LOGD(TAG, "%s", event.c_str()); + MyEvents.SignalEvent(event, NULL); + } + } + } + m_inputstate = pstate; + *StdMetrics.ms_m_egpio_input = m_inputstate; + + return pstate; + } + +std::bitset<10> esp32egpio::InputState() + { + return m_inputstate; + } + +/** + * MonitorTask: poll input state + */ +static void MonitorTaskEntry(void* me) + { + ((esp32egpio*)me)->MonitorTask(); + } + +void esp32egpio::MonitorTask() + { + while (true) + { + if (m_monitor_semaphore.Take()) + Inputs(m_monitor_ports); + } + } + +/** + * CheckMonitor: activate/deactivate port input monitoring as necessary + */ +bool esp32egpio::CheckMonitor() + { + *StdMetrics.ms_m_egpio_monitor = m_monitor_ports; + if (m_monitor_ports.any()) + { + // activate monitoring: + if (!m_monitor_task) + { + xTaskCreatePinnedToCore(MonitorTaskEntry, "OVMS EGPIOmon", + 4*512, (void*)this, 15, &m_monitor_task, CORE(1)); + if (!m_monitor_task) + { + ESP_LOGE(TAG, "Monitor: unable to create task"); + return false; + } + } + if (!m_monitor_timer) + { + m_monitor_timer = new OvmsInterval("EGPIOmon"); + if (!m_monitor_timer) + { + ESP_LOGE(TAG, "Monitor: unable to create timer"); + return false; + } + } + if (!m_monitor_timer->IsActive()) + { + int interval = MyConfig.GetParamValueInt("egpio", "monitor.interval", + CONFIG_OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL); + if (interval < 10) + interval = 10; + if (!m_monitor_timer->Start(interval, [&]{ m_monitor_semaphore.Give(); })) + { + ESP_LOGE(TAG, "Monitor: unable to start timer"); + return false; + } + } + } + else + { + // deactivate monitoring: + if (m_monitor_timer && !m_monitor_timer->Stop()) + { + ESP_LOGW(TAG, "Monitor: unable to stop timer"); + return false; + } + } + return true; + } + +/** + * Monitor: enable/disable input monitoring for specific ports + */ +bool esp32egpio::Monitor(std::bitset<10> ports, bool enable) + { + OvmsMutexLock lock(&m_monitor_mutex); + if (enable) + m_monitor_ports |= ports; + else + m_monitor_ports &= ~ports; + return CheckMonitor(); + } + +bool esp32egpio::Monitor(uint8_t port, bool enable) + { + OvmsMutexLock lock(&m_monitor_mutex); + m_monitor_ports[port] = enable; + return CheckMonitor(); + } + +std::bitset<10> esp32egpio::MonitorState() + { + OvmsMutexLock lock(&m_monitor_mutex); + return m_monitor_ports; + } + +/** + * GetConfigMonitorPorts: parse configuration for monitored ports + */ +std::bitset<10> esp32egpio::GetConfigMonitorPorts() + { + std::bitset<10> ports; + // Parse configuration: list of port numbers separated by spaces + std::string value = MyConfig.GetParamValue("egpio", "monitor.ports"); + std::istringstream vs(value); + std::string token; + int port; + while (std::getline(vs, token, ' ')) + { + std::istringstream ts(token); + ts >> port; + if (port >= 0 && port <= 9) + ports[port] = 1; + } + return ports; + } + +/** + * AutoInit: initialize from configuration on startup + */ +void esp32egpio::AutoInit() + { + if (MyConfig.GetParamValueBool("auto", "egpio", false)) + { + m_monitor_ports = GetConfigMonitorPorts(); + if (!CheckMonitor()) + ESP_LOGE(TAG, "AutoInit: monitoring could not be started"); + } + } + + +// =================================================================== +// COMMAND INTERFACE (compatible with max7317 commands) +// =================================================================== + +static void esp32egpio_output(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + if (!MyPeripherals || !MyPeripherals->m_esp32egpio) + { + writer->puts("Error: EGPIO component not available"); + return; + } + + for (int i=0; i 9) + { + writer->printf("Error: port %d out of range (0-9)\n", port); + continue; + } + MyPeripherals->m_esp32egpio->Output((uint8_t)port, (uint8_t)level); + } +#endif + } + +static void esp32egpio_pulse(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + if (!MyPeripherals || !MyPeripherals->m_esp32egpio) + { + writer->puts("Error: EGPIO component not available"); + return; + } + + int port = atoi(argv[0]); + int level = atoi(argv[1]); + int duration = atoi(argv[2]); + + if (port < 0 || port > 9) + { + writer->printf("Error: port %d out of range (0-9)\n", port); + return; + } + + MyPeripherals->m_esp32egpio->Output((uint8_t)port, (uint8_t)level); + vTaskDelay(pdMS_TO_TICKS(duration)); + MyPeripherals->m_esp32egpio->Output((uint8_t)port, level ? 0 : 1); +#endif + } + +static void esp32egpio_input(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + if (!MyPeripherals || !MyPeripherals->m_esp32egpio) + { + writer->puts("Error: EGPIO component not available"); + return; + } + + for (int i=0; i 9) + { + writer->printf("Error: port %d out of range (0-9)\n", port); + continue; + } + int level = MyPeripherals->m_esp32egpio->Input((uint8_t)port); + writer->printf("Port %d: %d\n", port, level); + } +#endif + } + +static void esp32egpio_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + if (!MyPeripherals || !MyPeripherals->m_esp32egpio) + { + writer->puts("Error: EGPIO component not available"); + return; + } + + std::bitset<10> output = MyPeripherals->m_esp32egpio->OutputState(); + std::bitset<10> input = MyPeripherals->m_esp32egpio->InputState(); + std::bitset<10> monitor = MyPeripherals->m_esp32egpio->MonitorState(); + + writer->puts("Port Output Input Monitor"); + for (int i=0; i<10; i++) + { + writer->printf(" %d %d %d %d\n", + i, (int)output.test(i), (int)input.test(i), (int)monitor.test(i)); + } +#endif + } + +static void esp32egpio_monitor(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) + { +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + if (!MyPeripherals || !MyPeripherals->m_esp32egpio) + { + writer->puts("Error: EGPIO component not available"); + return; + } + + std::string cmdname = cmd->GetName(); + + if (cmdname == "status") + { + std::bitset<10> ports = MyPeripherals->m_esp32egpio->MonitorState(); + if (ports.none()) + { + writer->puts("Status: input monitoring is disabled."); + } + else if (ports.all()) + { + writer->puts("Status: input monitoring enabled for all ports."); + } + else + { + writer->printf("Status: input monitoring enabled for ports:"); + for (int i=0; i<10; i++) + { + if (ports[i]) + writer->printf(" %d", i); + } + writer->puts(""); + } + } + else if (cmdname == "on") + { + std::bitset<10> ports; + if (argc == 0) + { + ports = MyPeripherals->m_esp32egpio->GetConfigMonitorPorts(); + } + else + { + for (int i=0; i= 0 && port <= 9) + ports[port] = 1; + } + } + if (!MyPeripherals->m_esp32egpio->Monitor(ports, true)) + writer->puts("Error: could not enable monitoring"); + else + writer->puts("Monitoring enabled"); + } + else if (cmdname == "off") + { + std::bitset<10> ports; + if (argc == 0) + { + ports = ~0; // all ports + } + else + { + for (int i=0; i= 0 && port <= 9) + ports[port] = 1; + } + } + if (!MyPeripherals->m_esp32egpio->Monitor(ports, false)) + writer->puts("Error: could not disable monitoring"); + else + writer->puts("Monitoring disabled"); + } +#endif + } + +// =================================================================== +// INITIALIZATION CLASS +// =================================================================== + +class Esp32EgpioInit + { + public: Esp32EgpioInit(); + } MyEsp32EgpioInit __attribute__ ((init_priority (4200))); + +Esp32EgpioInit::Esp32EgpioInit() + { + ESP_LOGI(TAG, "Initialising ESP32 EGPIO (4200)"); + + MyConfig.RegisterParam("egpio", "EGPIO configuration", true, true); + + OvmsCommand* cmd_egpio = MyCommandApp.RegisterCommand( + "egpio", "EGPIO framework", esp32egpio_status, "", 0, 0, false); + cmd_egpio->RegisterCommand( + "output", "Set EGPIO output level(s)", esp32egpio_output, + " [ ...]", 2, 20); + cmd_egpio->RegisterCommand( + "pulse", "Pulse EGPIO output level(s)", esp32egpio_pulse, + " ", 3, 3); + cmd_egpio->RegisterCommand( + "input", "Get EGPIO input level(s)", esp32egpio_input, + " [ ...]", 1, 10); + cmd_egpio->RegisterCommand( + "status", "Show EGPIO status", esp32egpio_status); + + OvmsCommand* cmd_mon = cmd_egpio->RegisterCommand( + "monitor", "EGPIO input monitoring"); + cmd_mon->RegisterCommand( + "status", "Show input monitoring status", esp32egpio_monitor); + cmd_mon->RegisterCommand( + "on", "Enable input monitoring", esp32egpio_monitor, + "[ ...]\n" + "No ports = enable as configured in egpio/monitor.ports\n" + "Note: ports specified will be switched to input mode", 0, 10); + cmd_mon->RegisterCommand( + "off", "Disable input monitoring", esp32egpio_monitor, + "[ ...]\n" + "No ports = disable all", 0, 10); + } diff --git a/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.h b/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.h new file mode 100644 index 000000000..7be99248c --- /dev/null +++ b/vehicle/OVMS.V3/components/esp32egpio/src/esp32egpio.h @@ -0,0 +1,101 @@ +/* +; Project: Open Vehicle Monitor System +; Date: November 2025 +; +; Changes: +; 1.0 Initial release +; +; (C) 2025 Carsten Schmiemann +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to deal +; in the Software without restriction, including without limitation the rights +; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +; copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +; THE SOFTWARE. +*/ + +#ifndef __ESP32EGPIO_H__ +#define __ESP32EGPIO_H__ + +#include +#include "pcp.h" +#include +#include "ovms_timer.h" +#include "ovms_semaphore.h" +#include "ovms_mutex.h" +#include "driver/gpio.h" + +/** + * @brief ESP32 EGPIO - Direct GPIO control emulating MAX7317 interface + * + * This class provides the same interface as max7317 but uses direct ESP32 GPIO + * pins instead of an external SPI I/O expander. This is particularly useful for + * boards like Lilygo T-Call that don't have a MAX7317 chip. + * + * Features: + * - 10 configurable GPIO ports (0-9) mapped to ESP32 GPIO pins + * - Output control with state tracking + * - Input reading with optional monitoring/polling + * - Event generation on input state changes + * - Metrics integration (compatible with standard EGPIO metrics) + * - Command interface (works with standard 'egpio' commands) + */ +class esp32egpio : public pcp, public InternalRamAllocated + { + public: + esp32egpio(const char* name, const int8_t* gpio_map); + ~esp32egpio(); + + public: + // Main API - compatible with max7317 + void Output(uint8_t port, uint8_t level); + std::bitset<10> OutputState(); + uint8_t Input(uint8_t port); + std::bitset<10> Inputs(std::bitset<10> ports); + std::bitset<10> InputState(); + bool Monitor(std::bitset<10> ports, bool enable); + bool Monitor(uint8_t port, bool enable); + std::bitset<10> MonitorState(); + + public: + // Configuration and initialization + std::bitset<10> GetConfigMonitorPorts(); + void AutoInit(); + bool CheckMonitor(); + void MonitorTask(); + + protected: + // GPIO pin mapping: index = EGPIO port number, value = GPIO pin number (-1 = unused) + int8_t m_gpio_map[10]; + + // State tracking (same as max7317) + std::bitset<10> m_outputstate; + std::bitset<10> m_inputstate; + std::bitset<10> m_monitor_ports; + + // Monitoring infrastructure (same as max7317) + OvmsInterval* m_monitor_timer; + OvmsSemaphore m_monitor_semaphore; + OvmsMutex m_monitor_mutex; + TaskHandle_t m_monitor_task; + + protected: + // Internal helpers + bool IsValidPort(uint8_t port); + bool IsConfiguredPort(uint8_t port); + void InitializeGPIO(uint8_t port, gpio_mode_t mode); + }; + +#endif // __ESP32EGPIO_H__ diff --git a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10.h b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10.h index ca3782d1d..04e47ad06 100644 --- a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10.h +++ b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10.h @@ -35,3 +35,15 @@ #define MODEM_EGPIO_DTR MODEM_GPIO_DTR #define VSPI_PIN_MCP2515_2_INT -1 // dummy definition to avoid compiler error #define VSPI_PIN_MCP2515_2_CS -1 + +// ESP32 EGPIO pin mapping (index = EGPIO port number, value = GPIO pin number) +#define ESP32EGPIO_PIN_0 -1 // unused +#define ESP32EGPIO_PIN_1 -1 // unused +#define ESP32EGPIO_PIN_2 -1 // unused +#define ESP32EGPIO_PIN_3 -1 // unused +#define ESP32EGPIO_PIN_4 -1 // unused +#define ESP32EGPIO_PIN_5 -1 // unused +#define ESP32EGPIO_PIN_6 -1 // unused +#define ESP32EGPIO_PIN_7 -1 // unused +#define ESP32EGPIO_PIN_8 -1 // unused +#define ESP32EGPIO_PIN_9 -1 // unused \ No newline at end of file diff --git a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10_a.h b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10_a.h index 37fb48978..72c6762fe 100644 --- a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10_a.h +++ b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v10_a.h @@ -34,3 +34,15 @@ #define MODEM_EGPIO_DTR MODEM_GPIO_DTR #define VSPI_PIN_MCP2515_2_INT -1 // dummy definition to avoid compiler error #define VSPI_PIN_MCP2515_2_CS -1 + +// ESP32 EGPIO pin mapping (index = EGPIO port number, value = GPIO pin number) +#define ESP32EGPIO_PIN_0 -1 // unused +#define ESP32EGPIO_PIN_1 -1 // unused +#define ESP32EGPIO_PIN_2 -1 // unused +#define ESP32EGPIO_PIN_3 -1 // unused +#define ESP32EGPIO_PIN_4 -1 // unused +#define ESP32EGPIO_PIN_5 -1 // unused +#define ESP32EGPIO_PIN_6 -1 // unused +#define ESP32EGPIO_PIN_7 -1 // unused +#define ESP32EGPIO_PIN_8 -1 // unused +#define ESP32EGPIO_PIN_9 -1 // unused \ No newline at end of file diff --git a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v11.h b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v11.h index 15facba56..7c6670099 100644 --- a/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v11.h +++ b/vehicle/OVMS.V3/components/gpio_maps/lilygo_tc_v11.h @@ -35,3 +35,15 @@ #define MODEM_EGPIO_DTR MODEM_GPIO_DTR #define VSPI_PIN_MCP2515_2_INT -1 // dummy definition to avoid compiler error #define VSPI_PIN_MCP2515_2_CS -1 + +// ESP32 EGPIO pin mapping (index = EGPIO port number, value = GPIO pin number) +#define ESP32EGPIO_PIN_0 -1 // unused +#define ESP32EGPIO_PIN_1 -1 // unused +#define ESP32EGPIO_PIN_2 -1 // unused +#define ESP32EGPIO_PIN_3 -1 // unused +#define ESP32EGPIO_PIN_4 -1 // unused +#define ESP32EGPIO_PIN_5 -1 // unused +#define ESP32EGPIO_PIN_6 -1 // unused +#define ESP32EGPIO_PIN_7 -1 // unused +#define ESP32EGPIO_PIN_8 -1 // unused +#define ESP32EGPIO_PIN_9 -1 // unused \ No newline at end of file diff --git a/vehicle/OVMS.V3/main/Kconfig b/vehicle/OVMS.V3/main/Kconfig index 0ff3b4b65..9f98a3bee 100644 --- a/vehicle/OVMS.V3/main/Kconfig +++ b/vehicle/OVMS.V3/main/Kconfig @@ -727,6 +727,23 @@ config OVMS_COMP_MAX7317_MONITOR_INTERVAL If enabled, the monitoring polls the ports with this interval by default. The interval can be configured via param egpio[monitor.interval]. +config OVMS_COMP_ESP32EGPIO + bool "Include support for ESP32 native GPIO (esp32egpio)" + default y + depends on OVMS + help + Enable to include support for ESP32 native GPIO pins as extended GPIO. + +config OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL + int "ESP32 EGPIO port monitor default polling interval [ms]" + range 10 10000 + default 50 + depends on OVMS_COMP_ESP32EGPIO + help + The ESP32 EGPIO has no IRQ so needs to be polled to monitor port changes. + If enabled, the monitoring polls the ports with this interval by default. + The interval can be configured via param egpio[monitor.interval]. + config OVMS_COMP_ESP32CAN bool "Include support for ESP32 on-board CAN controller (can1)" default y diff --git a/vehicle/OVMS.V3/main/metrics_standard.cpp b/vehicle/OVMS.V3/main/metrics_standard.cpp index 3433e8925..280029cd0 100644 --- a/vehicle/OVMS.V3/main/metrics_standard.cpp +++ b/vehicle/OVMS.V3/main/metrics_standard.cpp @@ -60,11 +60,11 @@ MetricsStandard::MetricsStandard() ms_m_net_mdm_model = new OvmsMetricString(MS_N_MDM_MODEL, SM_STALE_MAX); ms_m_net_mdm_mode = new OvmsMetricString(MS_N_MDM_MODE, SM_STALE_MAX); -#ifdef CONFIG_OVMS_COMP_MAX7317 +#if defined(CONFIG_OVMS_COMP_MAX7317) || defined(CONFIG_OVMS_COMP_ESP32EGPIO) ms_m_egpio_input = new OvmsMetricBitset<10,0>(MS_M_EGPIO_INPUT, SM_STALE_MAX); ms_m_egpio_output = new OvmsMetricBitset<10,0>(MS_M_EGPIO_OUTPUT, SM_STALE_MAX); ms_m_egpio_monitor = new OvmsMetricBitset<10,0>(MS_M_EGPIO_MONITOR, SM_STALE_MAX); -#endif //CONFIG_OVMS_COMP_MAX7317 +#endif //CONFIG_OVMS_COMP_MAX7317 || CONFIG_OVMS_COMP_ESP32EGPIO ms_m_obd2ecu_on = new OvmsMetricBool(MS_M_OBD2ECU_ON, SM_STALE_MID); ms_s_v2_connected = new OvmsMetricBool(MS_S_V2_CONNECTED); diff --git a/vehicle/OVMS.V3/main/metrics_standard.h b/vehicle/OVMS.V3/main/metrics_standard.h index fd3f3191e..49e145de8 100644 --- a/vehicle/OVMS.V3/main/metrics_standard.h +++ b/vehicle/OVMS.V3/main/metrics_standard.h @@ -63,11 +63,11 @@ #define MS_N_WIFI_NETWORK "m.net.wifi.network" #define MS_N_WIFI_SQ "m.net.wifi.sq" -#ifdef CONFIG_OVMS_COMP_MAX7317 +#if defined(CONFIG_OVMS_COMP_MAX7317) || defined(CONFIG_OVMS_COMP_ESP32EGPIO) #define MS_M_EGPIO_INPUT "m.egpio.input" #define MS_M_EGPIO_MONITOR "m.egpio.monitor" #define MS_M_EGPIO_OUTPUT "m.egpio.output" -#endif //CONFIG_OVMS_COMP_MAX7317 +#endif //CONFIG_OVMS_COMP_MAX7317 || CONFIG_OVMS_COMP_ESP32EGPIO #define MS_M_OBD2ECU_ON "m.obdc2ecu.on" #define MS_S_V2_CONNECTED "s.v2.connected" @@ -307,11 +307,11 @@ class MetricsStandard OvmsMetricBool* ms_m_net_ip; // True = device has ip available OvmsMetricBool* ms_m_net_good_sq; // True = sq is above the configured threshold for sq usability -#ifdef CONFIG_OVMS_COMP_MAX7317 - OvmsMetricBitset<10,0>* ms_m_egpio_input; // EGPIO (MAX7317) input port state (ports 0…9) - OvmsMetricBitset<10,0>* ms_m_egpio_output; // EGPIO (MAX7317) output port state - OvmsMetricBitset<10,0>* ms_m_egpio_monitor; // EGPIO (MAX7317) input monitoring state -#endif //CONFIG_OVMS_COMP_MAX7317 +#if defined(CONFIG_OVMS_COMP_MAX7317) || defined(CONFIG_OVMS_COMP_ESP32EGPIO) + OvmsMetricBitset<10,0>* ms_m_egpio_input; // EGPIO (MAX7317/ESP32EGPIO) input port state (ports 0…9) + OvmsMetricBitset<10,0>* ms_m_egpio_output; // EGPIO (MAX7317/ESP32EGPIO) output port state + OvmsMetricBitset<10,0>* ms_m_egpio_monitor; // EGPIO (MAX7317/ESP32EGPIO) input monitoring state +#endif //CONFIG_OVMS_COMP_MAX7317 || CONFIG_OVMS_COMP_ESP32EGPIO OvmsMetricBool* ms_m_obd2ecu_on; // OBD2ECU process is on. OvmsMetricBool* ms_s_v2_connected; // True = V2 server connected [1] diff --git a/vehicle/OVMS.V3/main/ovms_peripherals.cpp b/vehicle/OVMS.V3/main/ovms_peripherals.cpp index 1428b3fb9..b6d391735 100644 --- a/vehicle/OVMS.V3/main/ovms_peripherals.cpp +++ b/vehicle/OVMS.V3/main/ovms_peripherals.cpp @@ -150,6 +150,17 @@ Peripherals::Peripherals() m_max7317 = new max7317("egpio", m_spibus, VSPI_HOST, 10000000, VSPI_PIN_MAX7317_CS); #endif // #ifdef CONFIG_OVMS_COMP_MAX7317 +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + static const int8_t esp32egpio_pins[10] = { + ESP32EGPIO_PIN_0, ESP32EGPIO_PIN_1, ESP32EGPIO_PIN_2, + ESP32EGPIO_PIN_3, ESP32EGPIO_PIN_4, ESP32EGPIO_PIN_5, + ESP32EGPIO_PIN_6, ESP32EGPIO_PIN_7, ESP32EGPIO_PIN_8, + ESP32EGPIO_PIN_9 + }; + ESP_LOGI(TAG, " ESP32 EGPIO"); + m_esp32egpio = new esp32egpio("esp32egpio", esp32egpio_pins); +#endif // #ifdef CONFIG_OVMS_COMP_ESP32EGPIO + #ifdef CONFIG_OVMS_COMP_ESP32CAN ESP_LOGI(TAG, " ESP32 CAN"); m_esp32can = new esp32can("can1", ESP32CAN_PIN_TX, ESP32CAN_PIN_RX); diff --git a/vehicle/OVMS.V3/main/ovms_peripherals.h b/vehicle/OVMS.V3/main/ovms_peripherals.h index e2ecac09f..bf8fc4dee 100644 --- a/vehicle/OVMS.V3/main/ovms_peripherals.h +++ b/vehicle/OVMS.V3/main/ovms_peripherals.h @@ -55,6 +55,10 @@ #include "max7317.h" #endif // #ifdef CONFIG_OVMS_COMP_MAX7317 +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO +#include "esp32egpio.h" +#endif // #ifdef CONFIG_OVMS_COMP_ESP32EGPIO + #ifdef CONFIG_OVMS_COMP_BLUETOOTH #include "esp32bluetooth.h" #endif // #ifdef CONFIG_OVMS_COMP_BLUETOOTH @@ -161,6 +165,10 @@ class Peripherals : public InternalRamAllocated max7317* m_max7317; #endif // #ifdef CONFIG_OVMS_COMP_MAX7317 +#ifdef CONFIG_OVMS_COMP_ESP32EGPIO + esp32egpio* m_esp32egpio; +#endif // #ifdef CONFIG_OVMS_COMP_ESP32EGPIO + #ifdef CONFIG_OVMS_COMP_ESP32CAN esp32can* m_esp32can; #endif // #ifdef CONFIG_OVMS_COMP_ESP32CAN diff --git a/vehicle/OVMS.V3/support/sdkconfig.default.hw31 b/vehicle/OVMS.V3/support/sdkconfig.default.hw31 index a60e22601..cac8a6f7a 100644 --- a/vehicle/OVMS.V3/support/sdkconfig.default.hw31 +++ b/vehicle/OVMS.V3/support/sdkconfig.default.hw31 @@ -706,6 +706,8 @@ CONFIG_OVMS_VEHICLE_CAN_RX_QUEUE_SIZE=60 CONFIG_OVMS_COMP_WIFI=y CONFIG_OVMS_COMP_MAX7317=y CONFIG_OVMS_COMP_MAX7317_MONITOR_INTERVAL=50 +CONFIG_OVMS_COMP_ESP32EGPIO=n +CONFIG_OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL=50 CONFIG_OVMS_COMP_ESP32CAN=y CONFIG_OVMS_COMP_MCP2515=y CONFIG_OVMS_COMP_EXTERNAL_SWCAN= diff --git a/vehicle/OVMS.V3/support/sdkconfig.lilygo_tc b/vehicle/OVMS.V3/support/sdkconfig.lilygo_tc index b2135c740..cb37e84de 100644 --- a/vehicle/OVMS.V3/support/sdkconfig.lilygo_tc +++ b/vehicle/OVMS.V3/support/sdkconfig.lilygo_tc @@ -713,6 +713,8 @@ CONFIG_OVMS_VEHICLE_CAN_RX_QUEUE_SIZE=60 # CONFIG_OVMS_COMP_WIFI=y CONFIG_OVMS_COMP_MAX7317= +CONFIG_OVMS_COMP_ESP32EGPIO=y +CONFIG_OVMS_COMP_ESP32EGPIO_MONITOR_INTERVAL=50 CONFIG_OVMS_COMP_ESP32CAN=y CONFIG_OVMS_COMP_MCP2515=y CONFIG_OVMS_COMP_EXTERNAL_SWCAN=