Compare commits

...

177 Commits

Author SHA1 Message Date
Suren Gabrielyan
767a090dc5 Merge pull request #924 from gabsuren/fix/ws_race_on_abort
fix(websocket): Fix websocket client race on abort and memory leak(IDFGH-16555)
2025-12-16 21:02:17 +04:00
surengab
23ca97d5ec fix(websocket): Fix race conditions, memory leak, and data loss
- Add state check in abort_connection to prevent double-close
- Fix memory leak: free errormsg_buffer on disconnect
- Reset connection state on reconnect to prevent stale data
- Implement lock ordering for separate TX lock mode
- Read buffered data immediately after connection to prevent data loss
- Added sdkconfig.ci.tx_lock config
2025-12-16 20:11:29 +04:00
david-cermak
77abff48e1 Merge pull request #973 from david-cermak/fix/mosq_build
fix(mosq): Fix build with the new picolibc
2025-12-16 08:59:55 +01:00
david-cermak
7f8494a4b1 Merge pull request #974 from david-cermak/fix/asio_picolib
[asio]: Fix picolib missing pthread_sigmask() declaration
2025-12-15 13:31:08 +01:00
david-cermak
fdec046428 Merge pull request #972 from david-cermak/fix/modem_ap2ppp_deinit
[modem]: Fix deinit function in ap2ppp example
2025-12-15 12:43:52 +01:00
David Cermak
25d54efd3a fix(asio): Fix picolib missing pthread_sigmask() declaration 2025-12-15 11:11:35 +01:00
David Cermak
7f4e3690db fix(test): Fix catch based target tests with v6.0 2025-12-15 10:52:21 +01:00
David Cermak
ebc1258ea6 fix(mosq): Fix mosquitto build on latest master 2025-12-15 10:06:28 +01:00
David Cermak
dc68bf87cc fix(mosq): Fix build with the new picolibc 2025-12-15 08:34:46 +01:00
David Cermak
853e8e2810 fix(modem): Fix deinit function in ap2ppp example 2025-12-12 16:08:55 +01:00
david-cermak
072781fcd9 Merge pull request #970 from david-cermak/bump/mqtt_cxx_0.5
[mqtt_cxx]: Bump -> v0.5.0
2025-12-11 13:03:32 +01:00
David Cermak
7ce85c7389 bump(mqtt_cxx): 0.4.0 -> 0.5.0
0.5.0
Bug Fixes
- Implement simple unit tests (f41c4a0a)
- Fix to construct in two steps (d979e1b3, #631)
- Add explicit dependency on esp-mqtt if needed (3d5e11b8)
2025-12-11 11:46:04 +01:00
David Cermak
1cc2c97022 fix(mqtt_cxx): Remove clang-tidy for mqtt-cxx as unsupported on latest 2025-12-11 11:46:00 +01:00
David Cermak
f41c4a0ad0 fix(mqtt_cxx): Implement simple unit tests 2025-12-11 11:45:58 +01:00
david-cermak
7336451109 Merge pull request #971 from david-cermak/feat/modem_sim_params
[modem_sim]: Add support to setup initial uart pins
2025-12-11 11:43:29 +01:00
David Cermak
c5b9375b4c fix(modem_sim): Add support to setup initial uart pins 2025-12-11 10:31:40 +01:00
david-cermak
25513d0dc3 Merge pull request #969 from david-cermak/bump/lws_v4.3.3_1
[lws]: Bump 4.3.3 -> 4.3.3~1
2025-12-09 06:37:22 +01:00
david-cermak
c0cf91e174 Merge pull request #968 from david-cermak/bump/eppp_v1.1.4
[eppp]: Bump -> v1.1.4
2025-12-08 11:41:32 +01:00
David Cermak
d979e1b333 fix(mqtt_cxx): Fix to construct in two steps
to avoid issues with receiving initial (on-connect) event before construction

Closes https://github.com/espressif/esp-protocols/issues/631
2025-12-05 18:30:25 +01:00
David Cermak
9f9c6c12f3 fix(mqtt_cxx): Fix build job of mqtt_cxx examples 2025-12-05 18:04:12 +01:00
David Cermak
742adea26d bump(lws): 4.3.3 -> 4.3.3~1
4.3.3~1
Bug Fixes
- Remove lws support for IDF>=v6.0 (b70cc3fc)
- Update websocket Echo server (#894) (318e41b3)
- Adds missing license info (7ea6879a)
Updated
- chore(lws): fixed formatting (91e7e9fa)
2025-12-05 16:00:25 +01:00
David Cermak
2535b3fefc bump(eppp): 1.1.3 -> 1.1.4
1.1.4
Bug Fixes
- Fixed missing freertos deps (f1e35977)
- Add optional mqtt dependency (911c2dbe)
2025-12-05 15:51:54 +01:00
David Cermak
ba0ef10038 fix(eppp): Fix CI to build for supported releases
Partially addresses https://github.com/espressif/esp-protocols/issues/949
2025-12-05 15:50:58 +01:00
David Cermak
f1e35977e5 fix(eppp): Fixed missing freertos deps 2025-12-05 12:54:37 +01:00
david-cermak
25735ead9c Merge pull request #966 from david-cermak/ci/sock_utils_min_build
CI build fix (sock_utils & mbedtls-cxx)
2025-12-05 11:05:41 +01:00
David Cermak
bf9e955514 fix(common): Use component manager from esp-idf docker image 2025-12-05 10:38:58 +01:00
David Cermak
726c41f842 fix(sock_utils): Use minimal build for examples 2025-12-04 08:32:06 +01:00
david-cermak
c7de9251ed Merge pull request #959 from david-cermak/fix/mdns_v1.10
[mdns]: v1.10: Fix to keep TXT/SRV in answers to queries
2025-12-03 12:43:28 +01:00
David Cermak
0f6235f13e fix(mdns): Fix to keep TXT/SRV in answers to queries
unlike discoveries, where they need to stay in additional section
Quick fix of regression b7b8c5dbd7
2025-12-03 11:58:57 +01:00
David Cermak
6c2c2cd22b fix(mdns): Create a test to check answer section for PTR/ANY
to differentiate between discoveries and actual queries

fix(mdns): Revert the fix to check if CI fails
2025-12-03 11:58:01 +01:00
david-cermak
9e0bcd4b08 Merge pull request #957 from david-cermak/bump/mosq_2.0.20_5
[mosq]: Bump 2.0.20~4 -> 2.0.20~5
2025-11-28 11:28:17 +01:00
david-cermak
07b1bcdc34 Merge pull request #776 from david-cermak/mdns/refactor
[mdns]: Refactor stage #1
2025-11-26 08:30:40 +01:00
David Cermak
f20a234f65 fix(mdns): Fix unused variable dcst warning for wifi-remote chips
Forward-port of 081eef88
2025-11-25 17:35:13 +01:00
David Cermak
63082b996d feat(mdns): support null value for boolean txt records
Forward port of fa96de3bd
2025-11-25 17:34:44 +01:00
David Cermak
27d43277d2 fix(mdns): put srv/txt records in additional section for ptr queries
Forward port of b7b8c5db
2025-11-25 17:34:17 +01:00
David Cermak
4d8d25a345 fix(mdns): Host test to use hw_support include dir
Forward port of 8bba3a97
2025-11-25 17:32:59 +01:00
David Cermak
bed116d98b feat(mdns): Refactor mdns library (stage #1)
The MDNS component has been refactored from a single monolithic file mdns.c
into a set of focused modules with clear responsibilities.
This restructuring maintains the same functionality while improving code organization,
maintainability, and testability.
In the stage#2 we will focus on module based tests
In the stage#3 we will focus on small scale refators and optimizations
2025-11-25 17:30:27 +01:00
David Cermak
9ec006a3e4 bump(mosq): 2.0.20~4 -> 2.0.20~5
2.0.20~5
Features
- Add support for basic MQTT authentication (65b58aa0)
Bug Fixes
- Add optional mqtt deps to examples (6f6110e3)
- Update example to optionally use basic mqtt auth (38384852)
- Fix unpwd-check wrap function (ba3377b2)
- Fix the version check (9fbb6e6d)
2025-11-25 13:47:02 +01:00
david-cermak
245b5a2ffb Merge pull request #956 from david-cermak/fix/modem_ping_in_examples
[modem]: Bump -> v2.0
2025-11-25 12:23:15 +01:00
David Cermak
b9ea0c31ce bump(modem): 1.4.0 -> 2.0.0
2.0.0
Breaking changes
- inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp) (Use generated AT command definitions for IDE navigation)
Features
- Add support for multiple connection in AT based example (2826287d)
- Add enhanced URC observer API (4889dd6f)
- Support esp-modem use without PPP (858f8570, #851)
- Modem simulator based on esp-at (e5787e3d)
Bug Fixes
- Update tests and examples to use modem-v2.0 (4aa0e4ba)
- Replace MQTT client with simple ping command (0ccaf2c0)
- Replace MQTT client with simple ping command (9b2b1f68)
- Update example to use optional mqtt deps (3141d6ca)
- Minor fixed in the test code (e772ce67)
- Add missing set_echo() C wrapper (d1e67080, #926)
- Fix modem console dependencies (453be4cd)
- Address build issues (018ba58e)
- Fix driver dependency issue on v6.0 (67c682d9)
- Fix CI build issues with IDFv6.0 (15140e04)
- Add support for ESP-AT based tcp-client example (14d3cb6b)
- Use idf-build-apps for building target tests (e9d9b3a8)
- Make MQTT public broker endpoint configurable (6d541194)
- Fix URC handling in DTE data callback (93029946)
- Use another public broker for examples and tests (fac2edbe)
- Fix incompatible iterator in std::search() in new gcc (ed0f6334)
- Fix autodetect to support ACFC mode in PPP frames (8b328a69, #801)
- Fix get_network_registration_state() to accept two params (5f54d907, #826)
- Consume buffer after handled URC (6eceb28f)
- Use generated AT command definitions for IDE navigation (e2fa1110, !BREAKING)
2025-11-24 15:57:10 +01:00
David Cermak
4aa0e4ba49 fix(modem): Update tests and examples to use modem-v2.0 2025-11-24 15:57:06 +01:00
David Cermak
c48c2ebe7e fix(modem): Use current IDF releases for modem tests 2025-11-24 15:23:17 +01:00
David Cermak
0ccaf2c0bb fix(modem): Replace MQTT client with simple ping command 2025-11-24 14:43:08 +01:00
david-cermak
ed569d8509 Merge pull request #954 from david-cermak/fix/mosq_mqtt_deps
[mosq]: Add optional mqtt deps to examples
2025-11-21 13:28:33 +01:00
David Cermak
9b2b1f680d fix(modem): Replace MQTT client with simple ping command 2025-11-21 12:37:09 +01:00
david-cermak
370dfecc15 Merge pull request #933 from david-cermak/fix/modem_set_echo
fix(modem): Add missing set_echo() C wrapper
2025-11-20 13:12:17 +01:00
David Cermak
e50c5eb40e fix(mosq): Update IDF version matrix in CI
publish-connect test moved from IDF -> esp-mqtt in v6.0
serverless example not supported on latest releases
2025-11-20 08:55:00 +01:00
David Cermak
6f6110e30e fix(mosq): Add optional mqtt deps to examples 2025-11-20 08:33:01 +01:00
david-cermak
41f7157ffb Merge pull request #951 from david-cermak/fix/asio_ci
[asio]: Fix CI target tests
2025-11-19 21:07:24 +01:00
David Cermak
858f67c280 fix(asio): Run target tests on virtual env 2025-11-19 13:40:43 +01:00
david-cermak
83ffeb0d12 Merge pull request #950 from david-cermak/fix/lws_remove_v6.0_support
[lws]: Remove lws support for IDF>=v6.0
2025-11-19 13:39:29 +01:00
david-cermak
6e99202a18 Merge pull request #948 from david-cermak/fix/ci_v6.0
Common fixes per v6.0/6.1 changes
2025-11-19 13:39:16 +01:00
david-cermak
fb5279ae88 Merge pull request #938 from david-cermak/feat/mosq_basic_auth
[mosq]: Add support for basic MQTT authentication
2025-11-19 13:39:05 +01:00
David Cermak
b70cc3fc09 fix(lws): Remove lws support for IDF>=v6.0 2025-11-19 11:57:57 +01:00
David Cermak
911c2dbe9f fix(eppp): Add optional mqtt dependency 2025-11-19 10:41:34 +01:00
David Cermak
c620855d56 fix(sock_utils): Run target tests via py-venv 2025-11-19 10:40:04 +01:00
david-cermak
cccfdd9315 Merge pull request #946 from david-cermak/fix/examples_mqtt
Add optional mqtt deps to examples
2025-11-19 10:33:14 +01:00
David Cermak
ecc7258093 ci(examples): Fix examples CI to build/test on supported releases
Ignore "The smallest app partition is nearly full" unconditionally
as most stable releases produce this on most projects
2025-11-19 10:11:13 +01:00
David Cermak
0caea67542 fix(examples): Add optional return for low level netif input
Also fixes driver/uart-driver dependency
2025-11-19 08:33:36 +01:00
david-cermak
11a8567598 Merge pull request #884 from david-cermak/feat/modem_prepare_v2.0
[modem]: Prepare for v2.0 release
2025-11-18 18:03:25 +01:00
David Cermak
9b80a7ef7d fix(example): Use eth-phy-generic on IDF>=5.4 2025-11-18 16:26:23 +01:00
David Cermak
12028ab250 fix(examples): Add optional mqtt deps to examples 2025-11-18 15:38:04 +01:00
David Cermak
68e299a357 docs(modem): Update docs and migration manual
Document the development mode
2025-11-18 15:28:05 +01:00
david-cermak
2681b9b3c6 Merge pull request #945 from david-cermak/fix/common_mqtt_deps
Add mqtt dependency to component's projects
2025-11-18 15:26:14 +01:00
david-cermak
782b7cd119 Merge pull request #849 from david-cermak/feat/modem_tcp_client_multi_conn
[modem]: tcp-client example to support multiple connections
2025-11-18 15:25:21 +01:00
David Cermak
3141d6cab5 fix(modem): Update example to use optional mqtt deps 2025-11-18 14:39:18 +01:00
David Cermak
7b8770e2fc fix(console): Add explicit dependency on esp-mqtt if needed 2025-11-18 14:16:34 +01:00
David Cermak
6153c0002a fix(console): Workaround ethernet-init IDF dependency 2025-11-18 14:16:34 +01:00
David Cermak
3d5e11b82f fix(mqtt_cxx): Add explicit dependency on esp-mqtt if needed 2025-11-18 14:16:34 +01:00
David Cermak
eacc3a0aa8 fix(common): Use actual IDF releases for console ci
And ignore potential ethernet-init warnings when used with cmd_ifconfig
2025-11-18 14:15:30 +01:00
David Cermak
2826287d43 feat(modem): Add support for multiple connection in AT based example 2025-11-18 10:37:01 +01:00
david-cermak
3bfa00389d Merge pull request #944 from david-cermak/bump/mdns_v1.9.1
[mdns]: Bump to `v1.9.1`
2025-11-18 10:36:46 +01:00
David Cermak
ace7fca8c6 bump(mdns): 1.9.0 -> 1.9.1
1.9.1
Bug Fixes
- Fix to use tagged AFL image + minor format fix (2b2f009a)
- Fix unused variable `dcst` warning for wifi-remote chips (081eef88)
2025-11-14 17:20:28 +01:00
David Cermak
2b2f009a65 fix(mdns): Fix to use tagged AFL image + minor format fix 2025-11-14 17:19:55 +01:00
david-cermak
1444d575f0 Merge pull request #940 from vikramdattu/bugfix/mdns_unused_var_wifi_remote
fix(mdns): Fix unused variable `dcst` warning for wifi-remote chips (IDFGH-16787)
2025-11-14 17:18:30 +01:00
Vikram Dattu
081eef88cf fix(mdns): Fix unused variable dcst warning for wifi-remote chips
- Cover WIFI_REMOTE as well for MDNS_ESP_WIFI_ENABLED macro
 - localize `esp_netif_dhcp_status_t dcst;` declaration where used
2025-11-14 17:43:54 +05:30
david-cermak
8b0704eaf4 Merge pull request #883 from david-cermak/feat/modem_urc
[modem]: Enhanced URC handling
2025-11-14 12:52:03 +01:00
David Cermak
4889dd6fcb feat(modem): Add enhanced URC observer API 2025-11-14 11:44:48 +01:00
david-cermak
134247d88f Merge pull request #711 from david-cermak/fix/modem_ota_test
[modem]: Minor fixed in the test code
2025-11-14 11:36:36 +01:00
David Cermak
e772ce673d fix(modem): Minor fixed in the test code
* Updated iperf commands
* Updated Github certificat
2025-11-14 10:27:29 +01:00
Suren Gabrielyan
3d4712b905 Merge pull request #941 from gabsuren/ws_bump_1.6.0
bump(websocket): 1.5.0 -> 1.6.0
2025-11-14 12:08:52 +04:00
surengab
67188fd7b4 bump(websocket): 1.5.0 -> 1.6.0
1.6.0
Features
- add WEBSOCKET_EVENT_HEADER_RECEIVED (#827) (18f0d028, #715)
- enhance example with docs, pytest setup, and standalone test server     - Add comprehensive README with TOC and quick start     - Add pytest setup and certificate generation scripts     - Add standalone WebSocket test server with TLS support     - Add troubleshooting and multiple testing approaches (cad527d2)
- Add websocket HTTP redirect (ce1560ac)
Bug Fixes
- remove redundant timeout check in client task loop (1e83bee4)
- fix PING timing - enable periodic PING during active traffic (7f424325)
- Update linux build docs on required libs (e52a5757)
- clean up component dependencies - Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration - Add cJSON dependency to target example's idf_component.yml (d665e6f1)
- fix relying on asprintf() to NULL strp on failure (54eb0027)
- Update Remaining Websocket Echo Server (#893) (18faeb3d)
- avoid long stopping time when waiting to auto-reconnect (2432e41d)
- Update Websocket Echo Server (94bd5b07)
Updated
- ci(common): Update test component dir for IDFv6.0 (18418c83)
2025-11-13 16:55:20 +04:00
Suren Gabrielyan
4d7c6848b2 Merge pull request #935 from gabsuren/refactor/websocket_timeout_check_remove
fix(websocket): remove redundant timeout check in client task loop
2025-11-13 15:59:45 +04:00
surengab
1e83bee4fe fix(websocket): remove redundant timeout check in client task loop 2025-11-12 22:00:55 +04:00
David Cermak
3838485229 fix(mosq): Update example to optionally use basic mqtt auth 2025-11-12 11:45:40 +01:00
David Cermak
ba3377b262 fix(mosq): Fix unpwd-check wrap function 2025-11-12 11:44:55 +01:00
David Cermak
e5bed394ee fix(ci): Run mosquitto tests on py-venv3.12 2025-11-10 11:55:29 +01:00
David Cermak
65b58aa05a feat(mosq): Add support for basic MQTT authentication 2025-11-10 09:19:23 +01:00
David Cermak
d1e6708063 fix(modem): Add missing set_echo() C wrapper
Closes https://github.com/espressif/esp-protocols/issues/926
2025-11-05 12:57:04 +01:00
Suren Gabrielyan
318bca1657 Merge pull request #930 from gabsuren/fix/ping_unreset
fix(websocket): fix PING timing - enable periodic PING during active traffic (IDFGH-16701)
2025-11-05 10:49:06 +04:00
david-cermak
9e1b9cdd20 Merge pull request #929 from david-cermak/bump/mdns_v1.9
[mdns]:  Bump version to `v1.9`
2025-11-03 12:09:42 +01:00
surengab
7f424325d8 fix(websocket): fix PING timing - enable periodic PING during active traffic 2025-11-03 14:03:30 +04:00
David Cermak
9ef228f247 bump(mdns): 1.8.2 -> 1.9.0
1.9.0
Features
- support null value for boolean txt records (fa96de3b)
Bug Fixes
- Add test case for bool/NULL txt handling (5068f221)
- Temporary fix for build issues on IDF master (0197c994)
- Add tests for delegated answers (487a746d)
- Add fuzzing into mdns CI (af6bb1b5)
- Host test to use hw_support include dir (8bba3a97)
- Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free (63bf7091)
- put srv/txt records in additional section for ptr queries (b7b8c5db)
Updated
- ci(common): Update test component dir for IDFv6.0 (18418c83)
2025-11-03 08:12:38 +01:00
David Cermak
5068f2217e fix(mdns): Add test case for bool/NULL txt handling 2025-11-03 08:11:41 +01:00
david-cermak
1ceb42c5a2 Merge pull request #928 from david-cermak/fix/websocket_linux_docs
fix(websocket): Update linux build docs on required libs
2025-10-31 08:22:58 +01:00
Alexander Salas Bastidas
e52a5757f1 fix(websocket): Update linux build docs on required libs
ESP-IDF linux target expects libbsd headers
Updated instructions for compiling the example on Linux to include the correct path for the executable.
2025-10-30 19:41:31 +01:00
Suren Gabrielyan
732cd29ec0 Merge pull request #925 from gabsuren/ci/fix_json_dependancy
fix(websocket): resolve JSON dependency issues for component and exam…
2025-10-29 20:26:42 +04:00
david-cermak
7a203cf085 Merge pull request #921 from david-cermak/bump/console_ping
bump(console): 1.1.0 -> 1.2.0
2025-10-29 17:04:32 +01:00
surengab
d665e6f18e fix(websocket): clean up component dependencies
- Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration
- Add cJSON dependency to target example's idf_component.yml
2025-10-29 19:19:02 +04:00
David Cermak
2e269640c6 bump(console): 1.1.0 -> 1.2.0
1.2.0
Features
- Add support for interface argument (90ddb04e)
2025-10-27 14:34:59 +01:00
david-cermak
c078c36361 Merge pull request #886 from david-cermak/feat/console_ping_interface
[console_ping]: Add support for interface argument
2025-10-24 15:17:14 +02:00
David Cermak
90ddb04e53 feat(console_ping): Add support for interface argument 2025-10-24 15:12:05 +02:00
david-cermak
cee3bdea9d Merge pull request #822 from tanyanquan/feat/support_null_value_txt
feat(mdns): support null value for boolean txt records
2025-10-24 14:05:47 +02:00
Tan Yan Quan
fa96de3bd7 feat(mdns): support null value for boolean txt records 2025-10-24 16:12:10 +08:00
bryghtlabs-richard
18f0d02806 feat(websocket): add WEBSOCKET_EVENT_HEADER_RECEIVED (#827)
Send a new event for each HTTP header-line received.

Depends on https://github.com/espressif/esp-idf/pull/16119
Closes https://github.com/espressif/esp-protocols/issues/715
2025-10-22 18:25:31 -03:00
david-cermak
bfa604b5f6 Merge pull request #904 from david-cermak/feat/add_tests_v1.9
[mdns]: Add tests for recent feats/fixes
2025-10-22 11:07:59 +02:00
David Cermak
92a31187ff fix(ci): Use python venv for mdns target tests 2025-10-21 17:16:21 +02:00
David Cermak
0197c994ee fix(mdns): Temporary fix for build issues on IDF master 2025-10-21 17:11:46 +02:00
David Cermak
487a746d14 fix(mdns): Add tests for delegated answers 2025-10-21 17:11:46 +02:00
David Cermak
af6bb1b5ee fix(mdns): Add fuzzing into mdns CI 2025-10-21 17:11:46 +02:00
Suren Gabrielyan
f5e62e83e9 Merge pull request #902 from gabsuren/docs/readme_update
[examples]: enhance example with docs, pytest setup, and standalone test server(IDFGH-16585)
2025-10-21 13:07:38 +04:00
surengab
cad527d2fc feat(examples): enhance example with docs, pytest setup, and standalone test server
- Add comprehensive README with TOC and quick start
    - Add pytest setup and certificate generation scripts
    - Add standalone WebSocket test server with TLS support
    - Add troubleshooting and multiple testing approaches
2025-10-21 12:17:00 +04:00
david-cermak
16cc2dcffb Merge pull request #914 from david-cermak/fix/modem_forwardport_patch
[modem]: Forward-port 1.4.1 patches
2025-10-21 07:14:47 +02:00
david-cermak
d622e41a54 Merge pull request #915 from david-cermak/fix/target_tests
[websockets]: Fix target tests
2025-10-21 07:14:28 +02:00
glmfe
ca6e39aac7 ci(common): Ignore ethernet warning. 2025-10-20 16:27:57 +02:00
David Cermak
e45944e143 fix(websocket): Use venv for running target tests 2025-10-20 16:27:22 +02:00
David Cermak
fe657b9737 fix(modem): Ignore more build warnings 2025-10-20 09:35:00 +02:00
David Cermak
453be4cd79 fix(modem): Fix modem console dependencies 2025-10-20 09:34:15 +02:00
David Cermak
018ba58ec5 fix(modem): Address build issues 2025-10-20 09:30:47 +02:00
David Cermak
67c682d911 fix(modem): Fix driver dependency issue on v6.0 2025-10-20 09:30:41 +02:00
Suren Gabrielyan
91915ce1c7 Merge pull request #901 from bryghtlabs-richard/fix/websocket-asprintf-ret-checks
fix(websocket): fix relying on asprintf() to NULL strp on failure (IDFGH-16595)
2025-10-15 14:27:56 +04:00
david-cermak
ae052e5507 Merge pull request #905 from david-cermak/fix/eppp_netif_rx_err
[eppp]: Fix tun netif to (optionally) return errors
2025-10-09 14:03:03 +02:00
David Cermak
44524f5de0 bump(eppp): 1.1.2 -> 1.1.3
1.1.3
Bug Fixes
- Fix test dependency issue on driver (1ace92c2)
- Fix tun netif to (optionally) return errors (7a6cf0f9)
2025-10-09 13:04:46 +02:00
David Cermak
1ace92c279 fix(eppp): Fix test dependency issue on driver 2025-10-09 13:03:06 +02:00
David Cermak
7a6cf0f9c0 fix(eppp): Fix tun netif to (optionally) return errors 2025-10-08 16:20:18 +02:00
Richard Allen
54eb002758 fix(websocket): fix relying on asprintf() to NULL strp on failure
asprintf()'s return value:
    When successful, these functions return the number of bytes
    printed, just like sprintf(3).  On error, -1 is returned, errno is
    set to indicate the error, and the contents of strp are undefined.

Fixes the following:
    error: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
2025-10-03 08:44:00 -05:00
Guilherme Alves Ferreira
318e41b3c3 fix(lws): Update websocket Echo server (#894)
* fix(lws): Update websocket Echo server

- Update Websocket Echo Server, "wss://echo.websocket.events" is no longer available.
2025-10-03 06:17:11 -03:00
david-cermak
6f6237a0cc Merge pull request #813 from tanyanquan/master
fix(mdns): put srv/txt records in additional section for ptr queries
2025-10-01 13:59:03 +02:00
Guilherme Alves Ferreira
18faeb3dfa fix(websocket): Update Remaining Websocket Echo Server (#893) 2025-10-01 07:52:42 -03:00
Guilherme Alves Ferreira
296123c14e Merge pull request #896 from glmfe/fix/ignore-ci-build-warnings
ci(common): Ignore deprecated idf-build-apps parameters
2025-09-30 10:50:22 -03:00
glmfe
4e178f06bd ci(common): Ignore deprecated idf-build-apps parameters
- Added deprecated flash, sign and extract_public_key to global warning list file
2025-09-29 08:21:50 -03:00
Guilherme Alves Ferreira
5ab7e8327e Merge pull request #729 from DazeTechnology/fix/websocket_long_stop
fix(websocket): avoid long stop time when waiting to auto-reconnect (IDFGH-14393)
2025-09-25 10:12:39 -03:00
Paolo Pasinetti
91e7e9fa08 chore(lws): fixed formatting 2025-09-19 15:42:33 +02:00
Paolo Pasinetti
ff5d6021be chore(asio): Fixed formatting 2025-09-19 15:42:33 +02:00
Paolo Pasinetti
2432e41dcb fix(websocket): avoid long stopping time when waiting to auto-reconnect
This commit fixes an issue that occurred when auto-reconnection is enabled,
and the client is disconnected from the server. In this situation, if the
esp_websocket_client_stop() method is called, the caller could remain stuck
waiting for a maximum time equal to half of wait_timeout_ms.

This fix allows the esp_websocket_client_task to be woken up when it is
waiting to reconnect, so it can be closed promptly when requested.
2025-09-19 15:42:33 +02:00
Guilherme Alves Ferreira
870ac91db7 Merge pull request #890 from glmfe/fix/update-ws-echo-server
fix(websocket): Update Websocket Echo Server
2025-09-19 08:35:01 -03:00
glmfe
94bd5b074a fix(websocket): Update Websocket Echo Server 2025-09-19 07:44:12 -03:00
david-cermak
db7baaffba Merge pull request #887 from david-cermak/fix/eppp_uart_deps
[eppp]: Fix uart driver deps with new IDF
2025-09-15 13:41:53 +02:00
David Cermak
1ea93a866b bump(eppp): 1.1.1 -> 1.1.2
1.1.2
Bug Fixes
- Update uart driver deps per IDF > v5.3 (92e14607)
2025-09-15 12:55:14 +02:00
David Cermak
92e1460721 fix(eppp): Update uart driver deps per IDF > v5.3 2025-09-15 12:55:08 +02:00
david-cermak
858d38b55f Merge pull request #885 from david-cermak/fix/eppp_uart_channels
[eppp]: Fix getting context for channel API
2025-09-09 17:03:00 +02:00
David Cermak
6428e68c8e bump(eppp): 1.1.0 -> 1.1.1
1.1.1
Bug Fixes
- Fix getting context for channel API (94563cdc)
- Cover more combinations in build tests (e0b8de8f)
2025-09-09 16:18:11 +02:00
David Cermak
94563cdc1f fix(eppp): Fix getting context for channel API 2025-09-09 13:42:18 +02:00
David Cermak
e0b8de8f38 fix(eppp): Cover more combinations in build tests 2025-09-09 13:42:18 +02:00
Guilherme Alves Ferreira
34b6681576 Merge pull request #771 from glmfe/feat/add-http-redir
feat(websocket): Add websocket HTTP redirect
2025-09-03 12:31:20 -03:00
david-cermak
1f7828f629 Merge pull request #875 from david-cermak/update/eppp_v1.1
[eppp]: Bump 1.0.1 -> 1.1.0
2025-09-03 16:03:05 +02:00
david-cermak
e74db36ebb Merge pull request #881 from david-cermak/fix/minor_ci_fixes
Fixup common CI issues with v6.0 idf-tools
2025-09-03 13:16:49 +02:00
David Cermak
f8d2ed2eed fix(tls_cxx): Remove unnnecessary warning ignore 2025-09-03 12:07:21 +02:00
David Cermak
8bba3a9734 fix(mdns): Host test to use hw_support include dir 2025-09-03 11:37:47 +02:00
David Cermak
9fbb6e6d0a fix(mosq): Fix the version check 2025-09-03 10:20:53 +02:00
David Cermak
e599cd826b fix(common): Fixup common CI issues with v6.0 2025-09-03 10:06:13 +02:00
david-cermak
e2d36b4fbd Merge pull request #879 from david-cermak/fix/modem_sim_napt
[modem_sim]: Enable LWIP-NAPT by default in modem simulator
2025-09-03 09:27:41 +02:00
david-cermak
82a784baf4 Merge pull request #880 from david-cermak/fix/modem_ci
[modem]: Fix recent CI issues
2025-09-02 18:01:05 +02:00
David Cermak
15140e04c6 fix(modem): Fix CI build issues with IDFv6.0
* Fix C++ console struct init in examples
* VFS deprecation in tests
* Add conditional defines over wakeup-cause
2025-09-02 15:47:00 +02:00
glmfe
ce1560acb1 feat(websocket): Add websocket HTTP redirect
- Handle 301 status (moved permanently) and redirect the connection to the new host.
2025-09-02 09:52:20 -03:00
David Cermak
e1be830fb7 fix(modem_sim): Enable LWIP-NAPT by default in modem simulator
As this mode is used by PPP clients
2025-09-01 18:47:47 +02:00
david-cermak
b7cfa31a0b Merge pull request #878 from david-cermak/bump/mosq_2.0.20.4
bump(mosq): 2.0.20~3 -> 2.0.20~4
2025-09-01 11:54:20 +02:00
David Cermak
845a1e2ef8 fix(mosq): Skip serverless example build on master
Due to esp-peer's long list dependencies, which are usually based
from stable IDF releases
2025-09-01 10:08:11 +02:00
David Cermak
6ae7a4d2ba bump(mosq): 2.0.20~3 -> 2.0.20~4
2.0.20~4
Features
- Update brokerless example to work with esp-peer (76e45f72)
Bug Fixes
- drop newlib from PRIV_REQUIRES (6318022c)
- Make esp-peer build optional (03df9ae9)
- Fix esp_webRTC deprecation with new FreeRTOS (78ae2559)
2025-09-01 09:55:34 +02:00
david-cermak
4d52982a69 Merge pull request #876 from Lapshin/master
fix: drop newlib from PRIV_REQUIRES
2025-09-01 09:27:09 +02:00
Alexey Lapshin
6318022cda fix: drop newlib from PRIV_REQUIRES 2025-08-28 16:18:22 +07:00
David Cermak
13591ade3d bump(eppp): 1.0.1 -> 1.1.0
1.1.0
Features
- Add support for UART flow control (cd57f1bb, #870)
Bug Fixes
- Fix SPI transport to allow already init GPIO ISR (497ee2d6, #868)
- Fix stack-overflow in ping task for TUN netif (b2568a3d, #867)
Updated
- ci(common): Update test component dir for IDFv6.0 (18418c83)
2025-08-27 15:11:15 +02:00
david-cermak
d09b302b9e Merge pull request #872 from david-cermak/fix/eppp_minor_fixes
[eppp]: Minor fixes v1.0
2025-08-27 08:57:25 +02:00
david-cermak
39e2333adb Merge pull request #871 from david-cermak/fix/modem_sim_docs
[modem_sim]: Add initial modem simulator docs
2025-08-26 11:00:46 +02:00
David Cermak
cd57f1bb13 feat(eppp): Add support for UART flow control
Closes https://github.com/espressif/esp-protocols/issues/870
2025-08-25 16:30:57 +02:00
David Cermak
497ee2d6d4 fix(eppp): Fix SPI transport to allow already init GPIO ISR
Closes https://github.com/espressif/esp-protocols/issues/868
2025-08-25 15:23:07 +02:00
David Cermak
b2568a3d83 fix(eppp): Fix stack-overflow in ping task for TUN netif
Closes https://github.com/espressif/esp-protocols/issues/867
2025-08-25 14:51:59 +02:00
david-cermak
58a21e39d0 Merge pull request #837 from david-cermak/feat/modem_tcp_client_extend
[modem]: Extend tcp-client example
2025-08-25 08:53:51 +02:00
David Cermak
9c7ee07755 feat(modem_sim): Add initial modem simulator docs 2025-08-25 08:52:48 +02:00
David Cermak
14d3cb6bd1 fix(modem): Add support for ESP-AT based tcp-client example 2025-08-25 06:38:57 +02:00
david-cermak
ccdb45ee94 Merge pull request #869 from david-cermak/fix/test_build_v6.0
fix(CI): Fix test component dir after IDFv6.0 update
2025-08-22 15:01:59 +02:00
David Cermak
18418c83ff ci(common): Update test component dir for IDFv6.0
Update websocket, mdns and eppp test apps
2025-08-22 14:23:43 +02:00
david-cermak
ecb7dae502 Merge pull request #863 from david-cermak/feat/modem_without_ppp
[modem]: Support esp-modem use without PPP
2025-08-21 12:46:29 +02:00
david-cermak
958ff6a584 Merge pull request #866 from david-cermak/fix/modem_sim
[modem_sim]: ESP-AT build failure on v5.4
2025-08-18 17:12:53 +02:00
David Cermak
5ea83be7ce fix(modem_sim): Fix build step 2025-08-18 16:35:07 +02:00
David Cermak
858f85706d feat(modem): Support esp-modem use without PPP
Closes https://github.com/espressif/esp-protocols/issues/851
2025-08-14 17:29:50 +02:00
Tan Yan Quan
b7b8c5dbd7 fix(mdns): put srv/txt records in additional section for ptr queries 2025-05-08 19:09:52 +08:00
283 changed files with 15807 additions and 10304 deletions

View File

@@ -81,18 +81,24 @@ jobs:
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }}
path: ${{ env.TEST_DIR }}/${{ matrix.example }}/build
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Download Example Test to target ${{ matrix.config }}
run: |
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 ${{ env.TEST_DIR }}/${{ matrix.example }}/build/flash_image.bin
- name: Run Example Test ${{ matrix.example }} on target
working-directory: ${{ env.TEST_DIR }}/${{ matrix.example }}
run: |
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
- uses: actions/upload-artifact@v4
if: always()
with:

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
idf_target: ["esp32"]
test: [ { app: ifconfig-basic, path: "components/console_cmd_ifconfig/examples"}]
include:

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
idf_target: ["esp32"]
test: [ { app: mqtt_ssl_auth_console, path: "components/console_cmd_mqtt/examples" }]
runs-on: ubuntu-22.04

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
idf_target: ["esp32"]
test: [ { app: ping-basic, path: "components/console_cmd_ping/examples" }]
runs-on: ubuntu-22.04

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
idf_target: ["esp32"]
test: [ { app: wifi-basic, path: "components/console_cmd_wifi/examples" }]
runs-on: ubuntu-22.04

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
idf_target: ["esp32"]
test: [ { app: console_basic, path: "components/console_simple_init/examples" }]
runs-on: ubuntu-22.04

View File

@@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3"]
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3"]
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}

View File

@@ -33,6 +33,9 @@ jobs:
shell: bash
working-directory: ${{matrix.test.path}}
run: |
if [[ "${{ matrix.idf_ver }}" == "release-v5.3" || "${{ matrix.idf_ver }}" == "release-v5.4" ]]; then
export EXPECTED_WARNING="unknown kconfig symbol 'LWIP_USE_ESP_GETADDRINFO'"
fi
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@@ -13,10 +13,7 @@ jobs:
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
include:
- idf_ver: "latest"
warning: "Warning: The smallest app partition is nearly full"
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
@@ -27,7 +24,7 @@ jobs:
uses: actions/checkout@v4
- name: Build with IDF-${{ matrix.idf_ver }}
env:
EXPECTED_WARNING: ${{ matrix.warning }}
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full"
shell: bash
run: |
. ${IDF_PATH}/export.sh
@@ -75,7 +72,7 @@ jobs:
needs: build_all_examples
strategy:
matrix:
idf_ver: ["release-v5.4", "latest"]
idf_ver: ["release-v5.5", "latest"]
runs-on:
- self-hosted
- modem

View File

@@ -13,7 +13,7 @@ jobs:
name: Libwebsockets build
strategy:
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
test: [ { app: example, path: "examples/client" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
@@ -51,7 +51,7 @@ jobs:
strategy:
fail-fast: false
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples/client" }]
runs-on:
@@ -65,14 +65,24 @@ jobs:
with:
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -24,6 +24,11 @@ jobs:
shell: bash
run: |
. ${IDF_PATH}/export.sh
if [[ "${{ matrix.idf_ver }}" == "latest" ]]; then
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'"
else
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_GENERIC'"
fi
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -d
@@ -71,6 +76,22 @@ jobs:
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
working-directory: components/mdns/${{ matrix.test.path }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -29,6 +29,8 @@ jobs:
# Build host tests app (with all configs and targets supported)
python ./ci/build_apps.py components/mdns/tests/host_test/
cd components/mdns/tests/host_test
ls -la
ls -ls build*
# First run the linux_app and send a quick A query and a reverse query
./build_linux_app/mdns_host.elf &
python dnsfixture.py A myesp.local --ip_only | xargs python dnsfixture.py X
@@ -55,8 +57,11 @@ jobs:
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd components/mdns/tests/test_afl_fuzz_host/
make INSTR=off
cd components/mdns/tests/host_unit_test/
idf.py reconfigure
mkdir build2 && cd build2
cmake ..
cmake --build .
- name: Test no malloc functions
shell: bash
run: |
@@ -68,3 +73,89 @@ jobs:
diff -q $file /tmp/$file || exit 1
echo "OK"
done
host_unit_test:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Unit tests on host
strategy:
matrix:
idf_ver: ["latest"]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
- name: Install bsdlib and ruby
run: |
apt-get update -y
apt-get install -y libbsd-dev ruby
- name: Build and run unit tests
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd components/mdns/tests/host_unit_test/
idf.py reconfigure
mkdir build2 && cd build2
cmake -DUNIT_TESTS=test_receiver ..
cmake --build .
ctest --extra-verbose
cd ..
mkdir build3 && cd build3
cmake -DUNIT_TESTS=test_sender ..
cmake --build .
ctest --extra-verbose
fuzz_test:
if: contains(github.event.pull_request.labels.*.name, 'mdns-fuzz') || github.event_name == 'push'
name: Fuzzer tests for mdns lib
strategy:
matrix:
idf_ver: ["latest"]
runs-on: ubuntu-22.04
container: aflplusplus/aflplusplus:v4.34c
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
- name: Checkout ESP-IDF
uses: actions/checkout@v4
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
- name: Install Necessary Libs
run: |
apt-get update -y
apt-get install -y libbsd-dev
- name: Run AFL++
shell: bash
run: |
export IDF_PATH=$GITHUB_WORKSPACE/idf
cd components/mdns/tests/host_unit_test/
pip install dnslib
cd input && python generate_cases.py && cd ..
cmake -B build2 -S . -G "Ninja" -DCMAKE_C_COMPILER=afl-cc
cmake --build build2
timeout 10m afl-fuzz -i input -o out -- build2/mdns_host_unit_test || \
if [ $? -eq 124 ]; then # timeout exit code
if [ -n "$(find out/default/crashes -type f 2>/dev/null)" ]; then
echo "Crashes found!";
tar -czf out/default/crashes.tar.gz -C out/default crashes;
exit 1;
fi
else
exit 1;
fi
- name: Upload Crash Artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-crashes
path: components/mdns/tests/host_unit_test/out/default/crashes.tar.gz
if-no-files-found: ignore

View File

@@ -13,13 +13,8 @@ jobs:
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
include:
- idf_ver: "release-v5.0"
example: "simple_cmux_client"
warning: "Warning: The smallest app partition is nearly full"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
@@ -30,9 +25,9 @@ jobs:
- if: ${{ matrix.skip_config }}
run: rm -f $GITHUB_WORKSPACE/protocols/components/esp_modem/examples/${{ matrix.example }}/sdkconfig.ci.${{ matrix.skip_config }}*
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
env:
EXPECTED_WARNING: ${{ matrix.warning }}
shell: bash
env:
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full\nwarning: unknown kconfig symbol 'ESP32_PANIC_PRINT_HALT'"
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
@@ -44,7 +39,7 @@ jobs:
name: Build tests
strategy:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
idf_ver: ["latest","release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
test: ["target", "target_ota", "target_iperf", "target_urc"]
runs-on: ubuntu-22.04

View File

@@ -13,7 +13,7 @@ jobs:
name: Build Target tests
strategy:
matrix:
idf_ver: ["latest"]
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
idf_target: ["esp32c3"]
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
include:
@@ -54,7 +54,7 @@ jobs:
name: Run Target tests
strategy:
matrix:
idf_ver: ["latest"]
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
idf_target: ["esp32c3"]
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
include:

View File

@@ -11,17 +11,21 @@ jobs:
build_modem_sim:
if: contains(github.event.pull_request.labels.*.name, 'modem_sim') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["release-v5.4"]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
runs-on: ubuntu-latest
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Checkout idf
uses: actions/checkout@v3
with:
repository: espressif/esp-idf
ref: 8ad0d3d8f2faab752635bee36070313c47c07a13
path: idf
- name: Build ESP-AT with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
export IDF_PATH=$GITHUB_WORKSPACE/idf
${IDF_PATH}/install.sh
cd common_components/modem_sim
./install.sh
source export.sh

View File

@@ -13,15 +13,16 @@ jobs:
name: Mosquitto build
strategy:
matrix:
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
example: ["broker", "serverless_mqtt"]
exclude:
- idf_ver: "release-v5.1"
example: "serverless_mqtt" # serverless_mqtt is not supported due to esp-peer
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
example: ["broker"]
include:
- idf_ver: "latest"
# serverless_mqtt is not supported on >=v6.0 (esp-peer dependency)
- idf_ver: "release-v5.3"
example: "serverless_mqtt"
- idf_ver: "release-v5.4"
example: "serverless_mqtt"
- idf_ver: "release-v5.5"
example: "serverless_mqtt"
warning: "'octal_mode' is deprecated"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
@@ -35,8 +36,6 @@ jobs:
with:
submodules: recursive
- name: Build with IDF-${{ matrix.idf_ver }}
env:
EXPECTED_WARNING: ${{ matrix.warning }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
@@ -64,7 +63,7 @@ jobs:
needs: build_mosq
strategy:
matrix:
idf_ver: ["latest", "release-v5.3"]
idf_ver: ["release-v5.4", "release-v5.5", "release-v6.0", "latest"]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
@@ -80,6 +79,20 @@ jobs:
- name: Run Test
working-directory: ${{ env.TEST_DIR }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
@@ -123,7 +136,7 @@ jobs:
name: Build IDF tests
strategy:
matrix:
idf_ver: ["latest"]
idf_ver: ["release-v5.5"] # TODO: add release-v6.0/latest with esp-mqtt directly
idf_target: ["esp32"]
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
runs-on: ubuntu-22.04
@@ -168,7 +181,7 @@ jobs:
needs: build_idf_tests_with_mosq
strategy:
matrix:
idf_ver: ["latest"]
idf_ver: ["release-v5.5"]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
@@ -183,6 +196,20 @@ jobs:
- name: Run Test
working-directory: ${{ env.TEST_DIR }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2" --upgrade
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do

View File

@@ -13,9 +13,9 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5"]
idf_target: ["esp32"]
test: [ { app: mqtt-basic, path: "components/esp_mqtt_cxx/examples" }]
test: [ { app: mqtt-basic, path: "components/esp_mqtt_cxx/examples" }, { app: test, path: "components/esp_mqtt_cxx/test/unit" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
@@ -25,8 +25,7 @@ jobs:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
python ./ci/build_apps.py ./${{ matrix.test.path }} --target ${{ matrix.idf_target }} -vv --preserve-all -c

View File

@@ -28,7 +28,7 @@ jobs:
shell: bash
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
pip install idf-build-apps --upgrade
python ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
@@ -87,6 +87,20 @@ jobs:
- name: Run Test
working-directory: ${{ env.TEST_DIR }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -25,9 +25,6 @@ jobs:
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
if [[ "${{ matrix.idf_ver }}" != "latest" ]]; then
export EXPECTED_WARNING="unknown kconfig symbol 'MBEDTLS_SSL_COOKIE_C'"
fi
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
pip install idf-build-apps --upgrade
python ./ci/build_apps.py ./components/mbedtls_cxx/${{ matrix.test.path }} -vv --preserve-all

View File

@@ -65,14 +65,27 @@ jobs:
with:
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
- name: Install Python packages
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -56,7 +56,7 @@ if __name__ == '__main__':
build_dir='build_@t_@w',
config_rules_str=args.rules,
build_log_filename='build_log.txt',
size_json_filename='size.json' if not args.linux else None,
size_json_filename=None,
check_warnings=True,
manifest_files=args.manifests,
default_build_targets=SUPPORTED_TARGETS,

View File

@@ -1 +1,9 @@
DeprecationWarning: pkg_resources is deprecated as an API
Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead.
Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead.
Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead.
Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead.
Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead.
warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'
WARNING: The following Kconfig variables were used in "if" clauses, but not
warning: unknown kconfig symbol 'LIBC_NEWLIB'

View File

@@ -6,3 +6,4 @@ dpkt
pytest
idf_build_apps
netifaces
esptool>=5.1.0

View File

@@ -0,0 +1,51 @@
# Modem Simulator Component
A Wi-Fi modem simulator that extends ESP-AT with PPP server capabilities, turning ESP32 into a fully functional Wi-Fi modem. Perfect for testing AT commands and PPP connections without real hardware dependencies.
## What it does
- Extends ESP-AT firmware with PPP server functionality
- Provides DATA mode for raw IP communication
- Enables existing communication stacks (MQTT, HTTP, custom protocols) to work over Wi-Fi
- Ideal for testing ESP-Modem library and CI reliability
## Quick Start
```bash
cd common_components/modem_sim
./install.sh
source export.sh
idf.py build
```
## Custom Platform/Module
```bash
./install.sh PLATFORM_ESP32S3 WROOM-32
```
## Configuration
The `sdkconfig.defaults` includes:
- Wi-Fi and Bluetooth enabled
- PPP server support
- AT commands for HTTP/MQTT
- 4MB flash configuration
## Project Structure
```
modem_sim/
├── install.sh # Installation script
├── export.sh # Environment setup
├── sdkconfig.defaults # Default configuration
├── pppd_cmd/ # Custom PPP commands
└── modem_sim_esp32/ # Generated ESP-AT build
```
## Use Cases
- Testing ESP-Modem library without real hardware
- Quick Wi-Fi connectivity for existing communication stacks
- CI/CD testing with reliable modem simulation
- Development and debugging of AT command implementations

View File

@@ -75,3 +75,5 @@ CONFIG_AT_HTTP_COMMAND_SUPPORT=y
CONFIG_AT_BLE_COMMAND_SUPPORT=n
CONFIG_AT_BLE_HID_COMMAND_SUPPORT=n
CONFIG_AT_BLUFI_COMMAND_SUPPORT=n
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y

View File

@@ -0,0 +1,63 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32c6"
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_APP_PROJECT_VER_FROM_CONFIG=y
CONFIG_APP_PROJECT_VER="v4.1.0.0-dev"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="module_config/module_esp32c6_default/partitions_at.csv"
CONFIG_PARTITION_TABLE_MD5=n
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_FILE="module_config/module_esp32c6_default/at_customize.csv"
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_OFFSET=0x1e000
CONFIG_ESP_TLS_PSK_VERIFICATION=y
CONFIG_ESP_TLS_INSECURE=y
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
CONFIG_ESP_ERR_TO_NAME_LOOKUP=n
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=1024
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
CONFIG_RTC_CLK_CAL_CYCLES=1024
CONFIG_ESP_PHY_MAC_BB_PD=y
CONFIG_PM_ENABLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_ESP_TASK_WDT_PANIC=y
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
CONFIG_ESP_DEBUG_OCDAWARE=n
CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0
CONFIG_FATFS_LFN_HEAP=y
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=n
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
CONFIG_LWIP_MAX_SOCKETS=16
CONFIG_LWIP_SO_LINGER=y
CONFIG_LWIP_SO_RCVBUF=y
CONFIG_LWIP_IP4_REASSEMBLY=y
CONFIG_LWIP_IP6_REASSEMBLY=y
CONFIG_LWIP_IPV6_AUTOCONFIG=y
CONFIG_LWIP_TCP_MAXRTX=6
CONFIG_LWIP_TCP_SYNMAXRTX=3
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_SNTP_MAX_SERVERS=3
CONFIG_LWIP_SNTP_STARTUP_DELAY=n
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n
CONFIG_MBEDTLS_HAVE_TIME_DATE=y
CONFIG_MBEDTLS_DHM_C=y
CONFIG_NEWLIB_NANO_FORMAT=y
CONFIG_VFS_SUPPORT_TERMIOS=n
CONFIG_WL_SECTOR_SIZE_512=y
CONFIG_AT_PROCESS_TASK_STACK_SIZE=6144
CONFIG_AT_MDNS_COMMAND_SUPPORT=n
CONFIG_AT_WPS_COMMAND_SUPPORT=n
CONFIG_AT_SMARTCONFIG_COMMAND_SUPPORT=n
CONFIG_AT_PING_COMMAND_SUPPORT=n
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y

View File

@@ -2,9 +2,10 @@
set -e
# Create directory "modem_sim_esp32", go inside it
# Usage: ./install.sh [platform] [module]
# Usage: ./install.sh [platform] [module] [uart_tx_pin] [uart_rx_pin]
SCRIPT_DIR=$(pwd)
UPDATE_UART_PINS_SCRIPT="$(cd "$(dirname "$0")" && pwd)/update_uart_pins.py"
mkdir -p modem_sim_esp32
cd modem_sim_esp32
@@ -39,6 +40,8 @@ mkdir -p build
# Default values for platform and module
platform="PLATFORM_ESP32"
module="WROOM-32"
uart_tx_pin=""
uart_rx_pin=""
# Override defaults if parameters are provided
if [ ! -z "$1" ]; then
@@ -47,18 +50,49 @@ fi
if [ ! -z "$2" ]; then
module="$2"
fi
if [ ! -z "$3" ]; then
uart_tx_pin="$3"
fi
if [ ! -z "$4" ]; then
uart_rx_pin="$4"
fi
target="${platform##*_}"
target="${target,,}"
# Use provided pins for description when present; otherwise keep defaults
description="4MB, Wi-Fi + BLE, OTA, TX:17 RX:16"
if [ -n "$uart_tx_pin" ] || [ -n "$uart_rx_pin" ]; then
desc_tx=${uart_tx_pin:-17}
desc_rx=${uart_rx_pin:-16}
description="4MB, Wi-Fi + BLE, OTA, TX:${desc_tx} RX:${desc_rx}"
fi
# Create file "build/module_info.json" with content
cat > build/module_info.json << EOF
{
"platform": "$platform",
"module": "$module",
"description": "4MB, Wi-Fi + BLE, OTA, TX:17 RX:16",
"description": "$description",
"silence": 0
}
EOF
cp "$SCRIPT_DIR/sdkconfig.defaults" "module_config/module_esp32_default/sdkconfig.defaults"
# Optionally update UART pins in factory_param_data.csv for the selected module
if [ -n "$uart_tx_pin" ] || [ -n "$uart_rx_pin" ]; then
csv_path="components/customized_partitions/raw_data/factory_param/factory_param_data.csv"
if [ ! -f "$csv_path" ]; then
echo "Warning: $csv_path not found; skipping UART pin update."
else
python3 "$UPDATE_UART_PINS_SCRIPT" "$platform" "$module" "$uart_tx_pin" "$uart_rx_pin" "$csv_path"
echo "Updated UART pins in $csv_path"
fi
fi
# Copy the platform-specific sdkconfig.defaults file if it exists
if [ -f "$SCRIPT_DIR/${target}.sdkconfig.defaults" ]; then
cp "$SCRIPT_DIR/${target}.sdkconfig.defaults" "module_config/module_${target}_default/sdkconfig.defaults"
fi
echo "Installation completed successfully!"
echo "Created modem_sim_esp32 directory with esp-at repository and configuration"

View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import csv
import pathlib
import sys
def update_uart_pins(platform: str, module: str, tx_pin: str, rx_pin: str, csv_path: str) -> None:
path = pathlib.Path(csv_path)
rows = []
found = False
with path.open(newline="") as f:
reader = csv.DictReader(f)
fieldnames = reader.fieldnames
for row in reader:
if row.get("platform") == platform and row.get("module_name") == module:
if tx_pin:
row["uart_tx_pin"] = tx_pin
if rx_pin:
row["uart_rx_pin"] = rx_pin
found = True
rows.append(row)
if not found:
print(f"Warning: no row updated for platform={platform} module={module}")
with path.open("w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
def main() -> int:
if len(sys.argv) != 6:
print("Usage: update_uart_pins.py <platform> <module> <uart_tx_pin> <uart_rx_pin> <csv_path>")
return 1
platform, module, tx_pin, rx_pin, csv_path = sys.argv[1:6]
update_uart_pins(platform, module, tx_pin, rx_pin, csv_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -7,5 +7,6 @@
#include "sys/socket.h"
#include "socketpair.h"
#include "asio_stub.hpp"
#include_next "asio/detail/config.hpp"

View File

@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <signal.h>
extern "C" int pthread_sigmask(int, const sigset_t *, sigset_t *);

View File

@@ -28,7 +28,7 @@ posix_event::posix_event()
}
} // namespace asio::detail
extern "C" int pause (void)
extern "C" int pause(void)
{
while (true) {
::sleep(UINT_MAX);

View File

@@ -9,4 +9,8 @@ dependencies:
override_path: '../console_simple_init'
public: true
espressif/ethernet_init:
version: '>=0.0.7'
matches:
- if: idf_version >=6.0
version: ^1.0.0
- if: idf_version <6.0
version: '==0.3.0'

View File

@@ -9,3 +9,7 @@ dependencies:
version: '>=1.1.0'
override_path: '../console_simple_init'
public: true
espressif/mqtt:
rules:
- if: idf_version >=6.0
version: ^1.0.0

View File

@@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(console): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py console_cmd_ping
tag_format: console_cmd_ping-v$version
version: 1.1.0
version: 1.2.0
version_files:
- idf_component.yml

View File

@@ -1,5 +1,11 @@
# Changelog
## [1.2.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.2.0)
### Features
- Add support for interface argument ([90ddb04e](https://github.com/espressif/esp-protocols/commit/90ddb04e))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.1.0)
### Features

View File

@@ -48,7 +48,7 @@ For more details refer [IDF Component Manager](https://docs.espressif.com/projec
### ping:
```
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] [-I <n>] <host>
send ICMP ECHO_REQUEST to network hosts
-W, --timeout=<t> Time to wait for a response, in seconds
-i, --interval=<t> Wait interval seconds between sending each packet
@@ -56,6 +56,7 @@ ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
-c, --count=<n> Stop after sending count packets
-Q, --tos=<n> Set Type of Service related bits in IP datagrams
-T, --ttl=<n> Set Time to Live related bits in IP datagrams
-I, --interface=<n> Set Interface number 0=no-interface selected, >0 netif number + 1 (1 is usually 'lo0')
<host> Host address
getaddrinfo [-f <AF>] [-F <FLAGS>]... [-p <port>] <hostname>
@@ -121,8 +122,8 @@ getaddrinfo -f AF_INET -F AI_PASSIVE www.example.com
ping www.example.com
```
2. To specify additional options, such as timeout, interval, packet size, etc.:
2. To specify additional options, such as timeout, interval, packet size, interface, etc.:
```
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 www.example.com
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 -I 0 www.example.com
```

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -99,6 +99,7 @@ static struct {
struct arg_int *count;
struct arg_int *tos;
struct arg_int *ttl;
struct arg_int *interface;
struct arg_str *host;
struct arg_end *end;
} ping_args;
@@ -137,6 +138,10 @@ static int do_ping_cmd(int argc, char **argv)
config.ttl = (uint32_t)(ping_args.ttl->ival[0]);
}
if (ping_args.interface->count > 0) {
config.interface = (uint32_t)(ping_args.interface->ival[0]);
}
// parse IP address
struct sockaddr_in6 sock_addr6;
ip_addr_t target_addr = {0};
@@ -155,12 +160,12 @@ static int do_ping_cmd(int argc, char **argv)
}
if (res->ai_family == AF_INET) {
#if CONFIG_LWIP_IPV4
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
struct in_addr addr4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
#endif
} else {
#if CONFIG_LWIP_IPV6
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
struct in6_addr addr6 = ((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
#endif
}
@@ -204,6 +209,7 @@ esp_err_t console_cmd_ping_register(void)
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
ping_args.ttl = arg_int0("T", "ttl", "<n>", "Set Time to Live related bits in IP datagrams");
ping_args.interface = arg_int0("I", "interface", "<n>", "Set Interface number");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {

View File

@@ -1,4 +1,4 @@
version: 1.1.0
version: 1.2.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ping
description: The component provides a console where the 'ping' command can be executed.
dependencies:

View File

@@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(eppp): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py eppp_link
tag_format: eppp-v$version
version: 1.0.1
version: 1.1.4
version_files:
- idf_component.yml

View File

@@ -1,5 +1,47 @@
# Changelog
## [1.1.4](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.4)
### Bug Fixes
- Fixed missing freertos deps ([f1e35977](https://github.com/espressif/esp-protocols/commit/f1e35977))
- Add optional mqtt dependency ([911c2dbe](https://github.com/espressif/esp-protocols/commit/911c2dbe))
## [1.1.3](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.3)
### Bug Fixes
- Fix test dependency issue on driver ([1ace92c2](https://github.com/espressif/esp-protocols/commit/1ace92c2))
- Fix tun netif to (optionally) return errors ([7a6cf0f9](https://github.com/espressif/esp-protocols/commit/7a6cf0f9))
## [1.1.2](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.2)
### Bug Fixes
- Update uart driver deps per IDF > v5.3 ([92e14607](https://github.com/espressif/esp-protocols/commit/92e14607))
## [1.1.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.1)
### Bug Fixes
- Fix getting context for channel API ([94563cdc](https://github.com/espressif/esp-protocols/commit/94563cdc))
- Cover more combinations in build tests ([e0b8de8f](https://github.com/espressif/esp-protocols/commit/e0b8de8f))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.0)
### Features
- Add support for UART flow control ([cd57f1bb](https://github.com/espressif/esp-protocols/commit/cd57f1bb), [#870](https://github.com/espressif/esp-protocols/issues/870))
### Bug Fixes
- Fix SPI transport to allow already init GPIO ISR ([497ee2d6](https://github.com/espressif/esp-protocols/commit/497ee2d6), [#868](https://github.com/espressif/esp-protocols/issues/868))
- Fix stack-overflow in ping task for TUN netif ([b2568a3d](https://github.com/espressif/esp-protocols/commit/b2568a3d), [#867](https://github.com/espressif/esp-protocols/issues/867))
### Updated
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
## [1.0.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.1)
### Bug Fixes

View File

@@ -1,5 +1,5 @@
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
set(driver_deps esp_driver_gpio esp_driver_spi)
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
else()
set(driver_deps driver)
endif()

View File

@@ -17,9 +17,17 @@
#include "esp_check.h"
#include "esp_idf_version.h"
#if defined(CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS)
typedef esp_err_t esp_netif_recv_ret_t;
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr) expr
#else
typedef void esp_netif_recv_ret_t;
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr)
#endif // CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS
static const char *TAG = "eppp_tun_netif";
static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
static esp_netif_recv_ret_t tun_input(void *h, void *buffer, unsigned int len, void *eb)
{
__attribute__((unused)) esp_err_t ret = ESP_OK;
struct netif *netif = h;
@@ -31,11 +39,12 @@ static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
ESP_GOTO_ON_FALSE(pbuf_remove_header(p, SIZEOF_ETH_HDR) == 0, ESP_FAIL, err, TAG, "pbuf_remove_header failed");
memcpy(p->payload, buffer, len);
ESP_GOTO_ON_FALSE(netif->input(p, netif) == ERR_OK, ESP_FAIL, err, TAG, "failed to input packet to lwip");
return;
return ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_OK);
err:
if (p) {
pbuf_free(p);
}
return ESP_NETIF_OPTIONAL_RETURN_CODE(ret);
}
static err_t tun_output(struct netif *netif, struct pbuf *p)
@@ -167,6 +176,9 @@ esp_err_t eppp_check_connection(esp_netif_t *netif)
{
esp_err_t ret = ESP_OK;
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
#if CONFIG_LOG_MAXIMUM_LEVEL > 3
config.task_stack_size += 1024; // Some additional stack needed for verbose logs
#endif
config.count = 100;
ESP_LOGI(TAG, "Checking connection on EPPP interface #%" PRIu32, config.interface);
ip_addr_t target_addr = {0};

View File

@@ -8,6 +8,8 @@
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdio_slave.h"
#include "esp_serial_slave_link/essl_sdio.h"
#include "eppp_sdio.h"

View File

@@ -8,6 +8,8 @@
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdio_slave.h"
#include "eppp_link.h"
#include "eppp_transport.h"

View File

@@ -208,7 +208,9 @@ static esp_err_t init_master(struct eppp_config_spi_s *config, struct eppp_spi *
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err_dev, TAG, "Failed to config interrupt GPIO");
ESP_GOTO_ON_ERROR(gpio_install_isr_service(0), err_dev, TAG, "Failed to install GPIO ISR");
ret = gpio_install_isr_service(0);
ESP_GOTO_ON_FALSE(ret == ESP_OK || ret == ESP_ERR_INVALID_STATE /* In case the GPIO ISR already installed */,
ret, err_dev, TAG, "Failed to install GPIO ISR");
ESP_GOTO_ON_ERROR(gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE), err_dev, TAG, "Failed to set ISR type");
ESP_GOTO_ON_ERROR(gpio_isr_handler_add(config->intr, gpio_isr_handler, h), err_dev, TAG, "Failed to add ISR handler");
return ESP_OK;
@@ -468,9 +470,6 @@ err:
if (h->ready_semaphore) {
vSemaphoreDelete(h->ready_semaphore);
}
if (h->out_queue) {
vQueueDelete(h->out_queue);
}
free(h);
return NULL;
}

View File

@@ -80,12 +80,12 @@ static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *confi
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.flow_ctrl = config->flow_control;
uart_config.source_clk = UART_SCLK_DEFAULT;
ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->rx_buffer_size, 0, config->queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART");
ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params");
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, config->rts_io, config->cts_io), TAG, "Failed to set UART pins");
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout");
return ESP_OK;
}
@@ -157,6 +157,8 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava
esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL);
} else {
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent);
if (h->parent.channel_rx) {
h->parent.channel_rx(netif, channel, in_buf + buf_start + sizeof(struct header), payload_size);
}
@@ -269,6 +271,7 @@ eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config)
ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART");
return &h->parent;
err:
free(h);
return NULL;
}

View File

@@ -5,3 +5,7 @@ dependencies:
override_path: ../../..
console_cmd_ping:
version: '*'
espressif/mqtt:
rules:
- if: idf_version >=6.0
version: ^1.0.0

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_EPPP_LINK_DEVICE_SPI=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
CONFIG_EPPP_LINK_USES_PPP=y

View File

@@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_EPPP_LINK_DEVICE_UART=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y

View File

@@ -1,4 +1 @@
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_LWIP_PPP_DEBUG_ON=y

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s2"
CONFIG_EPPP_LINK_DEVICE_SPI=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32c2"
CONFIG_EPPP_LINK_DEVICE_UART=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_EPPP_LINK_USES_PPP=y

View File

@@ -1,6 +1,3 @@
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n

View File

@@ -1,4 +1,4 @@
version: 1.0.1
version: 1.1.4
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
dependencies:

View File

@@ -34,6 +34,9 @@ extern "C" {
.rx_io = 26, \
.queue_size = 16, \
.rx_buffer_size = 1024, \
.rts_io = -1, \
.cts_io = -1, \
.flow_control = 0, \
}, \
#define EPPP_DEFAULT_SDIO_CONFIG() \
@@ -135,6 +138,9 @@ typedef struct eppp_config_t {
int rx_io;
int queue_size;
int rx_buffer_size;
int rts_io;
int cts_io;
int flow_control;
} uart;
struct eppp_config_sdio_s {

View File

@@ -1,7 +1,15 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "6.0")
set(test_component_dir $ENV{IDF_PATH}/tools/test_apps/components)
else()
set(test_component_dir $ENV{IDF_PATH}/tools/unit-test-app/components)
endif()
set(EXTRA_COMPONENT_DIRS ${test_component_dir})
project(test_app)

View File

@@ -1,4 +1,10 @@
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
else()
set(driver_deps driver)
endif()
idf_component_register(SRCS app_main.c
INCLUDE_DIRS "."
REQUIRES test_utils
PRIV_REQUIRES unity nvs_flash esp_netif driver esp_event)
PRIV_REQUIRES unity nvs_flash esp_netif esp_event ${driver_deps})

View File

@@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(modem): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_modem
tag_format: modem-v$version
version: 1.4.0
version: 2.0.0
version_files:
- idf_component.yml

View File

@@ -1,5 +1,41 @@
# Changelog
## [2.0.0](https://github.com/espressif/esp-protocols/commits/modem-v2.0.0)
### Breaking changes
- inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp) ([Use generated AT command definitions for IDE navigation](https://github.com/espressif/esp-protocols/commit/e2fa1110))
### Features
- Add support for multiple connection in AT based example ([2826287d](https://github.com/espressif/esp-protocols/commit/2826287d))
- Add enhanced URC observer API ([4889dd6f](https://github.com/espressif/esp-protocols/commit/4889dd6f))
- Support esp-modem use without PPP ([858f8570](https://github.com/espressif/esp-protocols/commit/858f8570), [#851](https://github.com/espressif/esp-protocols/issues/851))
- Modem simulator based on esp-at ([e5787e3d](https://github.com/espressif/esp-protocols/commit/e5787e3d))
### Bug Fixes
- Update tests and examples to use modem-v2.0 ([4aa0e4ba](https://github.com/espressif/esp-protocols/commit/4aa0e4ba))
- Replace MQTT client with simple ping command ([0ccaf2c0](https://github.com/espressif/esp-protocols/commit/0ccaf2c0))
- Replace MQTT client with simple ping command ([9b2b1f68](https://github.com/espressif/esp-protocols/commit/9b2b1f68))
- Update example to use optional mqtt deps ([3141d6ca](https://github.com/espressif/esp-protocols/commit/3141d6ca))
- Minor fixed in the test code ([e772ce67](https://github.com/espressif/esp-protocols/commit/e772ce67))
- Add missing set_echo() C wrapper ([d1e67080](https://github.com/espressif/esp-protocols/commit/d1e67080), [#926](https://github.com/espressif/esp-protocols/issues/926))
- Fix modem console dependencies ([453be4cd](https://github.com/espressif/esp-protocols/commit/453be4cd))
- Address build issues ([018ba58e](https://github.com/espressif/esp-protocols/commit/018ba58e))
- Fix driver dependency issue on v6.0 ([67c682d9](https://github.com/espressif/esp-protocols/commit/67c682d9))
- Fix CI build issues with IDFv6.0 ([15140e04](https://github.com/espressif/esp-protocols/commit/15140e04))
- Add support for ESP-AT based tcp-client example ([14d3cb6b](https://github.com/espressif/esp-protocols/commit/14d3cb6b))
- Use idf-build-apps for building target tests ([e9d9b3a8](https://github.com/espressif/esp-protocols/commit/e9d9b3a8))
- Make MQTT public broker endpoint configurable ([6d541194](https://github.com/espressif/esp-protocols/commit/6d541194))
- Fix URC handling in DTE data callback ([93029946](https://github.com/espressif/esp-protocols/commit/93029946))
- Use another public broker for examples and tests ([fac2edbe](https://github.com/espressif/esp-protocols/commit/fac2edbe))
- Fix incompatible iterator in std::search() in new gcc ([ed0f6334](https://github.com/espressif/esp-protocols/commit/ed0f6334))
- Fix autodetect to support ACFC mode in PPP frames ([8b328a69](https://github.com/espressif/esp-protocols/commit/8b328a69), [#801](https://github.com/espressif/esp-protocols/issues/801))
- Fix get_network_registration_state() to accept two params ([5f54d907](https://github.com/espressif/esp-protocols/commit/5f54d907), [#826](https://github.com/espressif/esp-protocols/issues/826))
- Consume buffer after handled URC ([6eceb28f](https://github.com/espressif/esp-protocols/commit/6eceb28f))
- Use generated AT command definitions for IDE navigation ([e2fa1110](https://github.com/espressif/esp-protocols/commit/e2fa1110), !BREAKING)
## [1.4.0](https://github.com/espressif/esp-protocols/commits/modem-v1.4.0)
### Features

View File

@@ -11,7 +11,12 @@ else()
src/esp_modem_uart.cpp
src/esp_modem_term_uart.cpp
src/esp_modem_netif.cpp)
set(dependencies driver esp_event esp_netif)
set(dependencies esp_event esp_netif)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
list(APPEND dependencies esp_driver_uart)
else()
list(APPEND dependencies driver)
endif()
endif()

View File

@@ -103,4 +103,14 @@ menu "esp-modem"
Set this to 'y' if you're making changes to the actual sources of
the AT command definitions (typically in esp_modem_command_declare.inc)
config ESP_MODEM_USE_PPP_MODE
bool "Use PPP mode"
default y
select LWIP_PPP_SUPPORT
help
If enabled, the library can use PPP netif from lwip.
This is the default and most common setting.
But it's possible to disable it and use only AT commands,
in this case it's not required to enable LWIP_PPP_SUPPORT.
endmenu

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -100,10 +100,10 @@ void wifi_init_softap(void)
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
@@ -120,7 +120,7 @@ void wifi_init_softap(void)
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",

View File

@@ -1,4 +1,4 @@
dependencies:
espressif/esp_modem:
version: "^1.0.1"
version: "^2"
override_path: "../../../"

View File

@@ -198,7 +198,7 @@ extern "C" bool modem_stop_network()
extern "C" void modem_deinit_network()
{
free(dce);
delete dce;
dce = nullptr;
}

View File

@@ -1,4 +1,4 @@
dependencies:
espressif/esp_modem:
version: "^1.0.1"
version: "^2"
override_path: "../../../"

View File

@@ -9,5 +9,4 @@ idf_component_register(SRCS "modem_console_main.cpp"
"${command_dir}/my_module_dce.cpp"
"httpget_handle.c"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash
INCLUDE_DIRS "." "${command_dir}")

View File

@@ -63,13 +63,12 @@ void ConsoleCommand::RegisterCommand(const char *command, const char *help, cons
arg_type end = { .end = arg_end(1) };
arg_table.emplace_back(end);
const esp_console_cmd_t command_def = {
.command = command,
.help = help,
.hint = nullptr,
.func = command_func_pts[last_command],
.argtable = &arg_table[0]
};
esp_console_cmd_t command_def = { };
command_def.command = command;
command_def.help = help;
command_def.hint = nullptr;
command_def.func = command_func_pts[last_command];
command_def.argtable = &arg_table[0];
ESP_ERROR_CHECK(esp_console_cmd_register(&command_def));
last_command++;
console_commands.emplace_back(this);

View File

@@ -3,7 +3,7 @@ dependencies:
## Required IDF version
idf: ">=4.1.0"
espressif/esp_modem:
version: "^1.0.0"
version: "^2"
override_path: "../../../"
espressif/esp_modem_usb_dte:
version: "^1.2.0"

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -21,6 +21,7 @@
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "esp_idf_version.h"
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
#include "esp_modem_usb_config.h"
#include "cxx_include/esp_modem_usb_api.hpp"
@@ -29,6 +30,12 @@
#include "console_helper.hpp"
#include "my_module_dce.hpp"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
#define GET_WAKEUP_CAUSE() esp_sleep_get_wakeup_causes()
#else
#define GET_WAKEUP_CAUSE() esp_sleep_get_wakeup_cause()
#endif
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
#elif defined(CONFIG_EXAMPLE_FLOW_CONTROL_SW)
@@ -90,10 +97,21 @@ void wakeup_modem(void)
}
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
command_result handle_urc(uint8_t *data, size_t len)
esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo &info)
{
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
return command_result::TIMEOUT;
// Log buffer information for debugging
ESP_LOGI(TAG, "URC Buffer Info: total_size=%zu, processed_offset=%zu, new_data_size=%zu, command_active=%s",
info.buffer_total_size, info.processed_offset, info.new_data_size,
info.is_command_active ? "true" : "false");
// Log the new data content
if (info.new_data_size > 0) {
ESP_LOG_BUFFER_HEXDUMP("on_read", info.new_data_start, info.new_data_size, ESP_LOG_INFO);
}
// For console example, we just log and don't consume anything
// This allows the data to be processed by command handlers
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
}
#endif
@@ -208,7 +226,7 @@ extern "C" void app_main(void)
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
switch (esp_sleep_get_wakeup_cause()) {
switch (GET_WAKEUP_CAUSE()) {
case ESP_SLEEP_WAKEUP_TIMER:
if (esp_modem::modem_mode::CMUX_MODE == mode_rtc) {
ESP_LOGI(TAG, "Deep sleep reset\n");
@@ -374,14 +392,14 @@ extern "C" void app_main(void)
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
});
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
const ConsoleCommand HandleURC("urc", "toggle enhanced urc handling", no_args, [&](ConsoleCommand * c) {
static int cnt = 0;
if (++cnt % 2) {
ESP_LOGI(TAG, "Adding URC handler");
dce->set_urc(handle_urc);
ESP_LOGI(TAG, "Adding enhanced URC handler");
dce->set_enhanced_urc(handle_enhanced_urc);
} else {
ESP_LOGI(TAG, "URC removed");
dce->set_urc(nullptr);
ESP_LOGI(TAG, "Enhanced URC removed");
dce->set_enhanced_urc(nullptr);
}
return 0;
});
@@ -428,7 +446,7 @@ extern "C" void app_main(void)
const ConsoleCommand SetDeepSleep("set_deep_sleep", "Put esp32 to deep sleep", &deep_sleep_args, sizeof(deep_sleep_args), [&](ConsoleCommand * c) {
int tout = c->get_int_of(&DeepSleepArgs::timeout);
ESP_LOGI(TAG, "Entering deep sleep for %d sec", tout);
ESP_LOGI(TAG, "Wakeup Cause: %d ", esp_sleep_get_wakeup_cause());
ESP_LOGI(TAG, "Wakeup Cause: %d ", GET_WAKEUP_CAUSE());
esp_deep_sleep(tout * 1000000);
return 0;
});

View File

@@ -1,4 +1,4 @@
dependencies:
espressif/esp_modem:
version: "^1.0.1"
version: "^2"
override_path: "../../../"

View File

@@ -22,3 +22,18 @@ To enable this mode, please set `EXAMPLE_CUSTOM_TCP_TRANSPORT=y`
This configuration could be used with any network library, which is connecting to a localhost endpoint instead of remote one. This example creates a localhost listener which basically mimics the remote endpoint by forwarding the traffic between the library and the TCP/socket layer of the modem (which is already secure if the TLS is used in the network library)
![with localhost listener](at_client_localhost.png)
### Multi-connection support
This example supports opening multiple TCP connections concurrently when the modem firmware allows it.
- ESP-AT: Multi-connection mode is enabled via `AT+CIPMUX=1`. The example assigns a unique link ID per DCE instance and includes the link ID in `CIPSTART/CIPSEND/CIPRECVDATA` commands.
- BG96/SIM7600: The example uses module-specific multi-connection syntax (for example `QIOPEN/CIPOPEN` with a connection ID) and tracks link IDs internally.
How it works:
- The `sock_dce` layer creates multiple DCE instances over a shared DTE. A lightweight mutex coordinates access to the UART so only one DCE issues AT commands at a time.
- Asynchronous URCs (for example `+IPD`, `+QIURC`, `+CIPRXGET: 1,<cid>`) wake the corresponding DCE which then performs receive operations for its link.
Usage:
- `app_main` starts two DCE tasks to demonstrate concurrent connections. Adjust the number of DCE instances as needed.
- For ESP-AT, ensure your firmware supports `CIPMUX=1` and passive receive (`CIPRECVTYPE`).

View File

@@ -1,7 +1,11 @@
set(module_dir "generic_module")
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
set(device_srcs sock_commands_bg96.cpp)
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
set(device_srcs sock_commands_sim7600.cpp)
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT)
set(device_srcs sock_commands_espat.cpp)
set(module_dir "espat_module")
endif()
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
@@ -14,4 +18,4 @@ idf_component_register(SRCS "modem_client.cpp"
"${command_dir}/sock_dce.cpp"
"tcp_transport_at.cpp"
"${device_srcs}"
INCLUDE_DIRS "." "${command_dir}")
INCLUDE_DIRS "." "${command_dir}" "${module_dir}")

View File

@@ -18,14 +18,44 @@ menu "Example Configuration"
bool "SIM7600"
help
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
config EXAMPLE_MODEM_DEVICE_ESPAT
bool "ESP-AT"
help
ESP-AT firmware for ESP32 modules with WiFi connectivity
endchoice
if EXAMPLE_MODEM_DEVICE_ESPAT
config EXAMPLE_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) to connect to.
config EXAMPLE_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2).
endif
config EXAMPLE_MODEM_APN
string "Set MODEM APN"
default "internet"
help
Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_USE_TLS
bool "Use TLS for MQTT broker"
default n
help
Enable TLS for connection to the MQTT broker.
config EXAMPLE_BROKER_HOST
string "MQTT broker host"
default "test.mosquitto.org"
help
Hostname or IP address of the MQTT broker.
menu "UART Configuration"
config EXAMPLE_MODEM_UART_TX_PIN
int "TXD Pin Number"

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <algorithm>
#include <charconv>
#include <sys/socket.h>
#include "esp_vfs.h"
@@ -14,6 +15,29 @@
namespace sock_dce {
constexpr auto const *TAG = "sock_dce";
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
// Definition of the static member variables
std::vector<DCE *> DCE::dce_list{};
bool DCE::network_init = false;
int Responder::s_link_id = 0;
SemaphoreHandle_t Responder::s_dte_mutex{};
// Constructor - add this DCE instance to the static list
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
: Module(std::move(dte_arg), config)
{
dce_list.push_back(this);
}
// Destructor - remove this DCE instance from the static list
DCE::~DCE()
{
auto it = std::find(dce_list.begin(), dce_list.end(), this);
if (it != dce_list.end()) {
dce_list.erase(it);
}
}
bool DCE::perform_sock()
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
void DCE::perform_at(uint8_t *data, size_t len)
{
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
if (state != status::RECEIVING) {
std::string_view resp_sv((char *)data, len);
at.check_urc(state, resp_sv);
if (state == status::IDLE) {
return;
}
}
// Trace incoming AT bytes when handling a response; use DEBUG level
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
switch (at.process_data(state, data, len)) {
case Responder::ret::OK:
// Release DTE access for this link after processing data
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::FAILED;
signal.set(IDLE);
return;
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
std::string_view response((char *)data, len);
switch (at.check_async_replies(state, response)) {
case Responder::ret::OK:
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::FAILED;
signal.set(IDLE);
return;
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
if (!signal.wait(IDLE, 1000)) {
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
close_sock();
return false;
}
// Take DTE mutex before issuing receive on this link
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
state = status::RECEIVING;
at.start_receiving(at.get_buf_len());
return true;
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
bool DCE::sock_to_at()
{
ESP_LOGD(TAG, "socket read: data available");
if (!signal.wait(IDLE, 1000)) {
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
close_sock();
return false;
}
// Take DTE mutex before issuing send on this link
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
state = status::SENDING;
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
if (len < 0) {
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
}
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
ESP_LOGD(TAG, "Socket created");
struct sockaddr_in addr = { };
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return;
}
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
bool DCE::connect(std::string host, int port)
{
dte->on_read(nullptr);
tcp_close();
dte->on_read([this](uint8_t *data, size_t len) {
this->perform_at(data, len);
return esp_modem::command_result::TIMEOUT;
});
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
// Take DTE mutex before starting connect for this link
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
if (!at.start_connecting(host, port)) {
ESP_LOGE(TAG, "Unable to start connecting");
dte->on_read(nullptr);
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
bool DCE::init()
{
if (network_init) {
return true;
}
network_init = true;
Responder::s_dte_mutex = xSemaphoreCreateBinary();
xSemaphoreGive(at.s_dte_mutex);
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
esp_vfs_eventfd_register(&config);
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
dte->on_read(nullptr);
const int retries = 5;
int i = 0;
@@ -287,6 +339,10 @@ bool DCE::init()
esp_modem::Task::Delay(5000);
}
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
dte->on_read([](uint8_t *data, size_t len) {
read_callback(data, len);
return esp_modem::command_result::TIMEOUT;
});
return true;
}

View File

@@ -8,6 +8,7 @@
#include "cxx_include/esp_modem_api.hpp"
#include <cxx_include/esp_modem_dce_factory.hpp>
#include "sock_commands.hpp"
#include "sock_module.hpp"
#include <sys/socket.h>
#pragma once
@@ -33,6 +34,7 @@ public:
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
ret process_data(status state, uint8_t *data, size_t len);
ret check_async_replies(status state, std::string_view &response);
ret check_urc(status state, std::string_view &response);
void start_sending(size_t len);
void start_receiving(size_t len);
@@ -62,13 +64,19 @@ public:
return total_len;
}
// Unique link identifier used to target multi-connection AT commands
int link_id{s_link_id++};
// Shared mutex guarding DTE access across concurrent DCE instances
static SemaphoreHandle_t s_dte_mutex;
private:
static int s_link_id;
static constexpr size_t buffer_size = 512;
bool on_read(char *data, size_t len)
{
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
::send(sock, data, len, 0);
printf("sending %d\n", len);
#else
::memcpy(&buffer[actual_read], data, len);
actual_read += len;
@@ -97,9 +105,11 @@ private:
std::shared_ptr<esp_modem::DTE> &dte;
};
class DCE : public ::esp_modem::GenericModule {
using esp_modem::GenericModule::GenericModule;
class DCE : public Module {
using Module::Module;
public:
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
~DCE();
/**
* @brief Opens network in AT command mode
@@ -162,6 +172,10 @@ public:
return 0;
}
at.clear_offsets();
// Take DTE mutex before issuing receive on this link
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
state = status::RECEIVING;
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
@@ -183,6 +197,10 @@ public:
if (!wait_to_idle(timeout_ms)) {
return -1;
}
// Take DTE mutex before issuing send on this link
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
state = status::SENDING;
memcpy(at.get_buf(), buffer, len_to_send);
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
@@ -223,6 +241,14 @@ public:
}
return -1;
}
static std::vector<DCE *> dce_list;
static bool network_init;
static void read_callback(uint8_t *data, size_t len)
{
for (auto dce : dce_list) {
dce->perform_at(data, len);
}
}
private:
esp_modem::SignalGroup signal;
void close_sock();

View File

@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#pragma once
namespace sock_dce {
class Module: public esp_modem::GenericModule {
using esp_modem::GenericModule::GenericModule;
public:
esp_modem::command_result sync() override;
esp_modem::command_result set_echo(bool on) override;
esp_modem::command_result set_pdp_context(esp_modem::PdpContext &pdp) override;
};
}

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <algorithm>
#include <charconv>
#include <sys/socket.h>
#include "esp_vfs.h"
@@ -14,6 +15,29 @@
namespace sock_dce {
constexpr auto const *TAG = "sock_dce";
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
// Definition of the static member variables
std::vector<DCE*> DCE::dce_list{};
bool DCE::network_init = false;
int Responder::s_link_id = 0;
SemaphoreHandle_t Responder::s_dte_mutex{};
// Constructor - add this DCE instance to the static list
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
: Module(std::move(dte_arg), config)
{
dce_list.push_back(this);
}
// Destructor - remove this DCE instance from the static list
DCE::~DCE()
{
auto it = std::find(dce_list.begin(), dce_list.end(), this);
if (it != dce_list.end()) {
dce_list.erase(it);
}
}
bool DCE::perform_sock()
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
void DCE::perform_at(uint8_t *data, size_t len)
{
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
if (state != status::RECEIVING) {
std::string_view resp_sv((char *)data, len);
at.check_urc(state, resp_sv);
if (state == status::IDLE) {
return;
}
}
// Trace incoming AT bytes when handling a response; use DEBUG level
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
switch (at.process_data(state, data, len)) {
case Responder::ret::OK:
// Release DTE access for this link after processing data
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::FAILED;
signal.set(IDLE);
return;
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
std::string_view response((char *)data, len);
switch (at.check_async_replies(state, response)) {
case Responder::ret::OK:
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
xSemaphoreGive(at.s_dte_mutex);
state = status::FAILED;
signal.set(IDLE);
return;
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
if (!signal.wait(IDLE, 1000)) {
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
close_sock();
return false;
}
// Take DTE mutex before issuing receive on this link
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
state = status::RECEIVING;
at.start_receiving(at.get_buf_len());
return true;
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
bool DCE::sock_to_at()
{
ESP_LOGD(TAG, "socket read: data available");
if (!signal.wait(IDLE, 1000)) {
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
close_sock();
return false;
}
// Take DTE mutex before issuing send on this link
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
state = status::SENDING;
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
if (len < 0) {
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
}
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
ESP_LOGD(TAG, "Socket created");
struct sockaddr_in addr = { };
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return;
}
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
bool DCE::connect(std::string host, int port)
{
dte->on_read(nullptr);
tcp_close();
dte->on_read([this](uint8_t *data, size_t len) {
this->perform_at(data, len);
return esp_modem::command_result::TIMEOUT;
});
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
// Take DTE mutex before starting connect for this link
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
if (!at.start_connecting(host, port)) {
ESP_LOGE(TAG, "Unable to start connecting");
dte->on_read(nullptr);
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
bool DCE::init()
{
if (network_init) {
return true;
}
network_init = true;
Responder::s_dte_mutex = xSemaphoreCreateBinary();
xSemaphoreGive(at.s_dte_mutex);
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
esp_vfs_eventfd_register(&config);
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
dte->on_read(nullptr);
const int retries = 5;
int i = 0;
@@ -287,6 +339,10 @@ bool DCE::init()
esp_modem::Task::Delay(5000);
}
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
dte->on_read([](uint8_t *data, size_t len) {
read_callback(data, len);
return esp_modem::command_result::TIMEOUT;
});
return true;
}

View File

@@ -8,6 +8,7 @@
#include "cxx_include/esp_modem_api.hpp"
#include <cxx_include/esp_modem_dce_factory.hpp>
#include "sock_commands.hpp"
#include "sock_module.hpp"
#include <sys/socket.h>
#pragma once
@@ -33,6 +34,7 @@ public:
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
ret process_data(status state, uint8_t *data, size_t len);
ret check_async_replies(status state, std::string_view &response);
ret check_urc(status state, std::string_view &response);
void start_sending(size_t len);
void start_receiving(size_t len);
@@ -62,13 +64,19 @@ public:
return total_len;
}
// Unique link identifier used to target multi-connection AT commands
int link_id{s_link_id++};
// Shared mutex guarding DTE access across concurrent DCE instances
static SemaphoreHandle_t s_dte_mutex;
private:
static int s_link_id;
static constexpr size_t buffer_size = 512;
bool on_read(char *data, size_t len)
{
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
::send(sock, data, len, 0);
printf("sending %d\n", len);
#else
::memcpy(&buffer[actual_read], data, len);
actual_read += len;
@@ -97,9 +105,11 @@ private:
std::shared_ptr<esp_modem::DTE> &dte;
};
class DCE : public ::esp_modem::GenericModule {
using esp_modem::GenericModule::GenericModule;
class DCE : public Module {
using Module::Module;
public:
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
~DCE();
// --- ESP-MODEM command module starts here ---
#include "esp_modem_command_declare_helper.inc"
@@ -140,6 +150,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
return 0;
}
at.clear_offsets();
// Take DTE mutex before issuing receive on this link
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
state = status::RECEIVING;
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
@@ -162,6 +176,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
if (!wait_to_idle(timeout_ms)) {
return -1;
}
// Take DTE mutex before issuing send on this link
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
state = status::SENDING;
memcpy(at.get_buf(), buffer, len_to_send);
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
@@ -203,6 +221,14 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
}
return -1;
}
static std::vector<DCE*> dce_list;
static bool network_init;
static void read_callback(uint8_t *data, size_t len)
{
for (auto dce : dce_list) {
dce->perform_at(data, len);
}
}
private:
esp_modem::SignalGroup signal;

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#pragma once
namespace sock_dce {
using Module = esp_modem::GenericModule;
}

View File

@@ -1,7 +1,11 @@
dependencies:
espressif/esp_modem:
version: "^1.0.1"
override_path: "../../../"
version: ^2
override_path: ../../../
espressif/mbedtls_cxx:
version: "*"
override_path: "../../../../mbedtls_cxx"
version: '*'
override_path: ../../../../mbedtls_cxx
espressif/mqtt:
rules:
- if: idf_version >=6.0
version: ^1.0.0

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -22,15 +22,26 @@
#include "esp_log.h"
#include "tcp_transport_mbedtls.h"
#include "tcp_transport_at.h"
#include "sdkconfig.h"
#define BROKER_URL "mqtt.eclipseprojects.io"
#define USE_TLS CONFIG_EXAMPLE_USE_TLS
#define BROKER_HOST CONFIG_EXAMPLE_BROKER_HOST
#if USE_TLS
#define BROKER_SCHEME "mqtts"
#define BROKER_PORT 8883
#else
#define BROKER_SCHEME "mqtt"
#define BROKER_PORT 1883
#endif
#define BROKER_URL BROKER_SCHEME "://" BROKER_HOST
static const char *TAG = "modem_client";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int GOT_DATA_BIT = BIT2;
static const int DCE0_DONE = BIT3;
static const int DCE1_DONE = BIT4;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
@@ -73,13 +84,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
}
}
static void perform(void* ctx);
extern "C" void app_main(void)
{
/* Init and register system/core components */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Default to INFO; individual modules use DEBUG for verbose tracing
esp_log_level_set("*", ESP_LOG_INFO);
event_group = xEventGroupCreate();
@@ -104,49 +117,78 @@ extern "C" void app_main(void)
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
/* create the DCE and initialize network manually (using AT commands) */
auto dce = sock_dce::create(&dce_config, std::move(dte));
auto dce = sock_dce::create(&dce_config, dte);
if (!dce->init()) {
ESP_LOGE(TAG, "Failed to setup network");
return;
}
esp_mqtt_client_config_t mqtt_config = {};
mqtt_config.broker.address.port = BROKER_PORT;
mqtt_config.session.message_retransmit_timeout = 10000;
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
dce->start_listening(BROKER_PORT);
#else
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
esp_transport_handle_t at = esp_transport_at_init(dce.get());
esp_transport_handle_t ssl = esp_transport_tls_init(at);
xTaskCreate(perform, "perform", 4096, dce.get(), 4, nullptr);
mqtt_config.network.transport = ssl;
/* create another DCE to serve a new connection */
auto dce1 = sock_dce::create(&dce_config, dte);
if (!dce1->init()) {
ESP_LOGE(TAG, "Failed to setup network");
return;
}
xTaskCreate(perform, "perform", 4096, dce1.get(), 4, nullptr);
xEventGroupWaitBits(event_group, DCE0_DONE | DCE1_DONE, pdFALSE, pdTRUE, portMAX_DELAY);
#ifdef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
// this example does never keeps both DCEs running and in tcp-transport option
// we don't need a task to run so we exit main and keep DCE's "running"
vTaskDelay(portMAX_DELAY);
#endif
}
static void perform(void* ctx)
{
auto dce = static_cast<sock_dce::DCE*>(ctx);
char mqtt_client_id[] = "MQTT_CLIENT_0";
static int counter = 0;
const int id = counter++;
mqtt_client_id[sizeof(mqtt_client_id) - 2] += id; // assumes a different client id per each thread
esp_mqtt_client_config_t mqtt_config = {};
mqtt_config.session.message_retransmit_timeout = 10000;
mqtt_config.credentials.client_id = mqtt_client_id;
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
mqtt_config.broker.address.port = BROKER_PORT + id;
mqtt_config.broker.address.uri = BROKER_SCHEME "://127.0.0.1";
dce->start_listening(BROKER_PORT + id);
#else
mqtt_config.broker.address.port = BROKER_PORT;
mqtt_config.broker.address.uri = BROKER_URL;
esp_transport_handle_t at = esp_transport_at_init(dce);
#if USE_TLS
esp_transport_handle_t ssl = esp_transport_tls_init(at);
mqtt_config.network.transport = ssl;
#else
mqtt_config.network.transport = at;
#endif // USE_TLS
#endif // CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, nullptr);
esp_mqtt_client_start(mqtt_client);
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
ESP_LOGE(TAG, "Failed to start DCE");
return;
}
while (1) {
while (true) {
while (dce->perform_sock()) {
ESP_LOGV(TAG, "...performing");
}
ESP_LOGE(TAG, "Loop exit.. retrying");
ESP_LOGD(TAG, "Loop exit.. retrying");
// handle disconnections errors
if (!dce->init()) {
ESP_LOGE(TAG, "Failed to reinit network");
return;
}
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
ESP_LOGI(TAG, "Network reinitialized, retrying");
}
}
#else
vTaskDelay(portMAX_DELAY);
#endif
xEventGroupSetBits(event_group, id ? DCE0_DONE : DCE1_DONE);
vTaskDelete(nullptr);
}

View File

@@ -109,17 +109,17 @@ void Responder::start_sending(size_t len)
{
data_to_send = len;
send_stat = 0;
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
send_cmd("AT+QISEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
}
void Responder::start_receiving(size_t len)
{
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
send_cmd("AT+QIRD=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
}
bool Responder::start_connecting(std::string host, int port)
{
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
send_cmd(std::string("AT+QIOPEN=1,") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
return true;
}
@@ -130,16 +130,24 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
auto *recv_data = (char *)data;
if (data_to_recv == 0) {
const std::string_view head = "+QIRD: ";
again:
const std::string_view recv_data_view = std::string_view(recv_data, len);
auto head_pos_found = recv_data_view.find(head);
if (head_pos_found == std::string_view::npos) {
return ret::FAIL;
return ret::IN_PROGRESS;
}
auto *head_pos = recv_data + head_pos_found;
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
if (next_nl == nullptr) {
if (head_pos + head.size() + 1 < recv_data + len) {
// might be that we misinterpreted the URC +QIRD: <>,<>,<> (notification) with the +QIRD: <> (read data)
// so we try to find the next +QIRD:
recv_data = head_pos + head.size() + 1;
goto again;
}
ESP_LOGD(TAG, "no new line found");
return ret::FAIL;
}
@@ -151,7 +159,9 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
if (actual_len == 0) {
ESP_LOGD(TAG, "no data received");
return ret::FAIL;
data_to_recv = 0;
// return OK here, as BG96 would keep unacked data and notifies us with +QIRD: 0
return ret::OK;
}
if (actual_len > buffer_size) {
@@ -182,6 +192,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
if (last_pos == nullptr || last_pos[1] != 'K') {
data_to_recv = 0;
ESP_LOGD(TAG, "no OK after data");
return ret::FAIL;
}
}
@@ -222,7 +233,7 @@ Responder::ret Responder::send(std::string_view response)
{
if (send_stat == 3) {
if (response.find("SEND OK") != std::string::npos) {
send_cmd("AT+QISEND=0,0\r");
send_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
send_stat++;
return ret::IN_PROGRESS;
} else if (response.find("SEND FAIL") != std::string::npos) {
@@ -267,7 +278,7 @@ Responder::ret Responder::send(std::string_view response)
if (ack < total) {
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
if (total - ack > 64) {
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
ESP_LOGD(TAG, "Need a pause: missing %d bytes acked", (total - ack));
return ret::NEED_MORE_TIME;
}
}
@@ -284,7 +295,8 @@ Responder::ret Responder::send(std::string_view response)
Responder::ret Responder::connect(std::string_view response)
{
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
std::string open_response = "+QIOPEN: " + std::to_string(link_id) + ",0";
if (response.find(open_response) != std::string::npos) {
ESP_LOGI(TAG, "Connected!");
return ret::OK;
}
@@ -295,10 +307,9 @@ Responder::ret Responder::connect(std::string_view response)
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
Responder::ret Responder::check_urc(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
if (response.find(std::string("+QIURC: \"recv\",") + std::to_string(link_id)) != std::string::npos) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Got data on modem!");
@@ -309,6 +320,9 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
response = response.substr(head_pos + head.size());
int next_cr = response.find('\r');
if (next_cr != std::string::npos) {
if (next_cr < 2) {
return ret::IN_PROGRESS;
}
response = response.substr(next_cr - 2, next_cr);
if (response.find(",0") != std::string::npos) {
ESP_LOGV(TAG, "Receiving done");
@@ -318,12 +332,21 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
ESP_LOGD(TAG, "Got data on modem!");
}
}
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
} else if (response.find(std::string("+QIURC: \"closed\",") + std::to_string(link_id)) != std::string::npos) {
ESP_LOGE(TAG, "Connection closed");
return ret::FAIL;
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (state == status::SENDING) {
return send(response);
} else if (state == status::CONNECTING) {
}
if (state == status::CONNECTING) {
return connect(response);
}
return ret::IN_PROGRESS;
@@ -342,7 +365,7 @@ Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
status Responder::pending()
{
send_cmd("AT+QISEND=0,0\r");
send_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
return status::SENDING;
}

View File

@@ -0,0 +1,363 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <charconv>
#include <cstring>
#include "sock_commands.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "sock_dce.hpp"
#include "sock_module.hpp"
static const char *TAG = "sock_commands_espat";
namespace sock_dce {
using namespace esp_modem;
command_result Module::sync()
{
return dce_commands::generic_command_common(dte.get(), "AT\r\n");
}
command_result Module::set_echo(bool on)
{
return dce_commands::generic_command_common(dte.get(), "ATE0\r\n");
}
command_result Module::set_pdp_context(PdpContext &pdp)
{
return command_result::OK;
}
}
namespace sock_commands {
using namespace esp_modem;
command_result net_open(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__);
// Set WiFi mode to station
auto ret = dce_commands::generic_command(t, "AT+CWMODE=1\r\n", "OK", "ERROR", 5000);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "Failed to set WiFi mode");
return ret;
}
// Connect to WiFi network
std::string wifi_cmd = "AT+CWJAP=\"" CONFIG_EXAMPLE_WIFI_SSID "\",\"" CONFIG_EXAMPLE_WIFI_PASSWORD "\"\r\n";
ret = dce_commands::generic_command(t, wifi_cmd, "OK", "ERROR", 15000);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "Failed to connect to WiFi");
return ret;
}
ESP_LOGI(TAG, "WiFi connected successfully");
// Enable multiple connections mode
ret = dce_commands::generic_command(t, "AT+CIPMUX=1\r\n", "OK", "ERROR", 1000);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "Failed to enable multiple connections mode");
return ret;
}
ESP_LOGD(TAG, "Multiple connections mode enabled");
// Set passive receive mode (1) for better control
for (int i = 0; i < 2; i++) {
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(i) + ",1\r\n";
dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
}
return command_result::OK;
}
command_result net_close(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__);
// Disconnect from WiFi
auto ret = dce_commands::generic_command(t, "AT+CWQAP\r\n", "OK", "ERROR", 5000);
if (ret != command_result::OK) {
ESP_LOGW(TAG, "Failed to disconnect WiFi (may already be disconnected)");
}
return command_result::OK;
}
command_result tcp_close(CommandableIf *t)
{
return command_result::OK;
ESP_LOGV(TAG, "%s", __func__);
// Use link ID 0 for closing connection
const int link_id = 0;
std::string close_cmd = "AT+CIPCLOSE=" + std::to_string(link_id) + "\r\n";
// In multiple connections mode, response format is: <link ID>,CLOSED
std::string expected_response = std::to_string(link_id) + ",CLOSED";
return dce_commands::generic_command(t, close_cmd, expected_response, "ERROR", 5000);
}
command_result get_ip(CommandableIf *t, std::string &ip)
{
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = dce_commands::at_raw(t, "AT+CIFSR\r\n", out, "OK", "ERROR", 5000);
if (ret != command_result::OK) {
return ret;
}
// Parse station IP from response
// Expected format: +CIFSR:STAIP,"192.168.1.100"
auto pos = out.find("+CIFSR:STAIP,\"");
if (pos != std::string::npos) {
pos += 14; // Move past "+CIFSR:STAIP,\""
auto end_pos = out.find("\"", pos);
if (end_pos != std::string::npos) {
ip = out.substr(pos, end_pos - pos);
ESP_LOGI(TAG, "Got IP address: %s", ip.c_str());
return command_result::OK;
}
}
ESP_LOGE(TAG, "Failed to parse IP address from response");
return command_result::FAIL;
}
command_result set_rx_mode(CommandableIf *t, int mode)
{
ESP_LOGV(TAG, "%s", __func__);
// For multiple connections mode, set receive mode for link ID 0
const int link_id = 0;
// Active mode (0) sends data automatically, Passive mode (1) notifies about data for reading
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(link_id) + "," + std::to_string(mode) + "\r\n";
return dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
}
} // sock_commands
namespace sock_dce {
void Responder::start_sending(size_t len)
{
data_to_send = len;
send_stat = 0;
// For multiple connections mode, include link ID
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
}
void Responder::start_receiving(size_t len)
{
// For multiple connections mode, include link ID
send_cmd("AT+CIPRECVDATA=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
}
bool Responder::start_connecting(std::string host, int port)
{
// For multiple connections mode, include link ID
std::string cmd = "AT+CIPSTART=" + std::to_string(link_id) + ",\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
send_cmd(cmd);
return true;
}
Responder::ret Responder::recv(uint8_t *data, size_t len)
{
const int MIN_MESSAGE = 6;
size_t actual_len = 0;
auto *recv_data = (char *)data;
if (data_to_recv == 0) {
const std::string_view head = "+CIPRECVDATA:";
const std::string_view recv_data_view(recv_data, len);
const auto head_pos_found = recv_data_view.find(head);
if (head_pos_found == std::string_view::npos) {
return ret::IN_PROGRESS;
}
const auto *head_pos = recv_data + head_pos_found;
// Find the end of the length field
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
if (next_comma == nullptr) {
return ret::FAIL;
}
// Parse the actual length
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "Cannot convert length");
return ret::FAIL;
}
ESP_LOGD(TAG, "Received: actual len=%zu", actual_len);
if (actual_len == 0) {
ESP_LOGD(TAG, "No data received");
return ret::FAIL;
}
if (actual_len > buffer_size) {
ESP_LOGE(TAG, "Data too large: %zu > %zu", actual_len, buffer_size);
return ret::FAIL;
}
// Move to the actual data after the comma
recv_data = next_comma + 1;
auto first_data_len = len - (recv_data - (char *)data);
if (actual_len > first_data_len) {
on_read(recv_data, first_data_len);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
on_read(recv_data, actual_len);
} else if (data_to_recv > len) { // Continue receiving
on_read(recv_data, len);
data_to_recv -= len;
return ret::NEED_MORE_DATA;
} else if (data_to_recv <= len) { // Last chunk
on_read(recv_data, data_to_recv);
actual_len = data_to_recv;
}
// Look for "OK" marker after the data
char *ok_pos = nullptr;
if (actual_len + 1 + 2 /* OK */ <= len) {
ok_pos = (char *)memchr(recv_data + actual_len + 1, 'O', MIN_MESSAGE);
if (ok_pos == nullptr) { // || ok_pos[1] != 'K') {
data_to_recv = 0;
ESP_LOGE(TAG, "Missed 'OK' marker");
return ret::OK;
return ret::FAIL;
}
if (ok_pos + 1 < recv_data + len && ok_pos[1] != 'K') {
// we ignore the condition when receiving 'O' as the last character in the last batch,
// don't wait for the 'K' in the next run, assume the data are valid and let higher layers deal with it.
data_to_recv = 0;
ESP_LOGE(TAG, "Missed 'OK' marker2");
return ret::FAIL;
}
}
if (ok_pos != nullptr && (char *)data + len - ok_pos - 2 > MIN_MESSAGE) {
// check for async replies after the Recv header
std::string_view response((char *)ok_pos + 2 /* OK */, (char *)data + len - ok_pos);
check_urc(status::RECEIVING, response);
}
// Reset and prepare for next receive
data_to_recv = 0;
return ret::OK;
}
Responder::ret Responder::send(uint8_t *data, size_t len)
{
if (send_stat < 3) {
// Look for the '>' prompt
if (memchr(data, '>', len) == NULL) {
if (send_stat++ < 2) {
return ret::NEED_MORE_DATA;
}
ESP_LOGE(TAG, "Missed '>' prompt");
return ret::FAIL;
}
// Send the actual data
auto written = dte->write(&buffer[0], data_to_send);
if (written != data_to_send) {
ESP_LOGE(TAG, "Failed to write data: %d/%zu", written, data_to_send);
return ret::FAIL;
}
data_to_send = 0;
send_stat = 3;
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::send(std::string_view response)
{
if (send_stat == 3) {
if (response.find("SEND OK") != std::string::npos) {
send_stat = 0;
return ret::OK;
} else if (response.find("SEND FAIL") != std::string::npos) {
ESP_LOGE(TAG, "Send failed");
return ret::FAIL;
} else if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Send error");
return ret::FAIL;
}
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::connect(std::string_view response)
{
// In multiple connections mode, response format is: <link ID>,CONNECT
if (response.find(",CONNECT") != std::string::npos || response.find("CONNECT") != std::string::npos) {
ESP_LOGI(TAG, "TCP connected!");
return ret::OK;
}
if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to connect");
return ret::FAIL;
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::check_urc(status state, std::string_view &response)
{
// Handle data notifications - in multiple connections mode, format is +IPD,<link ID>,<len>
std::string expected_urc = "+IPD," + std::to_string(link_id);
if (response.find(expected_urc) != std::string::npos) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Data available notification");
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
{
ESP_LOGD(TAG, "Response: %.*s", static_cast<int>(response.size()), response.data());
// Handle WiFi status messages
if (response.find("WIFI CONNECTED") != std::string::npos) {
ESP_LOGI(TAG, "WiFi connected");
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
ESP_LOGD(TAG, "WiFi disconnected");
}
// Handle TCP status messages (multiple connections format: <link ID>,CONNECT or <link ID>,CLOSED)
if (response.find("CONNECT") != std::string::npos && state == status::CONNECTING) {
return connect(response);
} else if (response.find("CLOSED") != std::string::npos) {
ESP_LOGD(TAG, "TCP connection closed");
return ret::FAIL;
}
if (state == status::SENDING) {
return send(response);
} else if (state == status::CONNECTING) {
return connect(response);
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
{
if (state == status::SENDING) {
return send(data, len);
}
if (state == status::RECEIVING) {
return recv(data, len);
}
return ret::IN_PROGRESS;
}
status Responder::pending()
{
// For ESP-AT, we don't need a pending check like BG96
// Just return current status
return status::SENDING;
}
} // sock_dce

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,13 +18,13 @@ using namespace esp_modem;
command_result net_open(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string response;
auto ret = dce_commands::generic_get_string(term, "AT+NETOPEN?\r", response, 1000);
if (ret != command_result::OK) {
return ret;
}
ESP_LOGV(TAG, "%s", response.data() );
ESP_LOGV(TAG, "%s", response.data());
if (response.find("+NETOPEN: 1") != std::string::npos) {
ESP_LOGD(TAG, "Already there");
ret = command_result::OK;
@@ -42,23 +42,23 @@ command_result net_open(CommandableIf *term)
command_result net_close(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return dce_commands::generic_command(term, "AT+NETCLOSE\r", "+NETCLOSE:", "ERROR", 30000);
}
command_result tcp_open(CommandableIf *term, const std::string &host, int port, int timeout)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
auto ret = dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 50000);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "Setting Rx mode failed!");
return ret;
}
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string ip_open = R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
ret = dce_commands::generic_command(term, ip_open, "+CIPOPEN: 0,0", "ERROR", timeout);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "%s Failed", __func__ );
ESP_LOGE(TAG, "%s Failed", __func__);
return ret;
}
return command_result::OK;
@@ -66,13 +66,13 @@ command_result tcp_open(CommandableIf *term, const std::string &host, int port,
command_result tcp_close(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
return dce_commands::generic_command(term, "AT+CIPCLOSE=0\r", "+CIPCLOSE:", "ERROR", 10000);
}
command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string send = "AT+CIPSEND=0," + std::to_string(len) + "\r";
auto ret = term->command(send, [&](uint8_t *data, size_t len) {
std::string_view response((char *)data, len);
@@ -86,10 +86,10 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
return ret;
}
ret = command_result::TIMEOUT;
ESP_LOGW(TAG, "Before setting...");
ESP_LOGD(TAG, "Before setting...");
term->on_read([&ret](uint8_t *cmd_data, size_t cmd_len) {
std::string_view response((char *)cmd_data, cmd_len);
ESP_LOGW(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
ESP_LOGD(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+CIPSEND:") != std::string::npos) {
ret = command_result::OK;
@@ -98,7 +98,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
}
return ret;
});
ESP_LOGW(TAG, "Before writing...");
ESP_LOGD(TAG, "Before writing...");
auto written = term->write(data, len);
if (written != len) {
ESP_LOGE(TAG, "written %d (%d)...", written, len);
@@ -107,7 +107,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
uint8_t ctrl_z = '\x1A';
term->write(&ctrl_z, 1);
int count = 0;
while (ret == command_result::TIMEOUT && count++ < 1000 ) {
while (ret == command_result::TIMEOUT && count++ < 1000) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
term->on_read(nullptr);
@@ -116,7 +116,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
command_result tcp_recv(CommandableIf *term, uint8_t *data, size_t len, size_t &out_len)
{
ESP_LOGV(TAG, "%s", __func__ );
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = dce_commands::generic_get_string(term, "AT+CIPRXGET=4,0\r", out);
if (ret != command_result::OK) {
@@ -195,17 +195,17 @@ void Responder::start_sending(size_t len)
{
data_to_send = len;
send_stat = 0;
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
}
void Responder::start_receiving(size_t len)
{
send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r");
send_cmd("AT+CIPRXGET=2," + std::to_string(link_id) + "," + std::to_string(len) + "\r");
}
bool Responder::start_connecting(std::string host, int port)
{
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
send_cmd(std::string("AT+CIPOPEN=") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
return true;
}
@@ -215,7 +215,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
size_t actual_len = 0;
auto *recv_data = (char *)data;
if (data_to_recv == 0) {
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
const std::string head = std::string("+CIPRXGET: 2,") + std::to_string(link_id) + ",";
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
if (head_pos == recv_data + len) {
return ret::FAIL;
@@ -329,7 +329,8 @@ Responder::ret Responder::send(std::string_view response)
Responder::ret Responder::connect(std::string_view response)
{
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
std::string open_response = "+CIPOPEN: " + std::to_string(link_id) + ",0";
if (response.find(open_response) != std::string::npos) {
ESP_LOGI(TAG, "Connected!");
return ret::OK;
}
@@ -340,14 +341,22 @@ Responder::ret Responder::connect(std::string_view response)
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
Responder::ret Responder::check_urc(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+CIPRXGET: 1") != std::string::npos) {
// 1. When <mode> is set to 1 and the 2-4 mode will take effect.
// 2. If AT+CIPRXGET=1, it will report +CIPRXGET: 1,<cid>(multi client) when
const std::string expected = std::string("+CIPRXGET: 1,") + std::to_string(link_id);
if (response.find(expected) != std::string::npos) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Got data on modem!");
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (state == status::SENDING) {
return send(response);
} else if (state == status::CONNECTING) {

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT=y

View File

@@ -1,12 +1,14 @@
## IDF Component Manager Manifest File
dependencies:
## Required IDF version
idf: ">=4.1.0"
idf: '>=4.1.0'
espressif/esp_modem:
version: "^1.0.0"
override_path: "../../../"
version: ^2
override_path: ../../../
espressif/esp_modem_usb_dte:
version: "^1.2.0"
version: ^1.2.0
rules:
- if: "idf_version >=4.4"
- if: "target in [esp32s2, esp32s3, esp32p4]"
- if: idf_version >=4.4
- if: target in [esp32s2, esp32s3, esp32p4]
console_cmd_ping:
version: '*'

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -16,8 +16,10 @@
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem_api.h"
#include "esp_console.h"
#include "console_ping.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
@@ -35,7 +37,6 @@ static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int DISCONNECT_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
@@ -64,47 +65,6 @@ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED
#define CHECK_USB_DISCONNECTION(event_group)
#endif
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
xEventGroupSetBits(event_group, GOT_DATA_BIT);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
break;
}
}
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
@@ -160,6 +120,11 @@ void app_main(void)
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
// Initialize console REPL, register ping and start it
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_cmd_ping_register());
ESP_ERROR_CHECK(console_cmd_start());
/* Configure the PPP netif */
esp_err_t err;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
@@ -251,7 +216,7 @@ void app_main(void)
#endif
#if CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT);
if (err != ESP_OK) {
@@ -270,7 +235,7 @@ void app_main(void)
}
#endif // CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
/* Run the modem demo app */
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
@@ -340,15 +305,11 @@ void app_main(void)
}
/* Config MQTT */
esp_mqtt_client_config_t mqtt_config = {
.broker.address.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI,
};
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
int ping_ret_val;
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
#if CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
esp_modem_pause_net(dce, true);
err = esp_modem_get_signal_quality(dce, &rssi, &ber);
if (err != ESP_OK) {
@@ -357,14 +318,15 @@ void app_main(void)
}
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
esp_modem_pause_net(dce, false);
esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
#endif // CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
ESP_LOGI(TAG, "Waiting for MQTT data");
xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (ping_ret_val != 0) {
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
}
CHECK_USB_DISCONNECTION(event_group);
esp_mqtt_client_destroy(mqtt_client);
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
from __future__ import print_function, unicode_literals
@@ -8,16 +8,12 @@ def test_pppos_connect(dut):
steps:
1. initializes connection with SIM800
2. checks we get an IP
3. checks for the MQTT events
3. checks that the ping command works
4. checks that the client cleanly disconnects
"""
# Check the sequence of connecting, publishing, disconnecting
dut.expect('Modem Connect to PPP Server', timeout=90)
# Check for MQTT connection and the data event
dut.expect('MQTT_EVENT_CONNECTED')
dut.expect('MQTT_EVENT_DATA')
dut.expect('TOPIC=/ci/esp-modem/pppos-client')
dut.expect('DATA=esp32-pppos')
dut.expect('Ping command finished with return value: 0', timeout=30)
# Check that we have disconnected
dut.expect('User interrupted event')
# And can use commands again

View File

@@ -1,2 +1,2 @@
idf_component_register(SRCS "simple_cmux_client_main.cpp" "simple_mqtt_client.cpp"
idf_component_register(SRCS "simple_cmux_client_main.cpp"
INCLUDE_DIRS ".")

View File

@@ -1,4 +1,6 @@
dependencies:
espressif/esp_modem:
version: "^1.0.1"
version: "^2"
override_path: "../../../"
console_cmd_ping:
version: '*'

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -21,11 +21,13 @@
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "simple_mqtt_client.hpp"
#include "esp_vfs_dev.h" // For optional VFS support
#include "esp_https_ota.h" // For potential OTA configuration
#include "vfs_resource/vfs_create.hpp"
#include "SIM7070_gnss.hpp"
#include "esp_console.h"
#include "console_ping.h"
#include "esp_event.h"
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
@@ -35,18 +37,13 @@
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_HW
#endif
#define BROKER_URL CONFIG_BROKER_URI
using namespace esp_modem;
static const char *TAG = "cmux_example";
class StatusHandler {
public:
static constexpr auto IP_Event = SignalGroup::bit0;
static constexpr auto MQTT_Connect = SignalGroup::bit1;
static constexpr auto MQTT_Data = SignalGroup::bit2;
static constexpr auto IP_Event = SignalGroup::bit0;
StatusHandler()
{
@@ -58,12 +55,6 @@ public:
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_event);
}
void handle_mqtt(MqttClient *client)
{
mqtt_client = client;
client->register_handler(ESP_EVENT_ANY_ID, on_event, this);
}
esp_err_t wait_for(decltype(IP_Event) event, int milliseconds)
{
return signal.wait_any(event, milliseconds);
@@ -80,8 +71,6 @@ private:
auto *handler = static_cast<StatusHandler *>(arg);
if (base == IP_EVENT) {
handler->ip_event(event, data);
} else {
handler->mqtt_event(event, data);
}
}
@@ -92,27 +81,16 @@ private:
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
ip_event_type = static_cast<ip_event_t>(id);
signal.set(IP_Event);
} else if (id == IP_EVENT_PPP_LOST_IP) {
ip_event_type = static_cast<ip_event_t>(id);
signal.set(IP_Event);
}
ip_event_type = static_cast<ip_event_t>(id);
}
void mqtt_event(int32_t event, void *data)
{
if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::CONNECT)) {
signal.set(MQTT_Connect);
} else if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::DATA)) {
ESP_LOGI(TAG, " TOPIC: %s", mqtt_client->get_topic(data).c_str());
ESP_LOGI(TAG, " DATA: %s", mqtt_client->get_data(data).c_str());
signal.set(MQTT_Data);
}
}
esp_modem::SignalGroup signal{};
MqttClient *mqtt_client{nullptr};
ip_event_t ip_event_type;
ip_event_t ip_event_type{};
};
@@ -122,6 +100,11 @@ extern "C" void app_main(void)
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_netif_init());
// Initialize console REPL, register ping and start it
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_cmd_ping_register());
ESP_ERROR_CHECK(console_cmd_start());
/* Configure and create the DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
/* setup UART specific configuration based on kconfig options */
@@ -175,7 +158,7 @@ extern "C" void app_main(void)
#endif
assert(dce);
/* Try to connect to the network and publish an mqtt topic */
/* Try to connect to the network */
StatusHandler handler;
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
@@ -224,24 +207,14 @@ extern "C" void app_main(void)
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_GOT_IP) {
std::cout << "Got IP address" << std::endl;
/* When connected to network, subscribe and publish some MQTT data */
MqttClient mqtt(BROKER_URL);
handler.handle_mqtt(&mqtt);
mqtt.connect();
if (!handler.wait_for(StatusHandler::MQTT_Connect, 60000)) {
ESP_LOGE(TAG, "Cannot connect to %s within specified timeout... exiting", BROKER_URL);
/* When connected to network, we can ping the internet */
int ping_ret_val;
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
if (ping_ret_val != 0) {
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
return;
}
std::cout << "Connected" << std::endl;
mqtt.subscribe(CONFIG_EXAMPLE_MQTT_TEST_TOPIC);
mqtt.publish(CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA);
if (!handler.wait_for(StatusHandler::MQTT_Data, 60000)) {
ESP_LOGE(TAG, "Didn't receive published data within specified timeout... exiting");
return;
}
std::cout << "Received MQTT data" << std::endl;
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_LOST_IP) {
ESP_LOGE(TAG, "PPP client has lost connection... exiting");
return;

View File

@@ -1,100 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/*
* PPPoS Client Example
*/
#include <memory>
#include "mqtt_client.h"
#include "simple_mqtt_client.hpp"
/**
* Reference to the MQTT event base
*/
ESP_EVENT_DECLARE_BASE(MQTT_EVENTS);
/**
* Thin wrapper around C mqtt_client
*/
struct MqttClientHandle {
explicit MqttClientHandle(const std::string &uri)
{
esp_mqtt_client_config_t config = { };
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
config.broker.address.uri = uri.c_str();
#else
config.uri = uri.c_str();
#endif
client = esp_mqtt_client_init(&config);
}
~MqttClientHandle()
{
esp_mqtt_client_destroy(client);
}
esp_mqtt_client_handle_t client;
};
/**
* @brief Definitions of MqttClient methods
*/
MqttClient::MqttClient(const std::string &uri):
h(std::unique_ptr<MqttClientHandle>(new MqttClientHandle(uri)))
{}
void MqttClient::connect()
{
esp_mqtt_client_start(h->client);
}
int32_t MqttClient::get_event(MqttClient::Event ev)
{
switch (ev) {
case Event::CONNECT: {
return MQTT_EVENT_CONNECTED;
}
case Event::DATA:
return MQTT_EVENT_DATA;
}
return -1;
}
int MqttClient::publish(const std::string &topic, const std::string &data, int qos)
{
return esp_mqtt_client_publish(h->client, topic.c_str(), data.c_str(), 0, qos, 0);
}
int MqttClient::subscribe(const std::string &topic, int qos)
{
return esp_mqtt_client_subscribe(h->client, topic.c_str(), qos);
}
std::string MqttClient::get_topic(void *event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)
return {};
return std::string(event->topic, event->topic_len);
}
std::string MqttClient::get_data(void *event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)
return {};
return std::string(event->data, event->data_len);
}
void MqttClient::register_handler(int32_t event_id, esp_event_handler_t event_handler, void *arg)
{
ESP_ERROR_CHECK(esp_mqtt_client_register_event(h->client, MQTT_EVENT_ANY, event_handler, arg));
}
MqttClient::~MqttClient() = default;

View File

@@ -1,81 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/*
* PPPoS Client Example
*/
#pragma once
#include <string>
#include <memory>
struct MqttClientHandle;
/**
* @brief Simple MQTT client wrapper
*/
class MqttClient {
public:
enum class Event {
CONNECT,
DATA,
};
explicit MqttClient(const std::string &uri);
~MqttClient();
/**
* @brief Start the mqtt-client
*/
void connect();
/**
* @brief Publish to topic
* @param topic Topic to publish
* @param data Data to publish
* @param qos QoS (0 by default)
* @return message id
*/
int publish(const std::string &topic, const std::string &data, int qos = 0);
/**
* @brief Subscribe to a topic
* @param topic Topic to subscribe
* @param qos QoS (0 by default)
* @return message id
*/
int subscribe(const std::string &topic, int qos = 0);
/**
* @brief Get topic from event data
* @return String topic
*/
std::string get_topic(void *);
/**
* @brief Get published data from event
* @return String representation of the data
*/
std::string get_data(void *);
/**
* @brief Register MQTT event
* @param id Event id
* @param event_handler Event handler
* @param event_handler_arg Event handler parameters
*/
void register_handler(int32_t id, esp_event_handler_t event_handler, void *event_handler_arg);
/**
* @brief Convert internal MQTT event to standard ESPEvent
* @param ev internal mqtt event
* @return corresponding esp_event id
*/
static int32_t get_event(Event ev);
private:
std::unique_ptr<MqttClientHandle> h;
};

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
from __future__ import print_function, unicode_literals
@@ -6,21 +6,10 @@ from __future__ import print_function, unicode_literals
def test_cmux_connection(dut):
"""
steps:
1. initializes connection with SIM800
2. checks we get an IP
3. checks for the MQTT events
1. checks we're in CMUX mode and get an IP
2. checks for ping command
"""
# Get topic and data from Kconfig
topic = ''
data = ''
try:
topic = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_TOPIC')
data = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_DATA')
except Exception:
print('ENV_TEST_FAILURE: Cannot find broker url in sdkconfig')
raise
# Check the sequence of connecting, publishing, disconnecting
dut.expect('Modem has correctly entered multiplexed')
# Check for MQTT connection and the data event
dut.expect(f'TOPIC: {topic}')
dut.expect(f'DATA: {data}')
# Check we're in CMUX mode and get an IP
dut.expect('Modem has correctly entered multiplexed command/data mode', timeout=60)
# Check for ping command
dut.expect('Ping command finished with return value: 0', timeout=30)

View File

@@ -1,4 +1,4 @@
version: "1.4.0"
version: "2.0.0"
description: Library for communicating with cellular modems in command and data modes
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
issues: https://github.com/espressif/esp-protocols/issues

View File

@@ -62,12 +62,16 @@ class Creator {
public:
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif): dte(std::move(dte)), device(nullptr), netif(esp_netif)
{
#ifdef CONFIG_ESP_MODEM_USE_PPP_MODE
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
#endif
}
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif, std::shared_ptr<T_Module> dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif)
{
#ifdef CONFIG_ESP_MODEM_USE_PPP_MODE
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
#endif
}
explicit Creator(std::shared_ptr<DTE> dte): dte(std::move(dte)), device(nullptr), netif(nullptr)

View File

@@ -103,6 +103,11 @@ public:
{
dte->set_urc_cb(on_read_cb);
}
void set_enhanced_urc(esp_modem::DTE::enhanced_urc_cb enhanced_cb)
{
dte->set_enhanced_urc_cb(enhanced_cb);
}
#endif
/**

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -115,6 +115,42 @@ public:
{
command_cb.urc_handler = std::move(line_cb);
}
/**
* @brief Enhanced URC handler with buffer consumption control
* @param buffer_info Information about the current buffer state
* @return Information about how much of the buffer to consume
*/
struct UrcBufferInfo {
const uint8_t* buffer_start; // Start of entire buffer
size_t buffer_total_size; // Total buffer size
size_t processed_offset; // Offset of already processed data
size_t new_data_size; // Size of new data since last call
const uint8_t* new_data_start; // Pointer to start of new data
bool is_command_active; // Whether a command is currently waiting for response
};
enum class UrcConsumeResult {
CONSUME_NONE, // Don't consume anything, continue waiting
CONSUME_PARTIAL, // Consume only part of the buffer
CONSUME_ALL // Consume entire buffer
};
struct UrcConsumeInfo {
UrcConsumeResult result;
size_t consume_size; // How many bytes to consume (0 = none, SIZE_MAX = all)
};
typedef std::function<UrcConsumeInfo(const UrcBufferInfo &)> enhanced_urc_cb;
/**
* @brief Set enhanced URC callback with buffer consumption control
* @param enhanced_cb Enhanced callback that can control buffer consumption
*/
void set_enhanced_urc_cb(enhanced_urc_cb enhanced_cb)
{
command_cb.enhanced_urc_handler = std::move(enhanced_cb);
}
#endif
/**
@@ -171,6 +207,33 @@ private:
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
void exit_cmux_internal(); /*!< Cleanup CMUX */
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
/**
* @brief Buffer state tracking for enhanced URC processing
*/
struct BufferState {
size_t total_processed = 0; /*!< Total bytes processed by URC handlers */
size_t last_urc_processed = 0; /*!< Last offset processed by URC */
bool command_waiting = false; /*!< Whether command is waiting for response */
size_t command_start_offset = 0; /*!< Where current command response started */
} buffer_state;
/**
* @brief Update buffer state when new data arrives
* @param new_data_size Size of new data added to buffer
*/
void update_buffer_state(size_t new_data_size);
/**
* @brief Create URC buffer information for enhanced handlers
* @param data Buffer data pointer
* @param consumed Already consumed bytes
* @param len New data length
* @return UrcBufferInfo structure with complete buffer context
*/
UrcBufferInfo create_urc_info(uint8_t* data, size_t consumed, size_t len);
#endif
Lock internal_lock{}; /*!< Locks DTE operations */
unique_buffer buffer; /*!< DTE buffer */
std::shared_ptr<CMux> cmux_term; /*!< Primary terminal for this DTE */
@@ -216,6 +279,7 @@ private:
struct command_cb {
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
got_line_cb urc_handler {}; /*!< URC callback if enabled */
enhanced_urc_cb enhanced_urc_handler {}; /*!< Enhanced URC callback with consumption control */
#endif
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
got_line_cb got_line; /*!< Supplied command callback */
@@ -223,7 +287,7 @@ private:
char separator{}; /*!< Command reply separator (end of line/processing unit) */
command_result result{}; /*!< Command return code */
SignalGroup signal; /*!< Event group used to signal request-response operations */
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
bool process_line(uint8_t *data, size_t consumed, size_t len, DTE* dte = nullptr); /*!< Lets the processing callback handle one line (processing unit) */
bool wait_for_line(uint32_t time_ms) /*!< Waiting for command processing */
{
return signal.wait_any(command_cb::GOT_LINE, time_ms);

View File

@@ -89,7 +89,7 @@
#define MPPE_SUPPORT 0
#define PPP_MAXIDLEFLAG 0
#define PPP_MAXIDLEFLAG 100
#define PRINTPKT_SUPPORT 1
#define PPP_PROTOCOLNAME 1

Some files were not shown because too many files have changed in this diff Show More