Compare commits

...

221 Commits

Author SHA1 Message Date
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
33a3ec54b6 Merge pull request #865 from david-cermak/fix/eppp_ipv4_only
[eppp]: Support for IPv4-only mode
2025-08-18 12:03:07 +02:00
David Cermak
c91578c827 bump(eppp): 1.0.0 -> 1.0.1
1.0.1
Bug Fixes
- Support for IPv4-only mode (653328ba, #864)
2025-08-18 11:09:33 +02:00
David Cermak
653328ba07 fix(eppp): Support for IPv4-only mode
Closes https://github.com/espressif/esp-protocols/issues/864
2025-08-18 11:03:17 +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
david-cermak
f8748e026d Merge pull request #862 from david-cermak/fix/mbedtls_cookie
fix(mbedtls_cxx): Enable mbedtls cookie support
2025-08-13 12:41:48 +02:00
David Cermak
479122b21d fix(mbedtls_cxx): Enable mbedtls cookie support 2025-08-13 12:05:14 +02:00
david-cermak
e8ce8f4739 Merge pull request #861 from david-cermak/fix/minor_links
fix(ci): Use default mqtt public endpoint
2025-08-12 16:23:12 +02:00
David Cermak
03df9ae957 fix(mosq): Make esp-peer build optional 2025-08-12 16:07:02 +02:00
David Cermak
35fa0b1d42 ci(common): Fix the link to CONTRIBUTING guidelines 2025-08-12 14:29:08 +02:00
David Cermak
e9d9b3a8bd fix(modem): Use idf-build-apps for building target tests
Also updates default mqtt broker public endpoint
2025-08-12 14:29:03 +02:00
Euripedes
07e8eddcb6 Merge pull request #856 from david-cermak/fix/mosquitto_build
[mosq]: Fix esp_webRTC-lib deprecation with new FreeRTOS
2025-07-29 08:06:05 +02:00
David Cermak
78ae25598b fix(mosq): Fix esp_webRTC deprecation with new FreeRTOS 2025-07-25 18:55:59 +02:00
david-cermak
ffeee3e87a Merge pull request #852 from david-cermak/fix/mqtt_endpoints
fix(examples): Address MQTT public endpoint availability
2025-07-25 16:04:24 +02:00
David Cermak
6d5411941b fix(modem): Make MQTT public broker endpoint configurable 2025-07-25 11:02:36 +02:00
David Cermak
e71365f835 fix(examples): Use configured public broker for MQTT linux test
Also updated pre-commit hook versions, as some don't work in py3.12
2025-07-25 11:01:53 +02:00
david-cermak
8dbf0e4561 Merge pull request #855 from cosmicKev/mdns-fix-custom-allocator
Fixes case where we create our own malloc/free allocators, therefore … (IDFGH-16053)
2025-07-25 07:48:09 +02:00
kevin filipe
63bf70914b fix(mdns): Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free 2025-07-24 13:21:55 +02:00
Guilherme Alves Ferreira
29f1dec408 Merge pull request #854 from glmfe/master
bump(websocket): 1.4.0 -> 1.5.0
2025-07-23 08:45:59 -03:00
glmfe
05715d80d7 bump(websocket): 1.4.0 -> 1.5.0
1.5.0
Features
- add separate tx lock for send and receive (250eebf)
- add unregister event to websocket client (ce16050)
- add ability to reconnect after close (19891d8)
Bug Fixes
- release client-lock during WEBSOCKET_EVENT_DATA (030cb75)
2025-07-22 17:54:02 -03:00
Guilherme Alves Ferreira
75d6845194 Merge pull request #850 from shootao/feat/add_ws_tx_lock
esp_websocket: add_ws_tx_lock (IDFGH-15952)
2025-07-21 08:08:13 -03:00
xutao
250eebf3fc feat(websocket): add separate tx lock for send and receive 2025-07-21 10:46:55 +08:00
david-cermak
84b61dca16 Merge pull request #840 from david-cermak/feat/mosq_esp_peer
[mosq]: Add support for esp-peer in brokerless example
2025-07-18 13:13:27 +02:00
David Cermak
76e45f7254 feat(mosq): Update brokerless example to work with esp-peer
* Relax CI criteria to build on v5.2+ (for the brokerless due to
  esp-peer dependency)
2025-07-18 12:49:34 +02:00
Euripedes
462561b8d9 Merge pull request #843 from david-cermak/fix/example_mqtt
[examples]: Use another public broker for MQTT example
2025-07-15 08:44:34 +02:00
David Cermak
ae8cf218c8 fix(examples): Use another public broker for MQTT example 2025-07-11 19:00:28 +02:00
david-cermak
c340f85a90 Merge pull request #836 from david-cermak/fix/modem_urc
[modem]: Fix URC handler processing
2025-07-11 18:14:08 +02:00
David Cermak
b95d8be41d fix(modem_sim): Support of PPPD exit 2025-07-11 17:37:51 +02:00
David Cermak
9302994673 fix(modem): Fix URC handling in DTE data callback
This partially revert 6eceb28f7d
and fixes URC handling and passing full buffer to higher layers
2025-07-11 11:01:28 +02:00
David Cermak
e5787e3d9f feat(modem_sim): Modem simulator based on esp-at 2025-07-11 11:01:17 +02:00
david-cermak
7cddc8c6f5 Merge pull request #834 from david-cermak/fix/modem_build_new_gcc
[modem]: Fix to use compatible iterator types for std::search in new gcc
2025-07-11 09:29:00 +02:00
David Cermak
fac2edbe59 fix(modem): Use another public broker for examples and tests
since mqtt.eclipseprojects.io is no longer available
2025-07-11 08:16:17 +02:00
David Cermak
ed0f633418 fix(modem): Fix incompatible iterator in std::search() in new gcc
Creates a temporary string view and uses find() instead.
2025-07-11 08:15:53 +02:00
david-cermak
a8631eecf5 Merge pull request #814 from david-cermak/feat/eppp_channels
[eppp-link]: Support for channels
2025-07-10 14:45:29 +02:00
David Cermak
3e28a7264c bump(eppp): 0.3.1 -> 1.0.0
1.0.0
Features
- Add support for custom channels (4ee9360f)
2025-07-10 13:57:13 +02:00
David Cermak
4ee9360f53 feat(eppp): Add support for custom channels 2025-07-10 13:55:46 +02:00
david-cermak
e9b21ea7a3 Merge pull request #833 from david-cermak/fix/mosq_minor
[mosq]: Fix some recent bugs
2025-07-01 12:41:01 +02:00
David Cermak
ac1b2b7573 bump(mosq): 2.0.20~2 -> 2.0.20~3
2.0.20~3
Bug Fixes
- Support build on older IDF branches (13b90ad1)
- Fix misleading error when accepting connection (fd410061, #807)
- Make mosquitto component c++ compatible (c4169765, #817)
- include config.h before any system header (1b1ede43)
2025-07-01 12:20:45 +02:00
David Cermak
13b90ad14b fix(mosq): Support build on older IDF branches
and added build jobs to CI
2025-07-01 12:18:08 +02:00
David Cermak
fd41006193 fix(mosq): Fix misleading error when accepting connection
Closes https://github.com/espressif/esp-protocols/issues/807
2025-07-01 11:31:32 +02:00
Euripedes
e71926f6ed Merge pull request #752 from bryghtlabs-richard/feat/websocketReconnectOnClose
feat(websocket): add ability to reconnect after close (IDFGH-15455)
2025-06-27 14:31:16 +02:00
c4169765af fix(mosq): Make mosquitto component c++ compatible
Closes https://github.com/espressif/esp-protocols/issues/817
Closes https://github.com/espressif/esp-protocols/issues/816
2025-06-27 12:36:16 +02:00
david-cermak
f43dd5012f Merge pull request #829 from david-cermak/fix/modem_get_network_state
[modem]: Fix get_network_registration_state() to accept two params
2025-06-25 12:35:45 +02:00
david-cermak
b143894874 Merge pull request #821 from lolrobbe2/fix-urc-line-handler-modem
fix(modem): Consume buffer after handled URC
2025-06-24 10:22:45 +02:00
david-cermak
d1c912c833 Merge pull request #830 from david-cermak/fix/dns_http_event
[dns]: Fix http event handler to accept default case
2025-06-24 10:21:16 +02:00
David Cermak
4fbd86b278 fix(dns): Fix http event handler to accept default case 2025-06-24 09:52:38 +02:00
david-cermak
af8272d441 Merge pull request #825 from david-cermak/fix/modem_auto_mode
[modem]: Fix autodetect to support ACFC mode in PPP frames
2025-06-24 09:09:01 +02:00
David Cermak
8b328a6904 fix(modem): Fix autodetect to support ACFC mode in PPP frames
Closes https://github.com/espressif/esp-protocols/issues/801
2025-06-23 17:13:32 +02:00
David Cermak
5f54d9075e fix(modem): Fix get_network_registration_state() to accept two params
Co-authored-by: Beernaert Robbe <robbe.beernaert@student.howest.be>
Closes https://github.com/espressif/esp-protocols/issues/826
2025-06-23 11:10:36 +02:00
david-cermak
01952f21cb Merge pull request #824 from david-cermak/fix/mdns_host_test
[mdns]: Fix host tests by freezing idf-build-apps to 2.10
2025-06-17 12:43:25 +02:00
David Cermak
5dfe076c94 fix(mdns): Fix host tests by freezing idf-build-apps to 2.10 2025-06-12 17:47:24 +02:00
david-cermak
ea9f29ad14 Merge pull request #823 from david-cermak/fix/eppp_without_pppos
[eppp]: Fix NETIF_PPP_STATUS link issue if PPP disabled in lwip
2025-06-12 14:20:05 +02:00
David Cermak
217e6d90c8 bump(eppp): 0.3.0 -> 0.3.1
0.3.1
Bug Fixes
- Fix NETIF_PPP_STATUS link issue if PPP disabled in lwip (077ea0bb)
2025-06-12 13:53:42 +02:00
David Cermak
077ea0bb1f fix(eppp): Fix NETIF_PPP_STATUS link issue if PPP disabled in lwip 2025-06-12 13:53:20 +02:00
robbedptechnics
6eceb28f7d fix(modem): Consume buffer after handled URC 2025-06-11 14:50:14 +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
Richard Allen
19891d8c3c feat(websocket): add ability to reconnect after close
Needed to support protocols like Socket.IO, where some
websocket closes issued by the server are reconnectable.
2025-01-30 12:12:12 -06:00
327 changed files with 18659 additions and 10635 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
@@ -59,10 +56,11 @@ jobs:
- name: Build with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${GITHUB_WORKSPACE}/ci/config_env.sh
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
python ./ci/build_apps.py examples/mqtt -l -t linux
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
python ./ci/build_apps.py examples/mqtt -l -t linux -r 'sdkconfig.ci'
timeout 5 ./examples/mqtt/build_linux/esp_mqtt_demo.elf | tee test.log || true
grep 'MQTT_EVENT_DATA' test.log
run_on_target:
@@ -74,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

@@ -24,11 +24,13 @@ jobs:
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps dnspython pytest pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
python -m pip install idf-build-apps==2.10.0 dnspython pytest pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
cd $GITHUB_WORKSPACE/protocols
# 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,8 +39,8 @@ jobs:
name: Build tests
strategy:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
test: ["target", "target_ota", "target_iperf"]
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
container: espressif/idf:${{ matrix.idf_ver }}

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:
@@ -34,18 +34,16 @@ jobs:
IDF_TARGET: ${{ matrix.idf_target }}
SDKCONFIG: sdkconfig.ci.${{ matrix.test.app }}
shell: bash
working-directory: ${{ env.TEST_DIR }}
run: |
. ${GITHUB_WORKSPACE}/ci/config_env.sh
. ${IDF_PATH}/export.sh
rm -rf sdkconfig build
[ -f ${SDKCONFIG} ] && cp ${SDKCONFIG} sdkconfig.defaults
idf.py set-target ${{ matrix.idf_target }}
idf.py build
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build
python -m pip install idf-build-apps
python ./ci/build_apps.py ${{ env.TEST_DIR }} -t ${{ matrix.idf_target }} -r 'sdkconfig.ci.${{ matrix.test.app }}'
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build_${{ matrix.idf_target }}
- uses: actions/upload-artifact@v4
with:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build
path: ${{ env.TEST_DIR }}/build_${{ matrix.idf_target }}
if-no-files-found: error
target_tests_esp_modem:
@@ -56,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:

32
.github/workflows/modem_sim__build.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: "modem_sim: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_modem_sim:
if: contains(github.event.pull_request.labels.*.name, 'modem_sim') || github.event_name == 'push'
name: Build
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
idf.py build

View File

@@ -13,11 +13,21 @@ jobs:
name: Mosquitto build
strategy:
matrix:
idf_ver: ["latest", "release-v5.3"]
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
example: ["broker"]
include:
# 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"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/mosquitto/examples
TEST_DIR: components/mosquitto/examples/${{ matrix.example }}
TARGET_TEST: broker
TARGET_TEST_DIR: build_esp32_default
steps:
@@ -31,14 +41,17 @@ jobs:
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
python ci/build_apps.py -c ${TEST_DIR} -m components/mosquitto/.build-test-rules.yml
# upload only the target test artifacts
cd ${TEST_DIR}/${TARGET_TEST}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
zip -qur artifacts.zip ${TARGET_TEST_DIR}
if [ "${{ matrix.example }}" == "${TARGET_TEST}" ]; then
# upload only the target test artifacts
cd ${TEST_DIR}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
zip -qur artifacts.zip ${TARGET_TEST_DIR}
fi
- uses: actions/upload-artifact@v4
if: ${{ matrix.example == 'broker' }}
with:
name: mosq_target_esp32_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/${{ env.TARGET_TEST }}/artifacts.zip
path: ${{ env.TEST_DIR }}/artifacts.zip
if-no-files-found: error
test_mosq:
@@ -50,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
@@ -66,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
@@ -109,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
@@ -154,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
@@ -169,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

@@ -39,7 +39,7 @@ jobs:
if ! pre-commit run --from-ref origin/HEAD --to-ref HEAD --hook-stage manual --show-diff-on-failure ; then
echo ""
echo "::notice::It looks like the commits in this PR have been made without having pre-commit hooks installed."
echo "::notice::Please see https://github.com/espressif/esp-protocols/CONTRIBUTING.md for instructions."
echo "::notice::Please see https://github.com/espressif/esp-protocols/blob/master/CONTRIBUTING.md for instructions."
echo ""
exit 1
fi

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

@@ -26,5 +26,5 @@ 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 ./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

6
.gitignore vendored
View File

@@ -94,3 +94,9 @@ docs/html
# esp-idf managed components
**/managed_components/**
# modem simulator uses esp-at clone
common_components/modem_sim/modem_sim_esp32/
# repository release tools
release_notes.txt

View File

@@ -25,12 +25,8 @@ repos:
(?x)^(
.*.py
)$
- repo: https://github.com/myint/unify
rev: v0.5
hooks:
- id: unify
- repo: https://github.com/pre-commit/mirrors-yapf
rev: "v0.32.0"
- repo: https://github.com/google/yapf
rev: "v0.43.0"
hooks:
- id: yapf
args: ['style={based_on_style: google, column_limit: 160, indent_width: 4}']
@@ -39,7 +35,7 @@ repos:
hooks:
- id: isort
- repo: https://github.com/myint/eradicate/
rev: v2.1.0
rev: 3.0.0
hooks:
- id: eradicate
- repo: https://github.com/espressif/check-copyright/
@@ -61,8 +57,8 @@ repos:
- repo: local
hooks:
- id: commit message scopes
name: "commit message must be scoped with: mdns, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws"
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws)\)\:)'
name: "commit message must be scoped with: mdns, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws, modem_sim"
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws|modem_sim)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

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,
@@ -65,6 +65,7 @@ if __name__ == '__main__':
sys.exit(
build_apps(apps,
verbose=2,
dry_run=False,
keep_going=False,
no_preserve=args.delete,

7
ci/config_env.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# This script is used to set some common variables for the CI pipeline.
set -e
# MQTT public broker URI
export CI_MQTT_BROKER_URI="test.mosquitto.org"

View File

@@ -1 +1,8 @@
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

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

@@ -0,0 +1,79 @@
# 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_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_esp32_default/partitions_at.csv"
CONFIG_PARTITION_TABLE_MD5=n
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_FILE="module_config/module_esp32_default/at_customize.csv"
CONFIG_BT_ENABLED=y
CONFIG_BT_BTU_TASK_STACK_SIZE=5120
CONFIG_BT_BLE_BLUFI_ENABLE=y
CONFIG_BT_STACK_NO_LOG=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
CONFIG_BTDM_CTRL_MODE_BTDM=y
CONFIG_BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL=y
CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE=200
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_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL=y
CONFIG_ETH_DMA_RX_BUFFER_NUM=3
CONFIG_ETH_DMA_TX_BUFFER_NUM=3
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_EXT_CRYST_ADDIT_CURRENT=y
CONFIG_RTC_CLK_CAL_CYCLES=1024
CONFIG_PM_ENABLE=y
CONFIG_PM_SLP_DISABLE_GPIO=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80=y
CONFIG_ESP_TASK_WDT_PANIC=y
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
CONFIG_ESP_DEBUG_OCDAWARE=n
CONFIG_ESP_WIFI_IRAM_OPT=n
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
CONFIG_ESP_WIFI_SLP_IRAM_OPT=y
CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0
CONFIG_FATFS_LFN_HEAP=y
CONFIG_FREERTOS_UNICORE=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_HEAP_PLACE_FUNCTION_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_MQTT_COMMAND_SUPPORT=y
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

@@ -0,0 +1,11 @@
#!/bin/bash
source $IDF_PATH/export.sh
export AT_CUSTOM_COMPONENTS="`pwd`/pppd_cmd"
cd modem_sim_esp32/esp-at
python -m pip install -r requirements.txt
python build.py reconfigure

View File

@@ -0,0 +1,98 @@
#!/bin/bash
set -e
# Create directory "modem_sim_esp32", go inside it
# 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
if [ -z "$IDF_PATH" ]; then
echo "Error: IDF_PATH environment variable is not set"
exit 1
fi
# Default ESP_AT_VERSION uses this specific commit from master to support new chips and features
ESP_AT_VERSION="aa9d7e0e9b741744f7bf5bec3bbf887cff033d5f"
# Shallow clone of esp-at.git at $ESP_AT_VERSION
if [ ! -d "esp-at" ]; then
# cannot shallow clone from a specific commit, so we init, shallow fetch, and checkout
mkdir -p esp-at && cd esp-at && git init && git remote add origin https://github.com/espressif/esp-at.git
git fetch --depth 1 origin $ESP_AT_VERSION && git checkout $ESP_AT_VERSION
else
echo "esp-at directory already exists, skipping clone."
cd esp-at
fi
# Add esp-idf directory which is a symlink to the $IDF_PATH
if [ ! -L "esp-idf" ]; then
ln -sf "$IDF_PATH" esp-idf
else
echo "esp-idf symlink already exists, skipping."
fi
# Create "build" directory
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
platform="$1"
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": "$description",
"silence": 0
}
EOF
# 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,6 @@
idf_component_register(
SRCS additional_commands.c
INCLUDE_DIRS include
REQUIRES at freertos nvs_flash)
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE)

View File

@@ -0,0 +1,411 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "esp_at.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "esp_check.h"
#include "esp_http_server.h"
#include "esp_timer.h"
static uint8_t at_test_cmd_test(uint8_t *cmd_name)
{
uint8_t buffer[64] = {0};
snprintf((char *)buffer, 64, "test command: <AT%s=?> is executed\r\n", cmd_name);
esp_at_port_write_data(buffer, strlen((char *)buffer));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_query_cmd_test(uint8_t *cmd_name)
{
uint8_t buffer[64] = {0};
snprintf((char *)buffer, 64, "query command: <AT%s?> is executed\r\n", cmd_name);
esp_at_port_write_data(buffer, strlen((char *)buffer));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_setup_cmd_test(uint8_t para_num)
{
uint8_t index = 0;
printf("setup command: <AT%s=%d> is executed\r\n", esp_at_get_current_cmd_name(), para_num);
// get first parameter, and parse it into a digit
int32_t digit = 0;
if (esp_at_get_para_as_digit(index++, &digit) != ESP_AT_PARA_PARSE_RESULT_OK) {
return ESP_AT_RESULT_CODE_ERROR;
}
printf("digit: %d\r\n", digit);
// get second parameter, and parse it into a string
uint8_t *str = NULL;
if (esp_at_get_para_as_str(index++, &str) != ESP_AT_PARA_PARSE_RESULT_OK) {
return ESP_AT_RESULT_CODE_ERROR;
}
printf("string: %s\r\n", str);
// allocate a buffer and construct the data, then send the data to mcu via interface (uart/spi/sdio/socket)
uint8_t *buffer = (uint8_t *)malloc(512);
if (!buffer) {
return ESP_AT_RESULT_CODE_ERROR;
}
int len = snprintf((char *)buffer, 512, "setup command: <AT%s=%d,\"%s\"> is executed\r\n",
esp_at_get_current_cmd_name(), digit, str);
esp_at_port_write_data(buffer, len);
// remember to free the buffer
free(buffer);
return ESP_AT_RESULT_CODE_OK;
}
#define TAG "at_custom_cmd"
static esp_netif_t *s_netif = NULL;
static httpd_handle_t http_server = NULL;
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
esp_netif_t **netif = data;
if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
printf("Disconnected!");
}
}
static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *)data;
esp_netif_t *netif = event->esp_netif;
if (event_id == IP_EVENT_PPP_GOT_IP) {
printf("Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
esp_netif_get_ifkey(netif), IP2STR(&event->ip_info.ip));
ESP_ERROR_CHECK(esp_netif_napt_enable(s_netif));
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Disconnected");
}
}
static SemaphoreHandle_t at_sync_sema = NULL;
static void wait_data_callback(void)
{
static uint8_t buffer[1500] = {0};
int len = esp_at_port_read_data(buffer, sizeof(buffer) - 1);
// Check for the escape sequence "+++" in the received data
const uint8_t escape_seq[] = "+++";
uint8_t *escape_ptr = memmem(buffer, len, escape_seq, 3);
if (escape_ptr != NULL) {
printf("Found +++ sequence, signal to the command processing thread\n");
int data_before_escape = escape_ptr - buffer;
if (data_before_escape > 0) {
esp_netif_receive(s_netif, buffer, data_before_escape, NULL);
}
if (at_sync_sema) {
xSemaphoreGive(at_sync_sema);
}
return;
}
esp_netif_receive(s_netif, buffer, len, NULL);
}
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
printf("transmit: %d bytes\n", len);
esp_at_port_write_data(buffer, len);
return ESP_OK;
}
static uint8_t at_exe_cmd_test(uint8_t *cmd_name)
{
uint8_t buffer[64] = {0};
snprintf((char *)buffer, 64, "execute command: <AT%s> is executed\r\n", cmd_name);
esp_at_port_write_data(buffer, strlen((char *)buffer));
printf("Command <AT%s> executed successfully\r\n", cmd_name);
if (!at_sync_sema) {
at_sync_sema = xSemaphoreCreateBinary();
assert(at_sync_sema != NULL);
esp_netif_driver_ifconfig_t driver_cfg = {
.handle = (void *)1,
.transmit = transmit,
};
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
.driver = ppp_driver_cfg,
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
};
s_netif = esp_netif_new(&netif_ppp_config);
esp_netif_ppp_config_t netif_params;
ESP_ERROR_CHECK(esp_netif_ppp_get_params(s_netif, &netif_params));
netif_params.ppp_our_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 1);
netif_params.ppp_their_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 2);
netif_params.ppp_error_event_enabled = true;
ESP_ERROR_CHECK(esp_netif_ppp_set_params(s_netif, &netif_params));
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL) != ESP_OK) {
printf("Failed to register IP event handler");
}
if (esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event, NULL) != ESP_OK) {
printf("Failed to register NETIF_PPP_STATUS event handler");
}
}
esp_at_port_write_data((uint8_t *)"CONNECT\r\n", strlen("CONNECT\r\n"));
// set the callback function which will be called by AT port after receiving the input data
esp_at_port_enter_specific(wait_data_callback);
esp_netif_action_start(s_netif, 0, 0, 0);
esp_netif_action_connected(s_netif, 0, 0, 0);
while (xSemaphoreTake(at_sync_sema, pdMS_TO_TICKS(1000)) == pdFALSE) {
printf(".");
}
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_test_cereg(uint8_t *cmd_name)
{
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_query_cereg(uint8_t *cmd_name)
{
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
static uint8_t buffer[] = "+CEREG: 7,8\r\n";
esp_at_port_write_data(buffer, sizeof(buffer));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_setup_cereg(uint8_t num)
{
printf("%s: AT command <AT%d> is executed\r\n", __func__, num);
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_exe_cereg(uint8_t *cmd_name)
{
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
return ESP_AT_RESULT_CODE_OK;
}
static esp_err_t hello_get_handler(httpd_req_t *req)
{
const char* resp_str = "Hello from ESP-AT HTTP Server!";
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static esp_err_t root_get_handler(httpd_req_t *req)
{
const char* resp_str = "ESP-AT HTTP Server is running";
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static esp_err_t test_get_handler(httpd_req_t *req)
{
const char* resp_str = "{\"status\":\"success\",\"message\":\"Test endpoint working\",\"timestamp\":12345}";
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static esp_err_t async_get_handler(httpd_req_t *req)
{
printf("Starting async chunked response handler\r\n");
// Set content type for plain text response
httpd_resp_set_type(req, "text/plain");
// Static counter to track requests
static uint8_t req_count = 0;
req_count++;
// Send initial response with request count
char buffer[256];
snprintf(buffer, sizeof(buffer), "=== Async Response #%d ===\r\n", req_count);
httpd_resp_sendstr_chunk(req, buffer);
// Long message broken into chunks
const char* chunks[] = {
"This is a simulated slow server response.\r\n",
"Chunk 1: The ESP-AT HTTP server is demonstrating...\r\n",
"Chunk 2: ...asynchronous chunked transfer encoding...\r\n",
"Chunk 3: ...with artificial delays between chunks...\r\n",
"Chunk 4: ...to simulate real-world network conditions.\r\n",
"Chunk 5: Processing data... please wait...\r\n",
"Chunk 6: Still processing... almost done...\r\n",
"Chunk 7: Final chunk - transfer complete!\r\n",
"=== END OF RESPONSE ===\r\n"
};
int num_chunks = sizeof(chunks) / sizeof(chunks[0]);
// Send each chunk with delays
for (int i = 0; i < num_chunks; i++) {
// Add a delay to simulate slow processing
vTaskDelay(pdMS_TO_TICKS(1500)); // 1.5 second delay between chunks
// Add chunk number and timestamp
snprintf(buffer, sizeof(buffer), "[%d/%d] [%d ms] %s",
i + 1, num_chunks, (int)(esp_timer_get_time() / 1000), chunks[i]);
printf("Sending chunk %d: %s", i + 1, chunks[i]);
httpd_resp_sendstr_chunk(req, buffer);
}
// Add final summary
vTaskDelay(pdMS_TO_TICKS(500));
snprintf(buffer, sizeof(buffer), "\r\nTransfer completed in %d chunks with delays.\r\n", num_chunks);
httpd_resp_sendstr_chunk(req, buffer);
// Send NULL to signal end of chunked transfer
httpd_resp_sendstr_chunk(req, NULL);
printf("Async chunked response completed\r\n");
return ESP_OK;
}
static const httpd_uri_t hello = {
.uri = "/hello",
.method = HTTP_GET,
.handler = hello_get_handler,
.user_ctx = NULL
};
static const httpd_uri_t root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler,
.user_ctx = NULL
};
static const httpd_uri_t test = {
.uri = "/test",
.method = HTTP_GET,
.handler = test_get_handler,
.user_ctx = NULL
};
static const httpd_uri_t async_uri = {
.uri = "/async",
.method = HTTP_GET,
.handler = async_get_handler,
.user_ctx = NULL
};
static esp_err_t start_http_server(void)
{
if (http_server != NULL) {
printf("HTTP server already running\r\n");
return ESP_OK;
}
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 8080;
config.lru_purge_enable = true;
printf("Starting HTTP server on port: %d\r\n", config.server_port);
if (httpd_start(&http_server, &config) == ESP_OK) {
printf("Registering URI handlers\r\n");
httpd_register_uri_handler(http_server, &hello);
httpd_register_uri_handler(http_server, &root);
httpd_register_uri_handler(http_server, &test);
httpd_register_uri_handler(http_server, &async_uri);
return ESP_OK;
}
printf("Error starting HTTP server!\r\n");
return ESP_FAIL;
}
static esp_err_t stop_http_server(void)
{
if (http_server != NULL) {
httpd_stop(http_server);
http_server = NULL;
printf("HTTP server stopped\r\n");
return ESP_OK;
}
return ESP_OK;
}
/* HTTP Server AT Commands */
static uint8_t at_test_httpd(uint8_t *cmd_name)
{
uint8_t buffer[64] = {0};
snprintf((char *)buffer, 64, "AT%s=<0/1> - Start/Stop HTTP server\r\n", cmd_name);
esp_at_port_write_data(buffer, strlen((char *)buffer));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_query_httpd(uint8_t *cmd_name)
{
uint8_t buffer[64] = {0};
snprintf((char *)buffer, 64, "+HTTPD:%d\r\n", http_server != NULL ? 1 : 0);
esp_at_port_write_data(buffer, strlen((char *)buffer));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_setup_httpd(uint8_t para_num)
{
int32_t action = 0;
if (esp_at_get_para_as_digit(0, &action) != ESP_AT_PARA_PARSE_RESULT_OK) {
return ESP_AT_RESULT_CODE_ERROR;
}
if (action == 1) {
if (start_http_server() == ESP_OK) {
printf("HTTP server started successfully\r\n");
return ESP_AT_RESULT_CODE_OK;
}
} else if (action == 0) {
if (stop_http_server() == ESP_OK) {
return ESP_AT_RESULT_CODE_OK;
}
}
return ESP_AT_RESULT_CODE_ERROR;
}
static uint8_t at_exe_httpd(uint8_t *cmd_name)
{
// Default action: start server
if (start_http_server() == ESP_OK) {
printf("HTTP server started via execute command\r\n");
return ESP_AT_RESULT_CODE_OK;
}
return ESP_AT_RESULT_CODE_ERROR;
}
static const esp_at_cmd_struct at_custom_cmd[] = {
{"+PPPD", at_test_cmd_test, at_query_cmd_test, at_setup_cmd_test, at_exe_cmd_test},
{"+CEREG", at_test_cereg, at_query_cereg, at_setup_cereg, at_exe_cereg},
{"+HTTPD", at_test_httpd, at_query_httpd, at_setup_httpd, at_exe_httpd},
};
bool esp_at_custom_cmd_register(void)
{
return esp_at_custom_cmd_array_regist(at_custom_cmd, sizeof(at_custom_cmd) / sizeof(esp_at_cmd_struct));
}
ESP_AT_CMD_SET_INIT_FN(esp_at_custom_cmd_register, 1);

View File

@@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_at_core.h"
#include "esp_at.h"
/**
* @brief You can include more header files here for your own AT commands.
*/

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

@@ -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: 0.3.0
version: 1.1.4
version_files:
- idf_component.yml

View File

@@ -1,5 +1,65 @@
# 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
- Support for IPv4-only mode ([653328ba](https://github.com/espressif/esp-protocols/commit/653328ba), [#864](https://github.com/espressif/esp-protocols/issues/864))
## [1.0.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.0)
### Features
- Add support for custom channels ([4ee9360f](https://github.com/espressif/esp-protocols/commit/4ee9360f))
## [0.3.1](https://github.com/espressif/esp-protocols/commits/eppp-v0.3.1)
### Bug Fixes
- Fix NETIF_PPP_STATUS link issue if PPP disabled in lwip ([077ea0bb](https://github.com/espressif/esp-protocols/commit/077ea0bb))
## [0.3.0](https://github.com/espressif/esp-protocols/commits/eppp-v0.3.0)
### Features

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

@@ -93,4 +93,21 @@ menu "eppp_link"
default "06:00:00:00:00:02"
depends on EPPP_LINK_DEVICE_ETH
config EPPP_LINK_CHANNELS_SUPPORT
bool "Enable channel support (multiple logical channels)"
default n
depends on !EPPP_LINK_DEVICE_ETH
help
Enable support for multiple logical channels in the EPPP link layer.
When enabled, you can configure the number of channels used for communication.
config EPPP_LINK_NR_OF_CHANNELS
int "Number of logical channels"
depends on EPPP_LINK_CHANNELS_SUPPORT && !EPPP_LINK_DEVICE_ETH
range 1 8
default 2
help
Set the number of logical channels for EPPP link communication.
Each channel can be used for independent data streams.
endmenu

View File

@@ -1,7 +1,7 @@
# ESP PPP Link component (eppp_link)
The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server, the other one as PPP client.
This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol (if enabled) to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi.
This component could be used for extending network using physical serial connection. Applications could vary from providing RPC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol (if enabled) to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi.
Uses simplified TUN network interface by default to enable faster data transfer on non-UART transports.
## Typical application
@@ -21,6 +21,27 @@ brings in the WiFi connectivity from the communication coprocessor.
+----------------+ +----------------+
```
## Features
### Network Interface Modes
Standard PPP Mode (where PPP protocols is preferred) or simple tunnel using TUN Mode.
### Transport layer
UART, SPI, SDIO, Ethernet
### Support for logical channels
Allows channeling custom data (e.g. 802.11 frames)
## (Other) usecases
Besides the communication coprocessor example mentioned above, this component could be used to:
* Bring Wi-Fi connectivity to a computer using ESP32 chip.
* Connect your microcontroller to the internet via a pppd server (running on a raspberry)
* Bridging two networks with two microcontrollers
## Configuration
### Choose the transport layer
@@ -39,6 +60,14 @@ Use `idf.py menuconfig` to select the transport layer:
Use PPP netif for UART; Keep the default (TUN) for others
### Channel support (multiple logical channels)
* `CONFIG_EPPP_LINK_CHANNELS_SUPPORT` -- Enable support for multiple logical channels (default: disabled)
* `CONFIG_EPPP_LINK_NR_OF_CHANNELS` -- Number of logical channels (default: 2, range: 1-8, only visible if channel support is enabled)
When channel support is enabled, the EPPP link can multiplex multiple logical data streams over the same transport. The number of channels is configurable. Channel support is not available for Ethernet transport.
To use channels in your application, use the `eppp_add_channels()` API and provide your own channel transmit/receive callbacks. These APIs and related types are only available when channel support is enabled in Kconfig.
## API
@@ -57,6 +86,9 @@ Use PPP netif for UART; Keep the default (TUN) for others
* `eppp_netif_start()` -- Starts the network, could be called after startup or whenever a connection is lost
* `eppp_netif_stop()` -- Stops the network
* `eppp_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration)
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
* `eppp_add_channels()` -- Register channel transmit/receive callbacks (only available if channel support is enabled)
#endif
## Throughput

View File

@@ -0,0 +1,370 @@
# ESP PPP Link Component (eppp_link) - Detailed Documentation
## Overview
The ESP PPP Link component provides a versatile communication bridge between two ESP32 microcontrollers, enabling network connectivity over various physical transport layers. One device acts as a server (typically providing connectivity), while the other acts as a client (consuming connectivity).
## Network Interface Modes
### PPP Mode vs TUN Mode
The component supports two distinct network interface modes:
#### PPP Mode (`CONFIG_EPPP_LINK_USES_PPP=y`)
- **Standard PPP Protocol**: Uses the Point-to-Point Protocol (RFC 1661) with full LCP negotiation
- **Compatibility**: Compatible with standard PPP implementations like Linux `pppd`
- **Features**:
- Automatic IP address negotiation
- Link Control Protocol (LCP) for connection establishment
- Authentication support (if configured)
- Standard PPP framing with escape sequences
- **Use Case**: When interfacing with standard PPP-capable systems or when full PPP compatibility is required
- **Transport Limitation**: Primarily designed for UART transport due to PPP's serial nature
#### TUN Mode (`CONFIG_EPPP_LINK_USES_PPP=n`, default)
- **Simplified Packet Interface**: Uses a custom packet-based protocol without PPP negotiation
- **Performance**: Faster data transfer due to reduced protocol overhead
- **Features**:
- Direct IP packet transmission
- Custom framing for packet boundaries
- No negotiation overhead
- Static IP address configuration
- **Use Case**: Default mode for ESP-to-ESP communication, optimal for non-UART transports
- **Transport Support**: Works efficiently with all transport types (UART, SPI, SDIO, Ethernet)
**Mode Selection**: Configure via `idf.py menuconfig``Component config``eppp_link``Use PPP network interface`
## Transport Layer Options
### UART Transport
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_UART=y`
- **Features**:
- Simple serial communication
- Configurable baud rate (up to 3Mbps tested)
- Hardware flow control support
- Custom framing for packet boundaries in TUN mode
- **Performance**: ~2 Mbps (TCP/UDP) @ 3 Mbaud
- **Use Case**: Basic connectivity, long-distance communication, debugging
- **Pins**: TX, RX configurable
```c
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.transport = EPPP_TRANSPORT_UART;
config.uart.tx_io = 25;
config.uart.rx_io = 26;
config.uart.baud = 921600;
```
### SPI Transport
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SPI=y`
- **Features**:
- Master/slave configuration
- GPIO interrupt signaling for flow control
- Configurable clock frequency
- Full-duplex communication
- Packet queue for transmission
- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) @ 16MHz
- **Use Case**: High-speed local communication, PCB-level connections
- **Pins**: MOSI, MISO, SCLK, CS, interrupt GPIO
```c
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.transport = EPPP_TRANSPORT_SPI;
config.spi.is_master = true;
config.spi.freq = 16000000;
config.spi.mosi = 11;
config.spi.miso = 13;
config.spi.sclk = 12;
config.spi.cs = 10;
config.spi.intr = 2;
```
### SDIO Transport
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SDIO=y`
- **Features**:
- Host/slave configuration
- High-speed data transfer
- 1-bit or 4-bit bus width
- Hardware flow control
- **Performance**: ~9 Mbps (TCP), ~11 Mbps (UDP)
- **Use Case**: Highest throughput applications, module-to-module communication
- **Pins**: CLK, CMD, D0-D3 (configurable width)
```c
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.transport = EPPP_TRANSPORT_SDIO;
config.sdio.is_host = true;
config.sdio.width = 4;
config.sdio.clk = 18;
config.sdio.cmd = 19;
config.sdio.d0 = 14;
// ... additional data pins
```
### Ethernet Transport
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_ETH=y`
- **Features**:
- Direct MAC-to-MAC communication
- Can work with or without PHY chips
- Standard Ethernet framing
- Automatic task management (no `eppp_perform()` needed)
- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) with internal EMAC
- **Use Case**: Board-to-board communication, integration with existing Ethernet infrastructure
- **Pins**: MDC, MDIO, plus PHY-specific pins
```c
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.transport = EPPP_TRANSPORT_ETHERNET;
config.ethernet.mdc_io = 23;
config.ethernet.mdio_io = 18;
config.ethernet.phy_addr = 1;
config.ethernet.rst_io = 5;
```
## Channel Support (Multiple Logical Channels)
### Overview
Channel support allows multiplexing multiple independent data streams over a single transport connection. This enables applications to separate different types of data (e.g., network traffic, control commands, sensor data) into distinct logical channels.
### Configuration
- **Enable**: `CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y`
- **Count**: `CONFIG_EPPP_LINK_NR_OF_CHANNELS` (1-8 channels, default 2)
- **Limitation**: Not available for Ethernet transport
### Channel Usage
```c
// Channel callback function type
typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len);
// Register channel callbacks
esp_err_t eppp_add_channels(esp_netif_t *netif,
eppp_channel_fn_t *tx, // Transmit function pointer (output)
const eppp_channel_fn_t rx, // Receive callback (input)
void* context); // User context
// Get user context
void* eppp_get_context(esp_netif_t *netif);
```
### Channel Example
```c
// Channel 0: Default network traffic (handled automatically)
// Channel 1: Control/chat messages
// Channel 2: WiFi data forwarding
static esp_err_t channel_receive(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
switch(channel) {
case 1: // Control channel
process_control_message(buffer, len);
break;
case 2: // WiFi channel
forward_to_wifi(buffer, len);
break;
default:
ESP_LOGE(TAG, "Unknown channel %d", channel);
return ESP_FAIL;
}
return ESP_OK;
}
// Register channels
eppp_channel_fn_t tx_func;
eppp_add_channels(netif, &tx_func, channel_receive, user_context);
// Transmit on specific channel
tx_func(netif, 1, "Hello", 5); // Send to channel 1
```
## API Reference
### Simple API (Recommended for most use cases)
#### Client Side
```c
esp_netif_t *eppp_connect(eppp_config_t *config);
```
- **Purpose**: Simplified client connection
- **Behavior**: Blocks until connection is established
- **Returns**: Configured network interface or NULL on failure
- **Use Case**: Simple applications that don't need fine-grained control
#### Server Side
```c
esp_netif_t *eppp_listen(eppp_config_t *config);
```
- **Purpose**: Simplified server listening
- **Behavior**: Blocks until client connects
- **Returns**: Configured network interface or NULL on failure
- **Use Case**: Simple applications that don't need fine-grained control
#### Connection Management
```c
void eppp_close(esp_netif_t *netif);
```
- **Purpose**: Close connection and cleanup resources
- **Behavior**: Stops tasks, closes transport, destroys network interface
### Advanced API (Manual Control)
#### Initialization
```c
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config);
```
- **Purpose**: Initialize endpoint without starting communication
- **Parameters**:
- `role`: `EPPP_SERVER` or `EPPP_CLIENT`
- `config`: Transport and network configuration
- **Returns**: Network interface handle
- **Use Case**: Applications needing manual control over connection lifecycle
#### Connection Control
```c
esp_err_t eppp_netif_start(esp_netif_t *netif);
esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms);
```
- **Purpose**: Manual network interface start/stop
- **Use Case**: Dynamic connection management, error recovery
#### Task Management
```c
esp_err_t eppp_perform(esp_netif_t *netif);
```
- **Purpose**: Single iteration of communication task
- **Returns**:
- `ESP_OK`: Continue operation
- `ESP_FAIL`: Operation failed but should continue
- `ESP_ERR_TIMEOUT`: Stop operation requested
- **Use Case**: Task-less operation, integration with custom task schedulers
- **Note**: Not needed for Ethernet transport (has its own task)
#### Resource Management
```c
void eppp_deinit(esp_netif_t *netif);
```
- **Purpose**: Clean up resources without stopping tasks
- **Use Case**: Manual resource management
## Configuration Examples
### Basic Client-Server Setup
**Client (Host) Configuration:**
```c
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.transport = EPPP_TRANSPORT_UART;
config.uart.tx_io = 25;
config.uart.rx_io = 26;
config.uart.baud = 921600;
esp_netif_t *netif = eppp_connect(&config);
if (netif == NULL) {
ESP_LOGE(TAG, "Failed to connect");
return;
}
ESP_LOGI(TAG, "Connected successfully");
// Use network interface for communication
}
```
**Server (Slave) Configuration:**
```c
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Initialize WiFi or other network interface
init_wifi();
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
config.transport = EPPP_TRANSPORT_UART;
config.uart.tx_io = 26; // Crossed with client
config.uart.rx_io = 25; // Crossed with client
config.uart.baud = 921600;
esp_netif_t *netif = eppp_listen(&config);
if (netif == NULL) {
ESP_LOGE(TAG, "Failed to setup server");
return;
}
// Enable NAT to share WiFi connection
ESP_ERROR_CHECK(esp_netif_napt_enable(netif));
ESP_LOGI(TAG, "Server ready");
}
```
### Advanced Manual Control
```c
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.task.run_task = false; // Disable automatic task
esp_netif_t *netif = eppp_init(EPPP_CLIENT, &config);
if (netif == NULL) {
ESP_LOGE(TAG, "Failed to initialize");
return;
}
// Start network interface
ESP_ERROR_CHECK(eppp_netif_start(netif));
// Custom task loop
while (true) {
esp_err_t ret = eppp_perform(netif);
if (ret == ESP_ERR_TIMEOUT) {
ESP_LOGI(TAG, "Operation stopped");
break;
} else if (ret != ESP_OK) {
ESP_LOGE(TAG, "Operation failed: %s", esp_err_to_name(ret));
}
// Add custom processing here
vTaskDelay(pdMS_TO_TICKS(1));
}
eppp_deinit(netif);
}
```
### Multi-Channel Configuration
```c
typedef struct {
eppp_channel_fn_t tx_func;
esp_netif_t *netif;
} channel_context_t;
static esp_err_t channel_rx(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
ESP_LOGI(TAG, "Channel %d received %d bytes", channel, len);
// Process channel data based on channel number
return ESP_OK;
}
void setup_channels(void)
{
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
esp_netif_t *netif = eppp_connect(&config);
channel_context_t *ctx = calloc(1, sizeof(channel_context_t));
ctx->netif = netif;
// Register channel callbacks
ESP_ERROR_CHECK(eppp_add_channels(netif, &ctx->tx_func, channel_rx, ctx));
// Send data on channel 1
const char *msg = "Hello on channel 1";
ctx->tx_func(netif, 1, (void*)msg, strlen(msg));
}
```

View File

@@ -163,6 +163,7 @@ static int get_netif_num(esp_netif_t *netif)
return netif_cnt;
}
#ifdef CONFIG_EPPP_LINK_USES_PPP
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
esp_netif_t **netif = data;
@@ -172,6 +173,7 @@ static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, voi
h->netif_stop = true;
}
}
#endif // CONFIG_EPPP_LINK_USES_PPP
static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
@@ -222,7 +224,9 @@ static void remove_handlers(void)
vEventGroupDelete(s_event_group);
s_event_group = NULL;
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event);
#ifdef CONFIG_EPPP_LINK_USES_PPP
esp_event_handler_unregister(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event);
#endif
}
}
@@ -296,11 +300,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time
remove_handlers();
return NULL;
}
#ifdef CONFIG_EPPP_LINK_USES_PPP
if (esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event, NULL) != ESP_OK) {
ESP_LOGE(TAG, "Failed to register PPP status handler");
remove_handlers();
return NULL;
}
#endif // CONFIG_EPPP_LINK_USES_PPP
}
esp_netif_t *netif = eppp_init(role, config);
if (!netif) {
@@ -361,3 +367,24 @@ void eppp_close(esp_netif_t *netif)
eppp_deinit(netif);
remove_handlers();
}
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context)
{
ESP_RETURN_ON_FALSE(netif != NULL && tx != NULL && rx != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
struct eppp_handle *h = esp_netif_get_io_driver(netif);
ESP_RETURN_ON_FALSE(h != NULL && h->channel_tx != NULL, ESP_ERR_INVALID_STATE, TAG, "Transport not initialized");
*tx = h->channel_tx;
h->channel_rx = rx;
h->context = context;
return ESP_OK;
}
void* eppp_get_context(esp_netif_t *netif)
{
ESP_RETURN_ON_FALSE(netif != NULL, NULL, TAG, "Invalid netif");
struct eppp_handle *h = esp_netif_get_io_driver(netif);
ESP_RETURN_ON_FALSE(h != NULL, NULL, TAG, "EPPP Not initialized");
return h->context;
}
#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)
@@ -65,12 +74,13 @@ static err_t tun_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t
LWIP_UNUSED_ARG(ipaddr);
return tun_output(netif, p);
}
#if LWIP_IPV6
static err_t tun_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
LWIP_UNUSED_ARG(ipaddr);
return tun_output(netif, p);
}
#endif
static err_t tun_init(struct netif *netif)
{
if (netif == NULL) {
@@ -151,9 +161,12 @@ static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
}
if (IP_IS_V4(&target_addr)) {
ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
}
#if LWIP_IPV6
else {
ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
#endif
ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
transmitted, received, loss, total_time_ms);
esp_ping_delete_session(hdl);
@@ -163,12 +176,19 @@ 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};
esp_netif_ip_info_t ip;
esp_netif_get_ip_info(netif, &ip);
#if LWIP_IPV6
target_addr.u_addr.ip4.addr = ip.gw.addr;
#else
target_addr.addr = ip.gw.addr;
#endif
config.target_addr = target_addr;
esp_ping_callbacks_t cbs = {
.cb_args = netif,

View File

@@ -13,6 +13,7 @@
#include "eppp_link.h"
#include "eppp_transport.h"
#include "eppp_transport_sdio.h"
#include "eppp_sdio.h"
#define TAG "eppp_sdio"
@@ -67,6 +68,9 @@ eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config)
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
struct eppp_sdio *h = calloc(1, sizeof(struct eppp_sdio));
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
h->parent.channel_tx = eppp_sdio_transmit_channel;
#endif
h->parent.base.post_attach = post_attach;
h->is_host = config->is_host;
esp_err_t (*init_fn)(struct eppp_config_sdio_s * eppp_config) = h->is_host ? eppp_sdio_host_init : eppp_sdio_slave_init;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,8 +8,10 @@
#define MAX_SDIO_PAYLOAD 1500
#define SDIO_ALIGN(size) (((size) + 3U) & ~(3U))
#define SDIO_PAYLOAD SDIO_ALIGN(MAX_SDIO_PAYLOAD)
#define SDIO_PACKET_SIZE SDIO_ALIGN(MAX_SDIO_PAYLOAD + 4)
#define PPP_SOF 0x7E
// Interrupts and registers
#define SLAVE_INTR 0
#define SLAVE_REG_REQ 0
@@ -17,3 +19,11 @@
// Requests from host to slave
#define REQ_RESET 1
#define REQ_INIT 2
struct header {
uint8_t magic;
uint8_t channel;
uint16_t size;
} __attribute__((packed));
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -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"
@@ -15,6 +17,7 @@
#include "sdmmc_cmd.h"
#include "esp_check.h"
#include "eppp_link.h"
#include "eppp_transport.h"
#if CONFIG_EPPP_LINK_DEVICE_SDIO_HOST
@@ -28,22 +31,23 @@ static SemaphoreHandle_t s_essl_mutex = NULL;
static essl_handle_t s_essl = NULL;
static sdmmc_card_t *s_card = NULL;
static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PAYLOAD];
static DMA_ATTR uint8_t rcv_buffer[SDIO_PAYLOAD];
static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PACKET_SIZE];
static DMA_ATTR uint8_t rcv_buffer[SDIO_PACKET_SIZE];
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
static esp_err_t eppp_sdio_host_tx_generic(int channel, void *buffer, size_t len)
{
if (s_essl == NULL || s_essl_mutex == NULL) {
// silently skip the Tx if the SDIO not fully initialized
return ESP_OK;
}
memcpy(send_buffer, buffer, len);
size_t send_len = SDIO_ALIGN(len);
if (send_len > len) {
// pad with SOF's
memset(&send_buffer[len], PPP_SOF, send_len - len);
}
struct header *head = (void *)send_buffer;
head->magic = PPP_SOF;
head->channel = channel;
head->size = len;
memcpy(send_buffer + sizeof(struct header), buffer, len);
size_t send_len = SDIO_ALIGN(len + sizeof(struct header));
xSemaphoreTake(s_essl_mutex, portMAX_DELAY);
esp_err_t ret = essl_send_packet(s_essl, send_buffer, send_len, PACKET_TIMEOUT_MS);
if (ret != ESP_OK) {
@@ -56,6 +60,19 @@ esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
return ret;
}
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
{
return eppp_sdio_host_tx_generic(0, buffer, len);
}
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
return eppp_sdio_host_tx_generic(channel, buffer, len);
}
#endif
static esp_err_t request_slave_reset(void)
{
esp_err_t ret = ESP_OK;
@@ -145,15 +162,37 @@ esp_err_t eppp_sdio_host_rx(esp_netif_t *netif)
if (intr & ESSL_SDIO_DEF_ESP32.new_packet_intr_mask) {
esp_err_t ret;
do {
size_t size_read = SDIO_PAYLOAD;
ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PAYLOAD, &size_read, PACKET_TIMEOUT_MS);
size_t size_read = SDIO_PACKET_SIZE;
ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PACKET_SIZE, &size_read, PACKET_TIMEOUT_MS);
if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "interrupt but no data can be read");
break;
} else if (ret == ESP_OK) {
ESP_LOGD(TAG, "receive data, size: %d", size_read);
struct header *head = (void *)rcv_buffer;
if (head->magic != PPP_SOF) {
ESP_LOGE(TAG, "invalid magic %x", head->magic);
break;
}
if (head->channel > NR_OF_CHANNELS) {
ESP_LOGE(TAG, "invalid channel %x", head->channel);
break;
}
if (head->size > SDIO_PAYLOAD || head->size > size_read) {
ESP_LOGE(TAG, "invalid size %x", head->size);
break;
}
ESP_LOG_BUFFER_HEXDUMP(TAG, rcv_buffer, size_read, ESP_LOG_VERBOSE);
esp_netif_receive(netif, rcv_buffer, size_read, NULL);
if (head->channel == 0) {
esp_netif_receive(netif, rcv_buffer + sizeof(struct header), head->size, NULL);
} else {
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
struct eppp_handle *h = esp_netif_get_io_driver(netif);
if (h->channel_rx) {
h->channel_rx(netif, head->channel, rcv_buffer + sizeof(struct header), head->size);
}
#endif
}
break;
} else {
ESP_LOGE(TAG, "rx packet error: %08X", ret);

View File

@@ -8,11 +8,13 @@
#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"
#include "eppp_sdio.h"
#include "esp_check.h"
#if CONFIG_EPPP_LINK_DEVICE_SDIO_SLAVE
#define BUFFER_NUM 4
#define BUFFER_SIZE SDIO_PAYLOAD
@@ -21,19 +23,18 @@ static DMA_ATTR uint8_t sdio_slave_rx_buffer[BUFFER_NUM][BUFFER_SIZE];
static DMA_ATTR uint8_t sdio_slave_tx_buffer[SDIO_PAYLOAD];
static int s_slave_request = 0;
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
static esp_err_t eppp_sdio_host_tx_generic(int channel, void *buffer, size_t len)
{
if (s_slave_request != REQ_INIT) {
// silently skip the Tx if the SDIO not fully initialized
return ESP_OK;
}
memcpy(sdio_slave_tx_buffer, buffer, len);
size_t send_len = SDIO_ALIGN(len);
if (send_len > len) {
// pad with SOF's if the size is not 4 bytes aligned
memset(&sdio_slave_tx_buffer[len], PPP_SOF, send_len - len);
}
struct header *head = (void *)sdio_slave_tx_buffer;
head->magic = PPP_SOF;
head->channel = channel;
head->size = len;
memcpy(sdio_slave_tx_buffer + sizeof(struct header), buffer, len);
size_t send_len = SDIO_ALIGN(len + sizeof(struct header));
ESP_LOG_BUFFER_HEXDUMP(TAG, sdio_slave_tx_buffer, send_len, ESP_LOG_VERBOSE);
esp_err_t ret = sdio_slave_transmit(sdio_slave_tx_buffer, send_len);
if (ret != ESP_OK) {
@@ -44,6 +45,18 @@ esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
return ESP_OK;
}
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
{
return eppp_sdio_host_tx_generic(0, buffer, len);
}
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
return eppp_sdio_host_tx_generic(channel, buffer, len);
}
#endif
static esp_err_t slave_reset(void)
{
esp_err_t ret = ESP_OK;
@@ -82,7 +95,29 @@ esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif)
if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) {
again:
ptr = sdio_slave_recv_get_buf(handle, &length);
esp_netif_receive(netif, ptr, length, NULL);
struct header *head = (void *)ptr;
if (head->magic != PPP_SOF) {
ESP_LOGE(TAG, "invalid magic %x", head->magic);
return ESP_FAIL;
}
if (head->channel > NR_OF_CHANNELS) {
ESP_LOGE(TAG, "invalid channel %x", head->channel);
return ESP_FAIL;
}
if (head->size > SDIO_PAYLOAD || head->size > length) {
ESP_LOGE(TAG, "invalid size %x", head->size);
return ESP_FAIL;
}
if (head->channel == 0) {
esp_netif_receive(netif, ptr + sizeof(struct header), head->size, NULL);
} else {
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
struct eppp_handle *h = esp_netif_get_io_driver(netif);
if (h->channel_rx) {
h->channel_rx(netif, head->channel, ptr + sizeof(struct header), head->size);
}
#endif
}
if (sdio_slave_recv_load_buf(handle) != ESP_OK) {
ESP_LOGE(TAG, "Failed to recycle packet buffer");
return ESP_FAIL;

View File

@@ -6,6 +6,7 @@
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_check.h"
@@ -24,7 +25,8 @@
#define MAX_PAYLOAD 1500
#define MIN_TRIGGER_US 20
#define SPI_HEADER_MAGIC 0x1234
#define PPP_SOF 0x7E
#define SPI_HEADER_MAGIC PPP_SOF
#define SPI_ALIGN(size) (((size) + 3U) & ~(3U))
#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6))
#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */
@@ -32,10 +34,12 @@
struct packet {
size_t len;
uint8_t *data;
int channel;
};
struct header {
uint16_t magic;
uint8_t magic;
uint8_t channel;
uint16_t size;
uint16_t next_size;
uint16_t check;
@@ -65,12 +69,10 @@ struct eppp_spi {
esp_timer_handle_t timer;
};
static esp_err_t transmit(void *h, void *buffer, size_t len)
static esp_err_t transmit_generic(struct eppp_spi *handle, int channel, void *buffer, size_t len)
{
struct eppp_handle *common = h;
struct eppp_spi *handle = __containerof(common, struct eppp_spi, parent);;
#if CONFIG_EPPP_LINK_DEVICE_SPI
struct packet buf = { };
struct packet buf = { .channel = channel };
uint8_t *current_buffer = buffer;
size_t remaining = len;
do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform
@@ -100,14 +102,25 @@ static esp_err_t transmit(void *h, void *buffer, size_t len)
}
gpio_set_level(handle->gpio_intr, 0);
}
#elif CONFIG_EPPP_LINK_DEVICE_UART
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_WARN);
uart_write_bytes(handle->uart_port, buffer, len);
#endif // DEVICE UART or SPI
return ESP_OK;
}
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
struct eppp_handle *handle = h;
struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);;
return transmit_generic(spi_handle, 0, buffer, len);
}
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);;
return transmit_generic(spi_handle, channel, buffer, len);
}
#endif
static void IRAM_ATTR timer_callback(void *arg)
{
struct eppp_spi *h = arg;
@@ -195,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;
@@ -339,6 +354,7 @@ esp_err_t eppp_perform(esp_netif_t *netif)
if (h->outbound.len <= h->transaction_size && allow_test_tx == false) {
// sending outbound
head->size = h->outbound.len;
head->channel = h->outbound.channel;
if (h->outbound.len > 0) {
memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len);
free(h->outbound.data);
@@ -355,6 +371,7 @@ esp_err_t eppp_perform(esp_netif_t *netif)
} else {
// outbound is bigger, need to transmit in another transaction (keep this empty)
head->size = 0;
head->channel = 0;
}
next_tx_size = head->next_size = h->outbound.len;
head->magic = SPI_HEADER_MAGIC;
@@ -367,17 +384,25 @@ esp_err_t eppp_perform(esp_netif_t *netif)
}
head = (void *)in_buf;
uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t));
if (check != head->check || head->magic != SPI_HEADER_MAGIC) {
if (check != head->check || head->magic != SPI_HEADER_MAGIC || head->channel > NR_OF_CHANNELS) {
h->transaction_size = 0; // need to start with HEADER only transaction
if (allow_test_tx) {
return ESP_OK;
}
ESP_LOGE(TAG, "Wrong checksum or magic");
ESP_LOGE(TAG, "Wrong checksum, magic, or channel: %x %x %x", check, head->magic, head->channel);
return ESP_FAIL;
}
if (head->size > 0) {
ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
if (head->channel == 0) {
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
} else {
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
if (h->parent.channel_rx) {
h->parent.channel_rx(netif, head->channel, in_buf + sizeof(struct header), head->size);
}
#endif
}
}
h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size);
return ESP_OK;
@@ -415,6 +440,9 @@ eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config)
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
struct eppp_spi *h = calloc(1, sizeof(struct eppp_spi));
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
h->parent.channel_tx = transmit_channel;
#endif
h->is_master = config->is_master;
h->parent.base.post_attach = post_attach;
h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet));
@@ -442,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

