feat: add ESP-IDF framework support by migrating to ESPHome socket API#13
feat: add ESP-IDF framework support by migrating to ESPHome socket API#13ablyler wants to merge 1 commit intocbpowell:masterfrom
Conversation
Replace Arduino-specific AsyncUDP library with ESPHome's cross-platform
socket API to enable compatibility with both Arduino and ESP-IDF frameworks.
Changes:
- Replace AsyncUDP/ESPAsyncUDP with socket::socket_ip() for UDP communication
- Convert from callback-based packet handling (onPacket) to polling in loop()
- Add framework-specific IP address formatting for lwIP (Arduino) and BSD sockets (ESP-IDF)
- Remove ESPAsyncUDP and ESP32 Async UDP library dependencies
- Add loop() override to poll for incoming UDP packets
- Maintain backward compatibility with existing ESPSense functionality
This allows the component to work with ESP-IDF framework while maintaining
full support for Arduino framework on ESP8266 and ESP32.
Fixes compilation errors when using framework: { type: esp-idf } in ESPHome.
Signed-off-by: Andy Blyler <andy@blyler.cc>
There was a problem hiding this comment.
Pull Request Overview
This PR migrates the ESPSense component from using platform-specific AsyncUDP libraries to ESPHome's unified socket API. This allows the component to work across different platforms without conditional library dependencies.
- Replaced AsyncUDP with ESPHome's socket API for unified cross-platform UDP handling
- Implemented manual UDP socket setup, binding, and packet processing
- Added comprehensive IP address formatting logic to handle different socket implementations
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| components/espsense/espsense.h | Migrated from AsyncUDP to ESPHome socket API with manual socket setup, non-blocking I/O, and cross-platform IP address handling |
| components/espsense/init.py | Removed platform-specific AsyncUDP library dependencies |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (len > REQ_SIZE) { | ||
| ESP_LOGD("ESPSense", "Packet is oversized, ignoring"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
This validation is duplicated in both loop() and parse_packet(). The check in loop() (lines 147-150) can be removed since the same validation already occurs in parse_packet() (lines 221-225), eliminating redundant code.
| if (len > REQ_SIZE) { | |
| ESP_LOGD("ESPSense", "Packet is oversized, ignoring"); | |
| return; | |
| } |
| #ifdef USE_SOCKET_IMPL_LWIP_TCP | ||
| // lwIP structure - access bytes directly | ||
| snprintf(remote_ip_str, sizeof(remote_ip_str), "%d.%d.%d.%d", | ||
| ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]); | ||
| #else | ||
| // BSD sockets - sin_addr.s_addr is uint32_t in network byte order | ||
| uint32_t ip_addr; | ||
| #ifdef USE_SOCKET_IMPL_BSD_SOCKETS | ||
| ip_addr = addr_in->sin_addr.s_addr; | ||
| // ntohl should be available via socket headers | ||
| ip_addr = ntohl(ip_addr); | ||
| #else | ||
| // Fallback: access as bytes | ||
| memcpy(&ip_addr, &addr_in->sin_addr.s_addr, sizeof(uint32_t)); | ||
| // Manual byte swap for network to host | ||
| ip_addr = ((ip_addr & 0xFF000000) >> 24) | ((ip_addr & 0x00FF0000) >> 8) | | ||
| ((ip_addr & 0x0000FF00) << 8) | ((ip_addr & 0x000000FF) << 24); | ||
| #endif | ||
| snprintf(remote_ip_str, sizeof(remote_ip_str), "%d.%d.%d.%d", | ||
| (ip_addr >> 24) & 0xFF, (ip_addr >> 16) & 0xFF, | ||
| (ip_addr >> 8) & 0xFF, ip_addr & 0xFF); | ||
| #endif |
There was a problem hiding this comment.
This complex nested conditional logic for IP address formatting is difficult to follow and maintain. Consider extracting this into a separate helper function (e.g., format_ip_address()) to improve readability and testability.
| uint8_t *ip_bytes = (uint8_t *)&addr_in->sin_addr; | ||
| #ifdef USE_SOCKET_IMPL_LWIP_TCP |
There was a problem hiding this comment.
The variable ip_bytes is declared but only used in the USE_SOCKET_IMPL_LWIP_TCP branch (lines 160-161). Move this declaration inside that conditional block to limit its scope and improve code clarity.
| uint8_t *ip_bytes = (uint8_t *)&addr_in->sin_addr; | |
| #ifdef USE_SOCKET_IMPL_LWIP_TCP | |
| #ifdef USE_SOCKET_IMPL_LWIP_TCP | |
| uint8_t *ip_bytes = (uint8_t *)&addr_in->sin_addr; |
|
Hey thanks for this! Sorry for the delay, just haven’t had a chance to look at it yet. I’ll aim to next week. |
Replace Arduino-specific AsyncUDP library with ESPHome's cross-platform socket API to enable compatibility with both Arduino and ESP-IDF frameworks.
Changes:
This allows the component to work with ESP-IDF framework while maintaining full support for Arduino framework on ESP8266 and ESP32.
Fixes compilation errors when using framework: { type: esp-idf } in ESPHome.