From 1be9a2b3f8b980eb49e312eb1ff43678c638dc4d Mon Sep 17 00:00:00 2001 From: VietDzung Date: Wed, 2 Jul 2025 11:46:03 +0700 Subject: [PATCH] Migrate to ESP IDF but not test --- src/esp32_idf_platform.cpp | 436 +++++++++++++++++++++++++++++-------- src/esp32_idf_platform.h | 118 ++++++---- 2 files changed, 421 insertions(+), 133 deletions(-) diff --git a/src/esp32_idf_platform.cpp b/src/esp32_idf_platform.cpp index 4800e43..c5fa398 100644 --- a/src/esp32_idf_platform.cpp +++ b/src/esp32_idf_platform.cpp @@ -1,135 +1,397 @@ #ifdef ESP_PLATFORM +// esp_idf_platform.cpp +#include "esp_efuse.h" #include "esp32_idf_platform.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "esp_log.h" +#include "knx/bits.h" +#include "nvs.h" -#define KNX_IDF_UART_NUM UART_NUM_1 -#define KNX_IDF_UART_BAUD 19200 -#define KNX_IDF_UART_TX_BUF_SIZE 256 -#define KNX_IDF_UART_RX_BUF_SIZE 256 +// Define a logging tag for this file +static const char* KTAG = "Esp32IdfPlatform"; -Esp32IdfPlatform::Esp32IdfPlatform() - : ArduinoPlatform(nullptr) // No HardwareSerial in IDF +Esp32IdfPlatform::Esp32IdfPlatform(uart_port_t uart_num) + : _uart_num(uart_num) { - // Optionally initialize NVS, WiFi, etc. here + // Set the memory type to use our NVS-based EEPROM emulation + _memoryType = Eeprom; } -void Esp32IdfPlatform::knxUartPins(int8_t rxPin, int8_t txPin) { +Esp32IdfPlatform::~Esp32IdfPlatform() +{ + if (_sock != -1) + { + closeMultiCast(); + } + if (_uart_installed) + { + closeUart(); + } + if (_eeprom_buffer) + { + free(_eeprom_buffer); + } + if (_nvs_handle) + { + nvs_close(_nvs_handle); + } +} + +void Esp32IdfPlatform::knxUartPins(int8_t rxPin, int8_t txPin) +{ _rxPin = rxPin; _txPin = txPin; } -void Esp32IdfPlatform::setupUart() { +void Esp32IdfPlatform::setNetif(esp_netif_t* netif) +{ + _netif = netif; +} + +void Esp32IdfPlatform::fatalError() +{ + ESP_LOGE(KTAG, "FATAL ERROR. System halted."); + // Loop forever to halt the system + while (1) + { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +// ESP specific uart handling with pins +void Esp32IdfPlatform::setupUart() +{ + if (_uart_installed) + return; uart_config_t uart_config = { - .baud_rate = KNX_IDF_UART_BAUD, + .baud_rate = 19200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_EVEN, .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, }; - uart_param_config(KNX_IDF_UART_NUM, &uart_config); - uart_set_pin(KNX_IDF_UART_NUM, _txPin, _rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); - uart_driver_install(KNX_IDF_UART_NUM, KNX_IDF_UART_RX_BUF_SIZE, KNX_IDF_UART_TX_BUF_SIZE, 0, NULL, 0); + ESP_ERROR_CHECK(uart_driver_install(_uart_num, 256 * 2, 0, 0, NULL, 0)); + ESP_ERROR_CHECK(uart_param_config(_uart_num, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(_uart_num, _txPin, _rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + _uart_installed = true; } -uint32_t Esp32IdfPlatform::currentIpAddress() { - esp_netif_ip_info_t ip_info; - esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { - return ip_info.ip.addr; +void Esp32IdfPlatform::closeUart() +{ + if (!_uart_installed) + return; + uart_driver_delete(_uart_num); + _uart_installed = false; +} + +int Esp32IdfPlatform::uartAvailable() +{ + if (!_uart_installed) + return 0; + size_t length = 0; + ESP_ERROR_CHECK(uart_get_buffered_data_len(_uart_num, &length)); + return length; +} + +size_t Esp32IdfPlatform::writeUart(const uint8_t data) +{ + if (!_uart_installed) + return 0; + return uart_write_bytes(_uart_num, &data, 1); +} + +size_t Esp32IdfPlatform::writeUart(const uint8_t* buffer, size_t size) +{ + if (!_uart_installed) + return 0; + return uart_write_bytes(_uart_num, buffer, size); +} + +int Esp32IdfPlatform::readUart() +{ + if (!_uart_installed) + return -1; + uint8_t data; + if (uart_read_bytes(_uart_num, &data, 1, pdMS_TO_TICKS(20)) > 0) + { + return data; } - return 0; + return -1; } -uint32_t Esp32IdfPlatform::currentSubnetMask() { +size_t Esp32IdfPlatform::readBytesUart(uint8_t* buffer, size_t length) +{ + if (!_uart_installed) + return 0; + return uart_read_bytes(_uart_num, buffer, length, pdMS_TO_TICKS(100)); +} + +void Esp32IdfPlatform::flushUart() +{ + if (!_uart_installed) + return; + ESP_ERROR_CHECK(uart_flush(_uart_num)); +} + +uint32_t Esp32IdfPlatform::currentIpAddress() +{ + if (!_netif) + return 0; esp_netif_ip_info_t ip_info; - esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { - return ip_info.netmask.addr; - } - return 0; + esp_netif_get_ip_info(_netif, &ip_info); + return ip_info.ip.addr; } -uint32_t Esp32IdfPlatform::currentDefaultGateway() { +uint32_t Esp32IdfPlatform::currentSubnetMask() +{ + if (!_netif) + return 0; esp_netif_ip_info_t ip_info; - esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { - return ip_info.gw.addr; - } - return 0; + esp_netif_get_ip_info(_netif, &ip_info); + return ip_info.netmask.addr; } -void Esp32IdfPlatform::macAddress(uint8_t* addr) { - esp_read_mac(addr, ESP_MAC_WIFI_STA); +uint32_t Esp32IdfPlatform::currentDefaultGateway() +{ + if (!_netif) + return 0; + esp_netif_ip_info_t ip_info; + esp_netif_get_ip_info(_netif, &ip_info); + return ip_info.gw.addr; } -uint32_t Esp32IdfPlatform::uniqueSerialNumber() { +void Esp32IdfPlatform::macAddress(uint8_t* addr) +{ + if (!_netif) + return; + esp_netif_get_mac(_netif, addr); +} + +uint32_t Esp32IdfPlatform::uniqueSerialNumber() +{ uint8_t mac[6]; - esp_read_mac(mac, ESP_MAC_WIFI_STA); - uint32_t upper = (mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]; - uint32_t lower = (mac[4] << 8) | mac[5]; - return upper ^ lower; + esp_efuse_mac_get_default(mac); + uint64_t chipid = 0; + for (int i = 0; i < 6; i++) + { + chipid |= ((uint64_t)mac[i] << (i * 8)); + } + uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF; + uint32_t lowerId = (chipid & 0xFFFFFFFF); + return (upperId ^ lowerId); } -void Esp32IdfPlatform::restart() { +void Esp32IdfPlatform::restart() +{ + ESP_LOGI(KTAG, "Restarting system..."); esp_restart(); } -void Esp32IdfPlatform::setupMultiCast(uint32_t addr, uint16_t port) { - if (_udpSock >= 0) close(_udpSock); - _udpSock = socket(AF_INET, SOCK_DGRAM, 0); - struct sockaddr_in mcast_addr = {}; - mcast_addr.sin_family = AF_INET; - mcast_addr.sin_addr.s_addr = htonl(addr); - mcast_addr.sin_port = htons(port); - // Set socket options for multicast as needed - // ... +void Esp32IdfPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + _multicast_addr = addr; + _multicast_port = port; + + _sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (_sock < 0) + { + ESP_LOGE(KTAG, "Failed to create socket. Errno: %d", errno); + return; + } + + struct sockaddr_in saddr = {0}; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(_sock, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in)) < 0) + { + ESP_LOGE(KTAG, "Failed to bind socket. Errno: %d", errno); + close(_sock); + _sock = -1; + return; + } + + struct ip_mreq imreq = {0}; + imreq.imr_interface.s_addr = IPADDR_ANY; + imreq.imr_multiaddr.s_addr = addr; + if (setsockopt(_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq)) < 0) + { + ESP_LOGE(KTAG, "Failed to join multicast group. Errno: %d", errno); + close(_sock); + _sock = -1; + return; + } + + ESP_LOGI(KTAG, "Successfully joined multicast group on port %d", port); } -void Esp32IdfPlatform::closeMultiCast() { - if (_udpSock >= 0) { - close(_udpSock); - _udpSock = -1; +void Esp32IdfPlatform::closeMultiCast() +{ + if (_sock != -1) + { + close(_sock); + _sock = -1; } } -bool Esp32IdfPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len) { - // Implement sending to multicast group - // ... +bool Esp32IdfPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len) +{ + if (_sock < 0) + return false; + + struct sockaddr_in dest_addr = {}; + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(_multicast_port); + dest_addr.sin_addr.s_addr = _multicast_addr; + + int sent_len = sendto(_sock, buffer, len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)); + if (sent_len < 0) + { + ESP_LOGE(KTAG, "sendBytesMultiCast failed. Errno: %d", errno); + return false; + } + return sent_len == len; +} + +int Esp32IdfPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) +{ + if (_sock < 0) + return 0; + + socklen_t socklen = sizeof(_remote_addr); + int len = recvfrom(_sock, buffer, maxLen, 0, (struct sockaddr*)&_remote_addr, &socklen); + + if (len <= 0) + { + return 0; // No data or error + } + + src_addr = _remote_addr.sin_addr.s_addr; + src_port = ntohs(_remote_addr.sin_port); + + return len; +} + +bool Esp32IdfPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) +{ + if (_sock < 0) + return false; + + struct sockaddr_in dest_addr; + dest_addr.sin_family = AF_INET; + + if (addr == 0) + { // If address is 0, use the address from the last received packet + dest_addr.sin_addr.s_addr = _remote_addr.sin_addr.s_addr; + } + else + { + dest_addr.sin_addr.s_addr = addr; + } + + if (port == 0) + { // If port is 0, use the port from the last received packet + dest_addr.sin_port = _remote_addr.sin_port; + } + else + { + dest_addr.sin_port = htons(port); + } + + if (sendto(_sock, buffer, len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) + { + ESP_LOGE(KTAG, "sendBytesUniCast failed. Errno: %d", errno); + return false; + } + return true; } -int Esp32IdfPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) { - // Implement reading from multicast socket - // ... - return 0; +uint8_t* Esp32IdfPlatform::getEepromBuffer(uint32_t size) +{ + if (_eeprom_buffer && _eeprom_size == size) + { + return _eeprom_buffer; + } + + if (_eeprom_buffer) + { + free(_eeprom_buffer); + _eeprom_buffer = nullptr; + } + + _eeprom_size = size; + _eeprom_buffer = (uint8_t*)malloc(size); + if (!_eeprom_buffer) + { + ESP_LOGE(KTAG, "Failed to allocate EEPROM buffer"); + fatalError(); + return nullptr; + } + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + + err = nvs_open(_nvs_namespace, NVS_READWRITE, &_nvs_handle); + if (err != ESP_OK) + { + ESP_LOGE(KTAG, "Error opening NVS handle: %s", esp_err_to_name(err)); + free(_eeprom_buffer); + _eeprom_buffer = nullptr; + fatalError(); + return nullptr; + } + + size_t required_size = size; + err = nvs_get_blob(_nvs_handle, _nvs_key, _eeprom_buffer, &required_size); + if (err != ESP_OK || required_size != size) + { + if (err == ESP_ERR_NVS_NOT_FOUND) + { + ESP_LOGI(KTAG, "No previous EEPROM data found in NVS. Initializing fresh buffer."); + } + else + { + ESP_LOGW(KTAG, "NVS get blob failed (%s) or size mismatch. Initializing fresh buffer.", esp_err_to_name(err)); + } + memset(_eeprom_buffer, 0xFF, size); + } + else + { + ESP_LOGI(KTAG, "Successfully loaded %d bytes from NVS into EEPROM buffer.", required_size); + } + + return _eeprom_buffer; } -bool Esp32IdfPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) { - // Implement sending to unicast address - // ... - return true; -} +void Esp32IdfPlatform::commitToEeprom() +{ + if (!_eeprom_buffer || !_nvs_handle) + { + ESP_LOGE(KTAG, "EEPROM not initialized, cannot commit."); + return; + } -uint8_t* Esp32IdfPlatform::getEepromBuffer(uint32_t size) { - // Use NVS or other ESP-IDF storage - // ... - return nullptr; -} + esp_err_t err = nvs_set_blob(_nvs_handle, _nvs_key, _eeprom_buffer, _eeprom_size); + if (err != ESP_OK) + { + ESP_LOGE(KTAG, "Failed to set NVS blob: %s", esp_err_to_name(err)); + return; + } -void Esp32IdfPlatform::commitToEeprom() { - // Commit NVS or other storage - // ... + err = nvs_commit(_nvs_handle); + if (err != ESP_OK) + { + ESP_LOGE(KTAG, "Failed to commit NVS: %s", esp_err_to_name(err)); + } + else + { + ESP_LOGI(KTAG, "Committed %d bytes to NVS.", _eeprom_size); + } } - -#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/esp32_idf_platform.h b/src/esp32_idf_platform.h index cbce5e5..dfb01b0 100644 --- a/src/esp32_idf_platform.h +++ b/src/esp32_idf_platform.h @@ -1,59 +1,85 @@ #ifdef ESP_PLATFORM -#include "arduino_platform.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +// esp_idf_platform.h +#pragma once -class Esp32IdfPlatform : public ArduinoPlatform +#include "driver/uart.h" +#include "esp_netif.h" +#include "esp_system.h" +#include "lwip/sockets.h" +#include "nvs_flash.h" +#include "knx/platform.h"// Include the provided base class + +class Esp32IdfPlatform : public Platform { - public: - Esp32IdfPlatform(); - Esp32IdfPlatform(/* UART params if needed */); + public: + Esp32IdfPlatform(uart_port_t uart_num = UART_NUM_1); + ~Esp32IdfPlatform(); - // uart - void knxUartPins(int8_t rxPin, int8_t txPin); - void setupUart() override; + // uart + void knxUartPins(int8_t rxPin, int8_t txPin); - // ip stuff - uint32_t currentIpAddress() override; - uint32_t currentSubnetMask() override; - uint32_t currentDefaultGateway() override; - void macAddress(uint8_t* addr) override; + // Call this after WiFi/Ethernet has started and received an IP. + void setNetif(esp_netif_t* netif); - // unique serial number - uint32_t uniqueSerialNumber() override; + // --- Overridden Virtual Functions --- - // basic stuff - void restart(); + // ip stuff + uint32_t currentIpAddress() override; + uint32_t currentSubnetMask() override; + uint32_t currentDefaultGateway() override; + void macAddress(uint8_t* addr) override; - //multicast - void setupMultiCast(uint32_t addr, uint16_t port) override; - void closeMultiCast() override; - bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; - int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) override; + // unique serial number + uint32_t uniqueSerialNumber() override; - //unicast - bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; + // basic stuff (pure virtual in base) + void restart() override; + void fatalError() override; - //memory - uint8_t* getEepromBuffer(uint32_t size); - void commitToEeprom(); + // multicast + void setupMultiCast(uint32_t addr, uint16_t port) override; + void closeMultiCast() override; + bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; + int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) override; - protected: - in_addr _remoteIP; - protected: - uint16_t _remotePort; + // unicast + bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; - private: - int _udpSock = -1; - int8_t _rxPin = -1; - int8_t _txPin = -1; - // Add NVS handle, etc. as needed + // UART + void setupUart() override; + void closeUart() override; + int uartAvailable() override; + size_t writeUart(const uint8_t data) override; + size_t writeUart(const uint8_t* buffer, size_t size) override; + int readUart() override; + size_t readBytesUart(uint8_t* buffer, size_t length) override; + void flushUart() override; + + // Memory (EEPROM emulation via NVS) + // We override these two functions to provide the low-level storage mechanism. + // The base Platform class will use them when _memoryType is Eeprom. + uint8_t* getEepromBuffer(uint32_t size) override; + void commitToEeprom() override; + + private: + // Network + esp_netif_t* _netif = nullptr; + int _sock = -1; + struct sockaddr_in _remote_addr; + uint32_t _multicast_addr = 0; + uint16_t _multicast_port = 0; + + // UART + uart_port_t _uart_num; + int8_t _rxPin = -1; + int8_t _txPin = -1; + bool _uart_installed = false; + + // NVS (for EEPROM emulation) + nvs_handle_t _nvs_handle; + uint8_t* _eeprom_buffer = nullptr; + uint32_t _eeprom_size = 0; + const char* _nvs_namespace = "knx_eeprom"; + const char* _nvs_key = "data"; }; -#endif \ No newline at end of file +#endif \ No newline at end of file