@@ -5,6 +5,13 @@
*/
#pragma once
#include "esp_netif_types.h"
#include "sdkconfig.h"
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
#define NR_OF_CHANNELS CONFIG_EPPP_LINK_NR_OF_CHANNELS
#else
#define NR_OF_CHANNELS 1
#endif
struct eppp_handle {
esp_netif_driver_base_t base;
@@ -12,6 +19,11 @@ struct eppp_handle {
bool stop;
bool exited;
bool netif_stop;
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
eppp_channel_fn_t channel_tx;
eppp_channel_fn_t channel_rx;
void* context;
#endif
};
esp_err_t eppp_check_connection(esp_netif_t *netif);

View File

@@ -31,19 +31,19 @@ struct eppp_uart {
struct header {
uint8_t magic;
uint8_t channel;
uint8_t check;
uint16_t size;
} __attribute__((packed));
static esp_err_t transmit(void *h, void *buffer, size_t len)
static esp_err_t transmit_generic(struct eppp_uart *handle, int channel, void *buffer, size_t len)
{
struct eppp_handle *common = h;
struct eppp_uart *handle = __containerof(common, struct eppp_uart, parent);
#ifndef CONFIG_EPPP_LINK_USES_PPP
static uint8_t out_buf[MAX_PACKET_SIZE] = {};
struct header *head = (void *)out_buf;
head->magic = HEADER_MAGIC;
head->check = 0;
head->channel = channel;
head->size = len;
head->check = (0xFF & len) ^ (len >> 8);
memcpy(out_buf + sizeof(struct header), buffer, len);
@@ -56,6 +56,22 @@ static esp_err_t transmit(void *h, void *buffer, size_t len)
return ESP_OK;
}
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
struct eppp_handle *handle = h;
struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent);
return transmit_generic(uart_handle, 0, buffer, len);
}
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
{
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent);
return transmit_generic(uart_handle, channel, buffer, len);
}
#endif
static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *config)
{
h->uart_port = config->port;
@@ -64,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;
}
@@ -122,6 +138,7 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava
// Check if we have the complete packet
uint16_t payload_size = head->size;
int channel = head->channel;
size_t total_packet_size = sizeof(struct header) + payload_size;
if (payload_size > MAX_PAYLOAD) {
@@ -136,7 +153,17 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava
}
// Got a complete packet, pass it to network
esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL);
if (channel == 0) {
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);
}
#endif
}
// Advance start pointer past this packet
buf_start += total_packet_size;
@@ -237,10 +264,14 @@ eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config)
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
struct eppp_uart *h = calloc(1, sizeof(struct eppp_uart));
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
h->parent.channel_tx = transmit_channel;
#endif
h->parent.base.post_attach = post_attach;
ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART");
return &h->parent;
err:
free(h);
return NULL;
}

View File

@@ -1,2 +1,6 @@
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
set(wifi_over_channels channel_wifi_station.c)
endif()
idf_component_register(SRCS app_main.c register_iperf.c
INCLUDE_DIRS ".")
${wifi_over_channels}
INCLUDE_DIRS ".")

View File

@@ -106,4 +106,14 @@ menu "Example Configuration"
help
Baudrate used by the PPP over UART
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
bool "Use WiFi over EPPP channel"
default n
depends on EPPP_LINK_CHANNELS_SUPPORT && ESP_WIFI_REMOTE_ENABLED
help
Enable this option to use WiFi over EPPP channel.
If this option is enabled, the example will only start the Wi-Fi driver,
but the Wi-Fi netif will reside on client's end and will channel
the Rx and Tx data via EPPP channels.
endmenu

View File

@@ -14,6 +14,7 @@
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "mqtt_client.h"
#include "console_ping.h"
@@ -88,6 +89,7 @@ static void mqtt_app_start(void)
}
#endif // MQTT
void station_over_eppp_channel(void *arg);
void app_main(void)
{
@@ -156,6 +158,9 @@ void app_main(void)
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
station_over_eppp_channel(eppp_netif);
#endif
#if CONFIG_EXAMPLE_MQTT
mqtt_app_start();
#endif

View File

@@ -0,0 +1,185 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_wifi_remote.h"
#define CHAT_CHANNEL 1
#define WIFI_CHANNEL 2
typedef enum {
UNKNOWN,
HELLO,
START,
ERROR,
} state_t;
typedef struct context {
eppp_channel_fn_t transmit;
EventGroupHandle_t flags;
state_t state;
esp_netif_t *eppp;
} context_t;
#define HELLO_BIT BIT0
#define START_BIT BIT1
#define CONNECT_BIT BIT2
#define SERVER_UP_BIT BIT3
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | SERVER_UP_BIT)
static uint8_t s_wifi_mac_addr[6] = { 0 };
static const char *TAG = "eppp_host_example_with_channels";
esp_netif_t* esp_wifi_remote_create_default_sta(void);
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "IP event_handler: event_base=%s event_id=%d", event_base, event_id);
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
}
}
esp_err_t esp_wifi_remote_get_mac(wifi_interface_t ifx, uint8_t mac[6])
{
if (ifx != WIFI_IF_STA) {
return ESP_ERR_INVALID_STATE;
}
for (int i = 0; i < sizeof(s_wifi_mac_addr); i++) {
if (s_wifi_mac_addr[i] == 0) {
return ESP_ERR_INVALID_STATE;
}
}
memcpy(mac, s_wifi_mac_addr, sizeof(s_wifi_mac_addr));
return ESP_OK;
}
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
{
context_t *ctx = eppp_get_context(netif);
if (nr == CHAT_CHANNEL) {
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
const char hello[] = "Hello client";
const char mac[] = "MAC: ";
const char connected[] = "Connected";
const char server_up[] = "Server up";
size_t mac_len = 5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */;
if (len == sizeof(server_up) && memcmp(buffer, server_up, len) == 0) {
if (ctx->state == UNKNOWN) {
ESP_LOGI(TAG, "Server is up");
ctx->state = HELLO;
} else {
ESP_LOGE(TAG, "Received server up in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
xEventGroupSetBits(ctx->flags, SERVER_UP_BIT);
} else if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, HELLO_BIT);
} else {
ESP_LOGE(TAG, "Received hello in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
} else if (len == mac_len && memcmp(buffer, mac, 5) == 0) {
if (ctx->state == HELLO) {
uint8_t mac_addr[6];
sscanf((char *)buffer + 5, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8,
&mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]);
ESP_LOGI(TAG, "Parsed MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
memcpy(s_wifi_mac_addr, mac_addr, sizeof(s_wifi_mac_addr));
xEventGroupSetBits(ctx->flags, START_BIT);
} else {
ESP_LOGE(TAG, "Received MAC in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
} else if (len == sizeof(connected) && memcmp(buffer, connected, len) == 0) {
if (ctx->state == START) {
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
} else {
ESP_LOGE(TAG, "Received connected in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
}
} else if (nr == WIFI_CHANNEL) {
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
return esp_wifi_remote_channel_rx(ctx->eppp, buffer, NULL, len);
} else {
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t wifi_transmit(void *h, void *buffer, size_t len)
{
esp_netif_t *eppp = (esp_netif_t *)h;
context_t *ctx = eppp_get_context(eppp);
ESP_LOG_BUFFER_HEXDUMP("wifi-transmit", buffer, len, ESP_LOG_VERBOSE);
return ctx->transmit(eppp, WIFI_CHANNEL, buffer, len);
}
void esp_netif_destroy_wifi_remote(void *esp_netif);
void station_over_eppp_channel(void *arg)
{
__attribute__((unused)) esp_err_t ret;
esp_netif_t *wifi = NULL;
context_t ctx = {
.transmit = NULL,
.flags = NULL,
.state = UNKNOWN,
.eppp = (esp_netif_t *)arg
};
ESP_GOTO_ON_FALSE(ctx.eppp != NULL, ESP_FAIL, err, TAG, "Incorrect EPPP netif");
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
ESP_GOTO_ON_ERROR(esp_wifi_remote_channel_set(WIFI_IF_STA, ctx.eppp, wifi_transmit), err, TAG, "Failed to set wifi channel tx function");
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, &ctx);
while (1) {
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
if (bits & HELLO_BIT) {
ESP_LOGI(TAG, "Hello done");
if (wifi == NULL) {
wifi = esp_wifi_remote_create_default_sta();
}
const char command[] = "Get MAC";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)command, sizeof(command));
} else if (bits & START_BIT) {
ctx.state = START;
ESP_LOGI(TAG, "Starting WIFI");
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_START, NULL, 0, 0);
} else if (bits & CONNECT_BIT) {
ESP_LOGI(TAG, "WIFI connected");
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_CONNECTED, NULL, 0, 0);
} else if ((bits & SERVER_UP_BIT) == SERVER_UP_BIT || ctx.state != START) {
if (ctx.state == ERROR) {
esp_netif_destroy_wifi_remote(wifi);
wifi = NULL;
ESP_LOGI(TAG, "WiFi netif has been destroyed");
}
const char hello[] = "Hello server";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
ctx.state = HELLO;
}
}
err:
vTaskDelete(NULL);
}

View File

@@ -1,7 +1,11 @@
dependencies:
espressif/iperf-cmd: "^0.1.1"
espressif/iperf-cmd: ^0.1.1
espressif/eppp_link:
version: "*"
override_path: "../../.."
version: '*'
override_path: ../../..
console_cmd_ping:
version: "*"
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

@@ -1,2 +1,6 @@
idf_component_register(SRCS "eppp_slave.c"
INCLUDE_DIRS ".")
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
set(wifi_over_channels channel_wifi_station.c)
endif()
idf_component_register(SRCS eppp_slave.c
${wifi_over_channels}
INCLUDE_DIRS ".")

View File

@@ -98,4 +98,14 @@ menu "Example Configuration"
help
Baudrate used by the PPP over UART
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
bool "Use WiFi over EPPP channel"
default n
depends on EPPP_LINK_CHANNELS_SUPPORT
help
Enable this option to use WiFi over EPPP channel.
If this option is enabled, the example will only start the Wi-Fi driver,
but the Wi-Fi netif will reside on client's end and will channel
the Rx and Tx data via EPPP channels.
endmenu

View File

@@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_private/wifi.h"
#define CHAT_CHANNEL 1
#define WIFI_CHANNEL 2
typedef enum {
UNKNOWN,
HELLO,
START,
ERROR,
} state_t;
typedef struct context {
eppp_channel_fn_t transmit;
EventGroupHandle_t flags;
state_t state;
esp_netif_t *eppp;
} context_t;
#define HELLO_BIT BIT0
#define START_BIT BIT1
#define CONNECT_BIT BIT2
#define DISCONNECT_BIT BIT3
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | DISCONNECT_BIT)
static const char *TAG = "eppp_host_example_with_channels";
static context_t *s_eppp_channel_ctx = NULL;
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
{
context_t *ctx = eppp_get_context(netif);
if (nr == CHAT_CHANNEL) {
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
const char hello[] = "Hello server";
const char mac[] = "Get MAC";
if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, HELLO_BIT);
} else {
ctx->state = ERROR;
}
} else if (len == sizeof(mac) && memcmp(buffer, mac, 5) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, START_BIT);
} else {
ctx->state = ERROR;
}
}
} else if (nr == WIFI_CHANNEL) {
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
return esp_wifi_internal_tx(WIFI_IF_STA, buffer, len);
} else {
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t wifi_receive(void *buffer, uint16_t len, void *eb)
{
s_eppp_channel_ctx->transmit(s_eppp_channel_ctx->eppp, WIFI_CHANNEL, buffer, len);
esp_wifi_internal_free_rx_buffer(eb);
return ESP_OK;
}
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
context_t *ctx = arg;
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%d", event_base, event_id);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "WIFI start event");
esp_wifi_connect();
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "connect to the AP fail");
xEventGroupSetBits(ctx->flags, DISCONNECT_BIT);
}
}
static void init_wifi_driver(context_t *ctx)
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
event_handler, ctx));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
}
void station_over_eppp_channel(void *arg)
{
__attribute__((unused)) esp_err_t ret;
context_t ctx = {
.transmit = NULL,
.flags = NULL,
.state = UNKNOWN,
.eppp = (esp_netif_t *)arg
};
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
init_wifi_driver(&ctx);
while (1) {
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
if (bits & HELLO_BIT) {
ESP_LOGI(TAG, "Hello from client received");
const char hello[] = "Hello client";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
} else if (bits & START_BIT) {
ctx.state = START;
ESP_LOGI(TAG, "Starting WIFI");
uint8_t mac[6];
if (esp_wifi_get_mac(WIFI_IF_STA, mac) != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_get_mac failed");
ctx.state = ERROR;
continue;
}
char mac_data[5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */];
sprintf(mac_data, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
ESP_LOGI(TAG, "Sending MAC: %.*s", (int)sizeof(mac_data), mac_data);
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)mac_data, sizeof(mac_data));
ret = esp_wifi_start();
ESP_LOGI(TAG, "WIFI start result: %d", ret);
s_eppp_channel_ctx = &ctx;
esp_wifi_internal_reg_rxcb(WIFI_IF_STA, wifi_receive);
} else if (bits & CONNECT_BIT) {
ESP_LOGI(TAG, "WIFI connected");
const char connected[] = "Connected";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)connected, sizeof(connected));
} else if (bits & DISCONNECT_BIT) {
const char disconnected[] = "Disconnected";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)disconnected, sizeof(disconnected));
} else if (ctx.state != START) {
ctx.state = HELLO;
const char up[] = "Server up";
esp_wifi_disconnect();
esp_wifi_stop();
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)up, sizeof(up));
}
}
err:
vTaskDelete(NULL);
}

View File

@@ -11,12 +11,14 @@
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_check.h"
#include "nvs_flash.h"
#include "eppp_link.h"
#include "inttypes.h"
static const char *TAG = "eppp_slave";
#if CONFIG_SOC_WIFI_SUPPORTED
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
@@ -27,12 +29,13 @@ static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%" PRIi32, event_base, event_id);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "WIFI start event");
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
@@ -107,12 +110,15 @@ void init_network_interface(void)
}
#else
// If the SoC does not have WiFi capabilities, we can initialize a different network interface, this function is a placeholder for that purpose.
// This function is also a no-op if EXAMPLE_WIFI_OVER_EPPP_CHANNEL==1, since the Wi-Fi network interface will live on the other peer (on the host side).
void init_network_interface(void)
{
// placeholder to initialize any other network interface if WiFi is not available
}
#endif // SoC WiFi capable chip
#endif // SoC WiFi capable chip || WiFi over EPPP channel
void station_over_eppp_channel(void *arg);
void app_main(void)
{
@@ -153,5 +159,9 @@ void app_main(void)
ESP_LOGE(TAG, "Failed to setup connection");
return ;
}
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
station_over_eppp_channel(eppp_netif);
#else
ESP_ERROR_CHECK(esp_netif_napt_enable(eppp_netif));
#endif // CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
}

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

@@ -0,0 +1 @@
CONFIG_LWIP_IPV6=n

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: 0.3.0
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

@@ -5,6 +5,10 @@
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1)
#define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2)
@@ -30,6 +34,9 @@
.rx_io = 26, \
.queue_size = 16, \
.rx_buffer_size = 1024, \
.rts_io = -1, \
.cts_io = -1, \
.flow_control = 0, \
}, \
#define EPPP_DEFAULT_SDIO_CONFIG() \
@@ -110,7 +117,6 @@ typedef enum eppp_transport {
typedef struct eppp_config_t {
eppp_transport_t transport;
struct eppp_config_spi_s {
int host;
bool is_master;
@@ -132,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 {
@@ -203,3 +212,15 @@ void eppp_netif_deinit(esp_netif_t *netif);
* - ESP_ERR_TIMEOUT indicates that the operation was requested to stop
*/
esp_err_t eppp_perform(esp_netif_t *netif);
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len);
esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context);
void* eppp_get_context(esp_netif_t *netif);
#endif // CONFIG_EPPP_LINK_CHANNELS_SUPPORT
#ifdef __cplusplus
}
#endif

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

@@ -177,6 +177,9 @@ esp_err_t esp_dns_http_event_handler(esp_http_client_event_t *evt)
case HTTP_EVENT_REDIRECT:
ESP_LOGE(TAG, "HTTP_EVENT_REDIRECT: Not supported(%d)", esp_http_client_get_status_code(evt->client));
break;
default:
ESP_LOGD(TAG, "Other HTTP event: %d", evt->event_id);
break;
}
return ESP_OK;
}

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

@@ -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;

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