Compare commits

..

128 Commits

Author SHA1 Message Date
b992ef6914 Merge pull request #346 from gabsuren/ci/update_websocket_1.1.0
bump(websocket): 1.0.1 -> 1.1.0
2023-08-28 12:52:37 +04:00
9cc594afaa Merge pull request #312 from david-cermak/fix/minor_modem_refactor
fix(modem): Refactor of DTE callbacks (and other fixes)
2023-08-28 10:49:18 +02:00
4cf9e505e1 feat(modem): Add factory method for simple creation of custom DCEs
This allows calling directly:
auto dce = dce_factory::Factory::create_unique_dce_from<SIM7600>(&dce_config, uart_dte, esp_netif);
instead of:
auto dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif);
Which is very useful when adding a custom module, so we won't need to
update factory layers and add the new device to enums, etc.
To add a new module, you just:
1) Define the module class NewModule: public GenericModule { };
2) Implement the specific commands:
command_result do_work_new_module(CommandableIf *t, params) {}
3) Connect 1) and 2)
command_result NewModule::do_work_new_module(params...)
{
    return dce_commands::do_work_new_module(dte.get(), params...);
}
2023-08-28 10:27:16 +02:00
c8c05075fb fix(modem): Fix netif data race causing PPP startup delays
Removes PPP_started signal on reception, since lwip handles data
reception if the netif is up/down (which we correctly set in start()
stop() methods)

Closes https://github.com/espressif/esp-protocols/issues/308
2023-08-28 10:27:16 +02:00
cb6e03ac62 fix(modem): Added support for inflatable buffer
As a configurable option, if disabled we report an error.

Closes https://github.com/espressif/esp-protocols/issues/272
2023-08-28 10:27:10 +02:00
2e42b9bb49 Merge pull request #347 from gabsuren/fix/websocket_fix_ci
fix(websocket): fix of websocket coverage report generation
2023-08-28 11:30:20 +04:00
86f7a8bbe3 Merge pull request #343 from victorrar/dev/IDFGH-10845
Fix LoadProhibited after failed CMUX initialization
2023-08-28 09:14:45 +02:00
7b5a41deea fix(websocket): fix of websocket coverage report generation 2023-08-22 19:42:14 +04:00
38d50eede0 bump(websocket): 1.0.1 -> 1.1.0
1.1.0
Features
- Added linux port for websocket (a22391a)
Bug Fixes
- added idf_component.yml for examples (d273e10)
2023-08-22 14:26:12 +04:00
27303d28b2 Merge pull request #345 from gabsuren/fix/example_component_manager
fix(common): added idf_component.yml for examples
2023-08-22 14:04:32 +04:00
d273e10819 fix(common): added idf_component.yml for examples 2023-08-22 13:55:10 +04:00
60c87ddf26 fix(modem): Fix LoadProhibited after failed CMUX initialization (IDFGH-10845) 2023-08-18 12:44:37 +03:00
a95891e211 fix(common): Bump espressif/check-copyright version for PyYAML fix 2023-08-18 12:42:23 +03:00
411dced3e2 Merge pull request #334 from gabsuren/mdns/fix_null_pointer
fix(mdns): added guard check for null pointer (IDF-7449)
2023-08-17 21:01:52 +04:00
42fe60828f Merge pull request #342 from gabsuren/ci/fix_workflow_run
ci(common): start coverage publish action only against master branch
2023-08-16 11:36:30 +04:00
36899f1135 Merge pull request #340 from gabsuren/docs/updated_doc
docs(common): updated README regarding esp-idf integration
2023-08-15 17:32:55 +04:00
0714e100ee ci(common): start coverage publish action only against master branch 2023-08-12 17:54:55 +04:00
cbfee945a0 docs(common): updated README regarding esp-idf integration 2023-08-11 15:46:05 +04:00
71bb461ed8 fix(mdns): added guard check for null pointer 2023-08-03 13:53:17 +04:00
5143f5ac01 Merge pull request #310 from gabsuren/feat/websocket_linux_port_component
feat(websocket): Added linux port for websocket (IDF-7097)
2023-07-27 11:07:06 +04:00
7e83741615 Merge pull request #330 from gabsuren/websocket/release_1.0.1
bump(websocket): 1.0.0 -> 1.0.1
2023-07-25 14:29:08 +04:00
b880fc0367 bump(websocket): 1.0.0 -> 1.0.1
1.0.1
Bug Fixes
- esp_websocket_client client allow sending 0 byte packets (b5177cb)
- Cleaned up printf/format warnings (-Wno-format) (e085826)
- Added unit tests to CI + minor fix to pass it (c974c14)
- Reintroduce missing CHANGELOGs (200cbb3, #235)
Updated
- docs(common): updated component and example links (f48d9b2)
- docs(common): improving documentation (ca3fce0)
- Fix weird error message spacings (8bb207e)
2023-07-25 13:55:59 +04:00
f6d5186e5b Merge pull request #325 from gabsuren/docs/link_fix_esp_modem
docs(modem): Update Doxyfile to generate correct docs links
2023-07-19 12:05:56 +04:00
8eb3a0feea Merge pull request #324 from gytxxsy/feature/set_ation_queue_len_in_kconfig
feat(mdns): Allow setting length of mDNS action queue in menuconfig
2023-07-18 13:40:36 +02:00
28cd898eca feat(mdns): Allow setting length of mDNS action queue in menuconfig 2023-07-18 19:09:16 +08:00
a22391ae2c feat(websocket): Added linux port for websocket 2023-07-18 14:18:39 +04:00
f5a0d5fb40 docs(modem): Update Doxyfile to generate correct docs links 2023-07-17 11:18:51 +04:00
ecc465daa3 Merge pull request #321 from gabsuren/mdns/release_1.2.0
bump(mdns): 1.1.0 -> 1.2.0
2023-07-04 12:24:13 +04:00
181b6e4013 bump(mdns): 1.1.0 -> 1.2.0
1.2.0
Features
- add an API for setting address to a delegated host (ddc3eb6)
- Add support for lwip build under linux (588465d)
- Allow for adding a delegated host with no address (c562461)
- Add APIs for looking up self hosted services and getting the self hostname (f0df12d)
Bug Fixes
- Refactor freertos linux compat layers (79a0e57)
- Fix delegated service PTR response (cab0e1d)
- Added unit tests to CI + minor fix to pass it (c974c14)
Updated
- docs: update documentation links (4de5298)
2023-07-04 11:56:33 +04:00
40e3875f76 Merge pull request #315 from zwx1995esp/feature/add_delegate_host_addr_set
feat(mdns): add an API for setting address to a delegated host
2023-07-04 10:54:35 +04:00
ddc3eb62d0 feat(mdns): add an API for setting address to a delegated host 2023-06-29 21:57:39 +08:00
3625889049 Merge pull request #286 from david-cermak/feat/example_multiple_netifs
feat(examples): Add multiple netif demo: eth+wifi+PPP
2023-06-28 17:49:32 +02:00
2bd6163ec8 ci(common): Introduce global warning list file 2023-06-28 16:49:37 +02:00
f1eb46580e feat(examples): Add build/host tests to examples 2023-06-28 12:59:48 +02:00
1c20328dcf feat(examples): Support simple PPP connect in multinet example 2023-06-28 12:59:44 +02:00
6de22f3893 Merge pull request #314 from david-cermak/fix/issue_template
Update issue template with correct docs links
2023-06-27 17:40:36 +02:00
e280b3b541 docs(common): Update issue template with correct docs links 2023-06-26 10:33:16 +02:00
00d7c40848 feat(examples): Add multiple netif demo: eth+wifi+PPP 2023-06-22 11:49:59 +02:00
4f1769ec71 Merge pull request #311 from david-cermak/fix/modem_known_issues
docs(modem): Add known issues section to docs
2023-06-22 10:57:54 +02:00
20dd910b3a bump(modem): 1.0.0 -> 1.0.1
1.0.1
Bug Fixes
- Support UART clean build with IDF v5.2 (e620eb5)
- enable code coverage publishing to github pages (4910e89)
- fix esp_modem build issue (ab94566)
- Example to use 1.0.0 (afb6930)
- Changelog to correctly pick references (423e965)
Updated
- docs(esp_modem): updated documents to show missed topics (0534853)
- docs(common): improving documentation (ca3fce0)
2023-06-22 09:50:23 +02:00
3635e2fabc docs(modem): Add known issues section to docs
Also updates self-hosted runner tag (from runner name to test scope
related tag)
2023-06-21 10:43:27 +02:00
84035d8f3b Merge pull request #265 from david-cermak/feat/linux_port_component
examples: Add simple mqtt client demo running on linux
2023-06-20 16:23:15 +02:00
e620eb5fb1 fix(modem): Support UART clean build with IDF v5.2 2023-06-20 15:56:57 +02:00
7d4755f119 fix(common): Improve linux port 2023-06-20 15:15:37 +02:00
588465d9db feat(examples): Add support for lwip build under linux 2023-06-20 15:15:37 +02:00
c443326a34 feat(examples): Add example of using mqtt client on linux 2023-06-20 15:15:37 +02:00
79a0e57ca1 fix(mdns): Refactor freertos linux compat layers
Move it to a separate component that could be shared by multiple network
libs.
2023-06-20 15:15:37 +02:00
68392f0ba9 Merge pull request #301 from zwx1995esp/fix/delegated_service_PTR_response
fix(mdns): Fix delegated service PTR response (IDFGH-10237)
2023-06-01 23:38:56 +02:00
a67cbbcab9 Merge pull request #278 from gabsuren/fix/websocket_client_zero_byte
fix(websocket): esp_websocket_client client allow sending 0 byte packets (IDFGH-9316)
2023-06-01 20:12:43 +04:00
b5177cb23a fix(websocket): esp_websocket_client client allow sending 0 byte packets 2023-06-01 15:32:18 +04:00
cab0e1d10e fix(mdns): Fix delegated service PTR response 2023-06-01 19:01:54 +08:00
fbc79a846b Merge pull request #302 from gytxxsy/feature/allow_add_host_without_addr
mdns: allow for adding a delegated host with no address (IDFGH-10246)
2023-05-30 22:10:01 +02:00
a50f91f422 Merge pull request #296 from gabsuren/fix/enable_coverage_modem
fix(ci): enable code coverage publishing to github pages
2023-05-29 21:51:49 +04:00
4910e89249 fix(ci): enable code coverage publishing to github pages 2023-05-26 13:46:52 +04:00
c562461711 feat(mdns): Allow for adding a delegated host with no address 2023-05-26 14:03:22 +08:00
8b9c957fe0 Merge pull request #295 from wqx6/mdns/lookup_selfhosted_services
mdns: Add APIs for looking up self hosted services and getting the self hostname (IDFGH-10130)
2023-05-18 07:15:59 +02:00
f0df12dad3 feat(mdns): Add APIs for looking up self hosted services and getting the self hostname 2023-05-18 12:25:50 +08:00
9637517192 Merge pull request #298 from david-cermak/bugfix/improve_ci
fix(websocket): Add unit tests to the workflow
2023-05-17 16:07:33 +02:00
e085826dbb fix(websocket): Cleaned up printf/format warnings (-Wno-format) 2023-05-17 07:35:21 +02:00
c974c14220 fix(websocket): Added unit tests to CI + minor fix to pass it 2023-05-17 07:14:48 +02:00
247baeed22 Merge pull request #297 from david-cermak/bugfix/improve_ci
websocket(ci): Add IDF v5.1 in IDF tests for websockets
2023-05-16 16:17:04 +02:00
613d67d1cf fix(ci): Add IDF v5.1 in IDF tests for websockets 2023-05-16 12:26:33 +02:00
441f79022e Merge pull request #293 from gabsuren/docs/update_modified_links
docs: update documentation links
2023-05-15 11:20:42 +04:00
4de52981cb docs: update documentation links 2023-05-12 12:21:55 +04:00
ef1c5eb28a Merge pull request #292 from david-cermak/fix/mdns_host_test
fix(mdns): Run host test against v5.1 instead of latest IDF
2023-05-10 10:22:58 +02:00
9a3aa1d23f Merge pull request #291 from david-cermak/fix/upload_component_action
ci(common): Use upload-components-ci-action workflow action
2023-05-10 10:11:25 +02:00
64b0e4ef1a fix(mdns): Run host test against v5.1 instead of latest IDF 2023-05-10 06:51:42 +02:00
fcff00740a ci(common): Use upload-components-ci-action workflow action 2023-05-09 22:18:09 +02:00
1102133458 Merge pull request #290 from david-cermak/fix/deploy_docs
ci(common): Fix build and deploy docs jobs
2023-05-09 20:11:27 +02:00
4f54c49912 ci(common): Fix build and deploy docs jobs 2023-05-09 17:24:21 +02:00
910f6ffadc Merge pull request #289 from david-cermak/fix/ci_publish
ci(common): Update component version only if tag doesn't exist
2023-05-09 15:38:14 +02:00
9836a8620f ci(common): Update component version only if tag doesn't exist
If the merged PR is a few commits behind master, the merge commit could
show changes in the version files as well. This fix checks if the
version already exists and updates the envirionment only if the tag
is not present.
2023-05-09 15:26:50 +02:00
af0ed62ecf Merge pull request #282 from gabsuren/docs/deploy_on_version_change
docs(common): deploy docs if component version get changed
2023-05-09 07:12:38 +02:00
217a96a2e4 docs(common): deploy docs if component version got changed 2023-05-05 18:21:18 +04:00
6a94e61a7e Merge pull request #288 from david-cermak/bump/mdns
bump(mdns): 1.0.9 -> 1.1.0
2023-04-28 20:36:35 +02:00
c8fe4092c4 bump(mdns): 1.0.9 -> 1.1.0
1.1.0
Features
- Decouple main module from mdns-networking (d238e93)
Bug Fixes
- Use idf-build-apps package for building mdns (1a0a41f)
- socket networking to init interfaces properly (ee9b04f)
- Removed unused internal lock from mdns_server struct (a06fb77)
- Resolve conflicts only on self hosted items (e69a9eb, #185)
- Fix memory issues reported by valgrind (0a682e7)
Updated
- docs(common): updated component and example links (f48d9b2)
- Add APIs to look up delegated services (87dcd7d)
- Fix deadly mdns crash (4fa3023)
- docs(common): improving documentation (ca3fce0)
- append all ipv6 address in mdns answer (5ed3e9a)
- test(mdns): Host tests to use IDF's esp_event_stub (537d170)
2023-04-28 19:32:47 +02:00
31187b7d17 Merge pull request #283 from espressif/esp_modem/fix_build
fix(esp_modem): fix esp_modem build issue
2023-04-27 20:31:19 +04:00
ab94566995 fix(esp_modem): fix esp_modem build issue 2023-04-27 12:25:59 +04:00
f67511c2c1 Merge pull request #275 from gabsuren/docs/esp_modem_docs
docs(esp_modem): updated documents to show missed topics
2023-04-27 10:56:12 +04:00
05348534a6 docs(esp_modem): updated documents to show missed topics 2023-04-26 16:24:30 +04:00
18ea910f02 Merge pull request #279 from david-cermak/feat/auto_tag
ci(common): Support for tagging and releasing in CI
2023-04-25 16:31:11 +02:00
62be981c92 ci(common): Fail CI if the PR to too much behind origin
And removes the docs generation/deploy part
2023-04-25 16:27:05 +02:00
6daf6c6ba6 ci(common): Support for tagging and releasing in CI 2023-04-25 13:54:44 +02:00
b0d3b41aae Merge pull request #277 from gabsuren/docs/update_links
docs(common): updated component and example links
2023-04-20 12:01:22 +04:00
f48d9b2fcf docs(common): updated component and example links 2023-04-19 11:58:05 +04:00
6c299c068b Merge pull request #274 from david-cermak/bugfix/ci_mdns_all_targets
mdns: Build all targets and releases
2023-04-18 10:38:46 +02:00
ea5d3cee29 Merge pull request #268 from wqx6/search_delegated_service
mdns: Add APIs to lookup delegated services (IDFGH-9790)
2023-04-18 10:38:27 +02:00
7330597586 Merge pull request #270 from 0xFEEDC0DE64/fix_mdns_crash
Fix deadly mdns crash (IDFGH-9800)
2023-04-18 10:37:11 +02:00
1a0a41fa2d fix(mdns): Use idf-build-apps package for building mdns 2023-04-17 13:44:18 +02:00
2f7cbd16db ci(common): Fix pre-commit manual run 2023-04-17 13:43:14 +02:00
87dcd7dc7d mdns: Add APIs to look up delegated services 2023-04-17 19:35:41 +08:00
8e5a27f4b7 docs: fixed broken link in README.md 2023-04-10 18:38:23 +04:00
6528f446bc docs: fixed broken link on README.md 2023-04-07 16:43:43 +04:00
4fa3023ea2 Fix deadly mdns crash 2023-04-05 16:13:07 +02:00
10f8200564 Merge pull request #267 from gabsuren/esp_mqtt_cxx/updated_docs_and_yml_file
docs(esp_mqtt_cxx): updated documentation and deployment *yml file
2023-04-05 11:51:28 +04:00
a547ec8147 docs(esp_mqtt_cxx): updated documentation and deployment file 2023-04-05 11:42:59 +04:00
db0e20f446 Merge pull request #256 from gabsuren/docs/documentation_improving
docs: Improving documentation (IDF-7064)
2023-03-31 12:07:09 +04:00
ca3fce003e docs(common): improving documentation
- update esp_modem to use esp_docs
- migrated docs from github pages to docs.espressif.com
2023-03-31 12:04:46 +04:00
f6ff132eb1 Merge pull request #259 from 0xFEEDC0DE64/fix_websocket_error_spacings
Fix weird error message spacings (IDFGH-9704)
2023-03-23 17:57:12 +01:00
8bb207e9bb Fix weird error message spacings 2023-03-23 11:44:51 +01:00
76047a4cc6 Merge pull request #137 from david-cermak/mdns/isolate_networking
mdns: Decouple networking from the main mdns module
2023-03-22 14:40:49 +01:00
ee9b04f598 fix(mdns): socket networking to init interfaces properly 2023-03-22 12:17:09 +01:00
d238e9311b feat(mdns): Decouple main module from mdns-networking
Removed sharing the data via the global variable `_mdns_server`, added
API `mdns_is_netif_ready()`
2023-03-22 12:16:28 +01:00
a838af46a6 Merge pull request #251 from xieqinan/feature/append-all-ipv6-address-in-mdns-answer
mdns: append all ipv6 address in mdns answer (IDFGH-9646)
2023-03-22 08:16:38 +01:00
5ed3e9a6a1 mdns: append all ipv6 address in mdns answer 2023-03-22 12:00:38 +08:00
5b3346f5c5 Merge pull request #225 from david-cermak/bugfix/mdns_wrong_name_collision
fix(mdns): Incorrect name collision
2023-03-20 17:24:47 +01:00
a06fb7714b fix(mdns): Removed unused internal lock from mdns_server struct 2023-03-20 16:55:49 +01:00
e69a9ebb3d fix(mdns): Resolve conflicts only on self hosted items
Skip solving conflicts for delegated names and delegated services

Closes https://github.com/espressif/esp-protocols/issues/185
2023-03-20 16:55:49 +01:00
2e607e8ffb Merge pull request #257 from david-cermak/fix/mdns_valgrind
fix(mdns): Fix memory issues reported by valgrind
2023-03-20 16:49:36 +01:00
0a9765626b Merge pull request #255 from david-cermak/feat/modem_cz_bump
esp_modem: version 1.0.0 (plus cz-bump config)
2023-03-20 12:33:32 +01:00
537d170f1d test(mdns): Host tests to use IDF's esp_event_stub
Also fixes one minor from esp_netif_linux implementation
2023-03-20 12:17:36 +01:00
0a682e7f12 fix(mdns): Fix memory issues reported by valgrind
* Read after the allocated area (which may lead to subsequent
mdns name corruption)
* Potentially uninit variable used in condition
2023-03-19 20:58:10 +01:00
afb69308df fix(modem): Example to use 1.0.0 2023-03-16 20:47:59 +01:00
284cdeb2ed Merge pull request #254 from lhauswald/feature/mqtt_client_certificate
MQTT: complete client authentication configuration (IDFGH-9656)
2023-03-16 20:35:23 +01:00
97d52495d4 bump(modem): 0.1.28 -> 1.0.0 2023-03-16 20:19:00 +01:00
423e965c88 fix(common): Changelog to correctly pick references
Also added .cz config for modem components
2023-03-16 20:19:00 +01:00
e762ada551 Merge pull request #156 from david-cermak/feature/modem_example_at_mqtt
feat(esp_modem): Add mqtt example in  AT-only mode
2023-03-16 20:17:32 +01:00
31d4323f53 fix(modem): AT-only example: support MQTT over TLS on BG96 2023-03-16 19:49:10 +01:00
97831c62b1 fix(modem): AT-only example: added into CI 2023-03-16 19:48:34 +01:00
ceedcfca23 fix(modem): AT-only example: initial adaptation to BG96
Plus rework a little to support implementation of multiple devices
2023-03-16 19:45:43 +01:00
7547267336 feat(modem): Add mqtt example in AT-only mode
Similar to pppos-client, but without PPP mode.
This uses standard mqtt client and a loopback service that forwards the
TCP traffic from localhost to the modem and vice-versa.
(next step is to create a dedicated tcp-transport layer for modem)
Console example uses DCE commandable to demontrate how to handle URC
2023-03-16 19:42:29 +01:00
bf114d3624 feat(modem): DTE to support sending and receiving raw data
MAJOR CHANGE: Enable DTE to redefine on_read() and write(cmd) directly
2023-03-16 19:39:13 +01:00
574738b8d3 Merge pull request #252 from tore-espressif/feature/esp_modem/usb_examples
esp_modem: Update examples for USB v1.1
2023-03-16 13:32:29 +01:00
ee09ff45e5 feat(mqtt_cxx): configure client authentication via certificate/key or secure element 2023-03-16 09:42:43 +01:00
895a72b27d Merge pull request #249 from david-cermak/feat/cz_bump
mdns: Bump config to auto-generate change log
2023-03-16 09:40:45 +01:00
bf32e452d6 fix(modem): warning in ap-2-pppos example when using lwip/napt
Latest IDF added new napt API that support enabling NAPT based on netif
pointer. Need to include "lwip/netif.h"
2023-03-16 09:26:30 +01:00
4c7720a2c1 ci(common): Run all test on push event
Added a description about the release process to the contributing guide
2023-03-16 08:56:18 +01:00
610372f5a7 esp_modem: Update examples for USB v1.1 2023-03-15 17:13:37 +01:00
227 changed files with 6321 additions and 1324 deletions

View File

@ -137,4 +137,4 @@ show_source = True
statistics = True
exclude =
components/asio/docs/conf_common.py
docs/asio/conf_common.py

View File

@ -8,7 +8,7 @@ body:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true

View File

@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: esp-protocols documentation
url: https://espressif.github.io/esp-protocols/
- name: esp-protocols documentation links
url: https://github.com/espressif/esp-protocols#readme
about: Documenation for esp-protocols components.
- name: ESP-IDF Programming Guide
url: https://docs.espressif.com/projects/esp-idf/en/latest/

View File

@ -7,7 +7,7 @@ body:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true

View File

@ -9,7 +9,7 @@ on:
jobs:
build_asio:
if: contains(github.event.pull_request.labels.*.name, 'asio')
if: contains(github.event.pull_request.labels.*.name, 'asio') || github.event_name == 'push'
name: Build
strategy:
matrix:
@ -58,7 +58,9 @@ jobs:
target_tests_asio:
# Skip running on forks since it won't have access to secrets
if: ${{ contains(github.event.pull_request.labels.*.name, 'asio') && (github.repository == 'espressif/esp-protocols') }}
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'asio') || github.event_name == 'push' )
name: Target tests
strategy:
matrix:

View File

@ -0,0 +1,48 @@
name: "examples: build/host-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_all_examples:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.1"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Build with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
build_and_run_on_host:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
name: Build and run examples on linux
strategy:
matrix:
idf_ver: ["latest"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Build with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${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
grep 'MQTT_EVENT_DATA' test.log

View File

@ -9,133 +9,48 @@ on:
jobs:
build_mdns:
if: contains(github.event.pull_request.labels.*.name, 'mdns')
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
idf_ver: ["latest", "release-v5.0"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
shell: bash
working-directory: ${{ matrix.test.path }}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
for dir in `ls -d build_*`; do
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 -m components/mdns/.build-test-rules.yml -d
# Build specific configs for test targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }}
cd components/mdns/${{ matrix.test.path }}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v3
if: ${{ matrix.idf_target }} == "esp32"
with:
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ matrix.test.path }}/artifacts.zip
name: mdns_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: components/mdns/${{ matrix.test.path }}/artifacts.zip
if-no-files-found: error
build_mdns_app:
if: contains(github.event.pull_request.labels.*.name, 'mdns')
name: Build Test Apps
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Build Environment
working-directory: components/mdns/tests/test_apps
run: |
. ${IDF_PATH}/export.sh
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
working-directory: components/mdns/tests/test_apps
run: |
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --pytest-apps
- name: Merge binaries with IDF-${{ matrix.idf_ver }}
working-directory: components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
- uses: actions/upload-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: |
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/bootloader/bootloader.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/partition_table/partition-table.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.elf
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/flasher_args.json
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.h
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.json
if-no-files-found: error
target_test_apps_mdns:
# Skip running on forks since it won't have access to secrets
if: ${{ contains(github.event.pull_request.labels.*.name, 'mdns') && (github.repository == 'espressif/esp-protocols') }}
name: Target Test Apps
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
needs: build_mdns_app
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
steps:
- name: Clear repository
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/mdns/tests/test_apps/build
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Download Test apps to target
run: |
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/mdns/tests/test_apps/build/flash_image.bin
- name: Run Example Test on target
working-directory: components/mdns/tests/test_apps
run: |
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{matrix.idf_target}}
- uses: actions/upload-artifact@v2
if: always()
with:
name: test_apps_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/mdns/tests/test_apps/*.xml
target_tests_mdns:
# Skip running on forks since it won't have access to secrets
if: ${{ contains(github.event.pull_request.labels.*.name, 'mdns') && (github.repository == 'espressif/esp-protocols') }}
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push' )
name: Target Example and Unit tests
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
needs: build_mdns
runs-on:
- self-hosted
@ -147,14 +62,14 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ matrix.test.path }}/ci/
path: components/mdns/${{ matrix.test.path }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
working-directory: ${{ matrix.test.path }}
working-directory: components/mdns/${{ matrix.test.path }}
run: |
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
@ -166,4 +81,4 @@ jobs:
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: ${{ matrix.test.path }}/*.xml
path: components/mdns/${{ matrix.test.path }}/*.xml

View File

@ -9,10 +9,10 @@ on:
jobs:
host_test_mdns:
if: contains(github.event.pull_request.labels.*.name, 'mdns')
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Host test
runs-on: ubuntu-20.04
container: espressif/idf:latest
container: espressif/idf:release-v5.1
steps:
- name: Checkout esp-protocols
@ -33,7 +33,7 @@ jobs:
cat ip.txt
build_afl_host_test_mdns:
if: contains(github.event.pull_request.labels.*.name, 'mdns')
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Build AFL host test
strategy:
matrix:

View File

@ -9,15 +9,19 @@ on:
jobs:
build_esp_modem:
if: contains(github.event.pull_request.labels.*.name, 'modem')
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v4.2", "release-v4.3", "release-v4.4", "release-v5.0"]
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
exclude:
- idf_ver: "release-v4.2"
example: simple_cmux_client
- idf_ver: "release-v4.2"
example: modem_tcp_client
- idf_ver: "release-v4.3"
example: modem_tcp_client
include:
- idf_ver: "release-v4.2"
skip_config: usb
@ -44,10 +48,10 @@ jobs:
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
cd $GITHUB_WORKSPACE/protocols
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }}
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/examples/.build-test-rules.yml
host_test_esp_modem:
if: contains(github.event.pull_request.labels.*.name, 'modem')
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Host Tests
runs-on: ubuntu-20.04
container: espressif/idf:release-v4.3
@ -84,9 +88,11 @@ jobs:
files: esp-protocols/components/esp_modem/test/host_test/junit.xml
host_test_gcov_esp_modem:
if: contains(github.event.pull_request.labels.*.name, 'modem')
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Run gcovr on esp modem host test
runs-on: ubuntu-22.04
permissions:
contents: write
container: espressif/idf:release-v4.3
env:
lwip: lwip-2.1.2
@ -98,11 +104,11 @@ jobs:
uses: actions/checkout@v3
with:
path: esp-protocols
repository: ''
persist-credentials: false
- name: Build and Test
shell: bash
run: |
apt-get update
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip
apt-get install -y rsync
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
@ -122,10 +128,9 @@ jobs:
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/*
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml
mkdir docs_gcovr
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr
touch docs_gcovr/.nojekyll
mkdir modem_coverage_report
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report
cp -rf modem_coverage_report $GITHUB_WORKSPACE
- name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
@ -138,7 +143,6 @@ jobs:
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
@ -146,28 +150,7 @@ jobs:
uses: actions/upload-artifact@v3
if: always()
with:
name: docs_gcovr
name: modem_coverage_report
path: |
${{ env.COMP_DIR }}/docs_gcovr
${{ env.COMP_DIR }}/modem_coverage_report
if-no-files-found: error
# show_report_data:
# name: Publish-Results
# if: github.ref == 'refs/heads/master' || github.repository != 'espressif/esp-protocols'
# runs-on: ubuntu-22.04
# needs: gcovr_analyzer_esp_modem
# steps:
# - name: Checkout 🛎️
# uses: actions/checkout@v3
# with:
# persist-credentials: false
# - name: Download Artifacts
# uses: actions/download-artifact@v1
# with:
# name: docs_gcovr
#
# - name: Deploy generated docs
# uses: JamesIves/github-pages-deploy-action@v4
# with:
# branch: gh-pages
# folder: 'docs_gcovr'

View File

@ -9,7 +9,7 @@ on:
jobs:
build_esp_modem_tests:
if: contains(github.event.pull_request.labels.*.name, 'modem')
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Build Target tests
strategy:
matrix:
@ -46,7 +46,9 @@ jobs:
target_tests_esp_modem:
# Skip running on forks since it won't have access to secrets
if: ${{ contains(github.event.pull_request.labels.*.name, 'modem') && (github.repository == 'espressif/esp-protocols') }}
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push' )
name: Run Target tests
strategy:
matrix:
@ -56,7 +58,7 @@ jobs:
needs: build_esp_modem_tests
runs-on:
- self-hosted
- BrnoRPI-GH006
- modem
env:
TEST_DIR: components/esp_modem/${{ matrix.test.path }}
steps:

View File

@ -9,7 +9,7 @@ on:
jobs:
build_esp_mqtt_cxx:
if: contains(github.event.pull_request.labels.*.name, 'mqtt')
if: contains(github.event.pull_request.labels.*.name, 'mqtt') || github.event_name == 'push'
name: Build
strategy:
matrix:

View File

@ -12,12 +12,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.base_ref }}
fetch-depth: 20
- name: Fetch head and base refs
# This is necessary for pre-commit to check the changes in the PR branch
# This is necessary for pre-commit to check the changes in the PR branch (and to set origin/HEAD and HEAD refs)
run: |
git fetch origin ${{ github.base_ref }}:base_ref
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_ref
git fetch origin ${{ github.event.pull_request.head.sha }}:pr_ref
git checkout pr_ref
git remote set-head origin --auto
git merge-base origin/HEAD HEAD || ( echo "Your PR is far behind origin/HEAD, please rebase" && exit 1 )
- name: Set up Python environment
uses: actions/setup-python@master
with:
@ -26,13 +31,12 @@ jobs:
run: |
pip install pre-commit
pre-commit install-hooks
pre-commit install --hook-type commit-msg --hook-type pre-push
- name: Run pre-commit and check for any changes
run: |
echo "Commits being checked:"
git log --oneline --no-decorate base_ref..pr_ref
git log --oneline --no-decorate origin/HEAD..HEAD
echo ""
if ! pre-commit run --from-ref base_ref --to-ref pr_ref --show-diff-on-failure ; then
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."

View File

@ -0,0 +1,45 @@
name: Publish coverage report to Github Pages
on:
workflow_run:
workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"]
types:
- completed
branches:
- master
jobs:
publish_github_pages:
runs-on: ubuntu-latest
if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Download Websocket Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: websocket__build-host-tests.yml
workflow_conclusion: success
name: websocket_coverage_report
path: websocket_coverage_report_artifact
- name: Download Modem Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: modem__build-host-tests.yml
workflow_conclusion: success
name: modem_coverage_report
path: modem_coverage_report_artifact
- name: Merge HTML files
run: |
echo "<html><body>" > index.html
cat modem_coverage_report_artifact/index.html >> index.html
cat websocket_coverage_report_artifact/index.html >> index.html
echo "</body></html>" >> index.html
mkdir coverage_report
mv index.html coverage_report
- name: Deploy generated docs
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: coverage_report

View File

@ -5,66 +5,89 @@ on:
branches:
- master
env:
DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_DEPLOY_URL_BASE }}
DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_DEPLOY_SERVER }}
DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_DEPLOY_SERVER_USER }}
DOCS_DEPLOY_KEY: ${{ secrets.DOCS_DEPLOY_PRIVATEKEY }}
DOCS_DEPLOY_PATH_ORIG : ${{ secrets.DOCS_DEPLOY_PATH }}
jobs:
docs_build:
name: Docs-Build-And-Upload
publish:
name: Publish Tag, Release, Docs, Component
runs-on: ubuntu-latest
# Skip running on forks since it won't have access to secrets
if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
uses: actions/checkout@v3
with:
persist-credentials: false
fetch-depth: 0
submodules: recursive
- name: Generate docs
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs
cd $GITHUB_WORKSPACE/components/esp_modem/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
cd $GITHUB_WORKSPACE/components/mdns/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
cd $GITHUB_WORKSPACE/components/asio/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/asio
cp -r html/. $GITHUB_WORKSPACE/docs/asio
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
echo '<a href="asio/index.html">ASIO</a><br>' >> index.html
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
- name: Upload components to component service
uses: espressif/github-actions/upload_components@master
token: "${{ secrets.GITHUB_TOKEN }}"
- name: Check for version update
shell: bash
run: ./ci/detect_component_bump
- name: Tag merge commit
if: env.BUMP_VERSION != ''
uses: anothrNick/github-tag-action@1.61.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CUSTOM_TAG: ${{ env.BUMP_TAG }}
- name: Create Release
if: env.BUMP_VERSION != ''
uses: softprops/action-gh-release@v1
with:
directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio"
body_path: "release_notes.md"
tag_name: ${{ env.BUMP_TAG }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate docs
shell: bash
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs==1.4.1
for comp in `ls components`; do
cd $GITHUB_WORKSPACE/docs/${comp}
if [[ "${{ env.BUMP_COMPONENT }}" == "${comp}" ]]; then
echo "Building specific version of ${comp} (${{ env.BUMP_VERSION }})"
./generate_docs ${{ env.BUMP_VERSION }}
else
echo "Building latest version of ${comp}"
./generate_docs
fi
done
- name: Deploying generated docs
shell: bash
run: |
source $GITHUB_WORKSPACE/docs/utils.sh
add_doc_server_ssh_keys $DOCS_DEPLOY_KEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
export GIT_VER=$(git describe --always)
export GITHUB_REF_NAME=latest
for comp in `ls components`; do
echo "Deploying latest of ${comp}"
export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs/${comp}
export DOCS_DEPLOY_PATH=$DOCS_DEPLOY_PATH_ORIG/${comp}
cd $GITHUB_WORKSPACE/docs/${comp}
deploy-docs
done;
# Deploy docs with version path
if [[ "${{ env.BUMP_VERSION }}" != "" ]]; then
echo "Deploying specific version of ${comp} (${{ env.BUMP_VERSION }})"
cd $GITHUB_WORKSPACE/docs/${{ env.BUMP_COMPONENT }}
export GITHUB_REF_NAME=${{ env.BUMP_VERSION }}
deploy-docs
fi
- name: Upload components to component service
uses: espressif/upload-components-ci-action@v1
with:
directories: >
components/asio;
components/esp_modem;
components/esp_mqtt_cxx;
components/esp_websocket_client;
components/mdns;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
- name: Deploy generated docs
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: docs

86
.github/workflows/run-host-tests.yml vendored Normal file
View File

@ -0,0 +1,86 @@
name: Run on host
on:
workflow_call:
inputs:
idf_version:
required: true
type: string
app_name:
type: string
required: true
app_path:
type: string
required: true
component_path:
type: string
required: true
upload_artifacts:
type: boolean
required: true
jobs:
build:
name: Build App
runs-on: ubuntu-20.04
permissions:
contents: write
container: espressif/idf:${{inputs.idf_version}}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
path: esp-protocols
- name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
rm -rf sdkconfig sdkconfig.defaults build
cp sdkconfig.ci.linux sdkconfig.defaults
idf.py build
./build/${{inputs.app_name}}.elf
- name: Build with Coverage Enabled
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
rm -rf build sdkconfig sdkconfig.defaults
cp sdkconfig.ci.coverage sdkconfig.defaults
idf.py fullclean
idf.py build
./build/${{inputs.app_name}}.elf
- name: Run Coverage
shell: bash
run: |
apt-get update && apt-get install -y python3-pip rsync
python -m pip install gcovr
cd $GITHUB_WORKSPACE/${{inputs.component_path}}
gcov `find . -name "*gcda"`
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x ${{inputs.app_name}}_coverage.xml
mkdir ${{inputs.app_name}}_coverage_report
touch ${{inputs.app_name}}_coverage_report/.nojekyll
cp index.html ${{inputs.app_name}}_coverage_report
cp -rf ${{inputs.app_name}}_coverage_report ${{inputs.app_name}}_coverage.xml $GITHUB_WORKSPACE
- name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: esp-protocols/**/${{inputs.app_name}}_coverage.xml
badge: true
fail_below_min: false
format: markdown
hide_branch_rate: false
hide_complexity: false
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
- name: Upload files to artifacts for run-target job
uses: actions/upload-artifact@v3
if: ${{inputs.upload_artifacts}}
with:
name: ${{inputs.app_name}}_coverage_report
path: |
${{inputs.component_path}}/${{inputs.app_name}}_coverage_report
if-no-files-found: error

View File

@ -0,0 +1,20 @@
name: "websocket: build/host-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
host_test_websocket:
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
uses: "./.github/workflows/run-host-tests.yml"
with:
idf_version: "latest"
app_name: "websocket"
app_path: "esp-protocols/components/esp_websocket_client/examples/linux"
component_path: "esp-protocols/components/esp_websocket_client"
upload_artifacts: true

View File

@ -9,85 +9,78 @@ on:
jobs:
build_websocket:
if: contains(github.event.pull_request.labels.*.name, 'websocket')
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["release-v5.0", "latest"]
idf_target: ["esp32"]
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/esp_websocket_client/examples
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
working-directory: ${{ env.TEST_DIR }}
run: |
. ${IDF_PATH}/export.sh
cat sdkconfig.ci >> sdkconfig.defaults
idf.py build
- name: Merge binaries
working-directory: ${{ env.TEST_DIR }}/build
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
python -m pip install idf-build-apps
python ./ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v3
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: |
${{ env.TEST_DIR }}/build/bootloader/bootloader.bin
${{ env.TEST_DIR }}/build/partition_table/partition-table.bin
${{ env.TEST_DIR }}/build/*.bin
${{ env.TEST_DIR }}/build/*.elf
${{ env.TEST_DIR }}/build/flasher_args.json
${{ env.TEST_DIR }}/build/config/sdkconfig.h
${{ env.TEST_DIR }}/build/config/sdkconfig.json
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/artifacts.zip
if-no-files-found: error
run-target-websocket:
# Skip running on forks since it won't have access to secrets
if: ${{ contains(github.event.pull_request.labels.*.name, 'websocket') && (github.repository == 'espressif/esp-protocols') }}
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push' )
name: Target test
needs: build_websocket
strategy:
fail-fast: false
matrix:
idf_ver: ["release-v5.0", "latest"]
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: components/esp_websocket_client/examples
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/build/
name: websocket_bin_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: Download Example Test to target
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
run: |
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
done
- uses: actions/upload-artifact@v3
if: always()
with:
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/*.xml
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: components/esp_websocket_client/${{ matrix.test.path }}/*.xml

View File

@ -43,7 +43,7 @@ repos:
hooks:
- id: eradicate
- repo: https://github.com/espressif/check-copyright/
rev: v1.0.1
rev: v1.0.3
hooks:
- id: check-copyright
args: ['--ignore', 'ci/check_copyright_ignore.txt', '--config', 'ci/check_copyright_config.yaml']
@ -57,12 +57,12 @@ repos:
hooks:
- id: commitizen
- id: commitizen-branch
stages: [push]
stages: [push, manual]
- repo: local
hooks:
- id: commit message scopes
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, common"
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx)\)\:)'
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx|examples)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

View File

@ -13,3 +13,19 @@ Contributions in the form of pull requests, issue reports, and feature requests
For quick merging, the contribution should be short, and concentrated on a single feature or topic. The larger the contribution is, the longer it would take to review it and merge it.
Please follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) rule when writing commit messages.
## Release process
When releasing a new component version we have to:
* Update the version number
* Update the changelog
* Create the version tag in this repository
* Deploy the component to component registry
* Update the documentation
This process is not fully automated, the first step needs to be performed manually by project maintainers running the `bump` command. Release procedure is as follows:
* Run `ci/bump [component] [version]` (version number is optional, `cz` would automatically increment it if not present)
* Check the updated `CHANGELOG.md` and the generated bump commit message
* Create a PR
Once the PR is merged, the CI job tags the merge commit, creates a new release, builds and deploys documentation and the new component to the component registry

View File

@ -1,26 +1,35 @@
# Collection of protocol components for ESP-IDF
[Documentation of esp-protocol](https://espressif.github.io/esp-protocols)
## How to use
The [ESP-Protocols](https://github.com/espressif/esp-protocols) repository contains a collection of protocol components for [ESP-IDF](https://github.com/espressif/esp-idf).
Additionally, each component is available in [IDF Component Registry](https://components.espressif.com).
Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
## Components
### esp_modem
* Brief introduction [README](components/esp_modem/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
### mDNS
* Brief introduction [README](components/mdns/README.md)
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
* Full html [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
* Full html [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html)
### esp_websocket_client
* Brief introduction [README](components/esp_websocket_client/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_websocket_client/docs/latest/index.html)
### ASIO port
* Brief introduction [README](components/asio/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/asio/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
### esp_mqtt_cxx
* Brief introduction [README](components/esp_mqtt_cxx/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_mqtt_cxx/docs/latest/index.html)

View File

@ -7,7 +7,6 @@ This file is used in CI for esp-protocols build tests
import argparse
import os
import sys
from pathlib import Path
from idf_build_apps import build_apps, find_apps, setup_logging
from idf_build_apps.constants import SUPPORTED_TARGETS
@ -24,27 +23,35 @@ if __name__ == '__main__':
default='all',
help='Build apps for given target',
)
parser.add_argument('-r', '--rules', nargs='*', default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'], help='Rules how to treat configs')
parser.add_argument('-m', '--manifests', nargs='*', default=[], help='list of manifest files')
parser.add_argument('-d', '--delete', action='store_true', help='Delete build artifacts')
parser.add_argument('-c', '--recursive', action='store_true', help='Build recursively')
parser.add_argument('-l', '--linux', action='store_true', help='Include linux build (dont check warnings)')
args = parser.parse_args()
IDF_PATH = os.environ['IDF_PATH']
print(args.paths)
# Compose the ignore warning strings from the global list and from the environment
ignore_warning_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),'ignore_build_warnings.txt')
ignore_warning = open(ignore_warning_file).read().rstrip('\n').split('\n')
if 'EXPECTED_WARNING' in os.environ:
ignore_warning += os.environ['EXPECTED_WARNING'].split('\n')
if args.linux:
SUPPORTED_TARGETS.append('linux')
ignore_warning = 'warning: ' # Ignore all common warnings on linux builds
setup_logging(2)
apps = find_apps(
args.paths,
recursive=True,
recursive=args.recursive,
target=args.target,
build_dir='build_@t_@w',
config_rules_str=[
'sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'
],
config_rules_str=args.rules,
build_log_path='build_log.txt',
size_json_path='size.json',
size_json_path='size.json' if not args.linux else None,
check_warnings=True,
preserve=True,
manifest_files=[
str(p) for p in Path('.').glob('**/.build-test-rules.yml')
],
preserve=not args.delete,
manifest_files=args.manifests,
default_build_targets=SUPPORTED_TARGETS,
manifest_rootpath='.',
)
@ -56,5 +63,5 @@ if __name__ == '__main__':
build_apps(apps,
dry_run=False,
keep_going=False,
ignore_warning_strs=os.environ['EXPECTED_WARNING']
if 'EXPECTED_WARNING' in os.environ else None))
ignore_warning_strs=ignore_warning)
)

23
ci/bump Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: bump component [version]"
exit 1;
fi
comp=$1; shift;
cd components/${comp}
if ! cz bump --dry-run; then
echo "Commitizen bump commad failed!"
exit 1;
fi
cz_bump_out=`cz bump --files-only "$@"`
commit_title=`echo "${cz_bump_out}" | head -1`
commit_body=`cat ../../release_notes.txt`
git add -u .
git commit -m $"${commit_title}
${commit_body}"

View File

@ -47,7 +47,7 @@ def main():
details = '[{}](https://github.com/espressif/esp-protocols/commit/{})'.format(commit, commit)
msg_details = git.show('-s', commit, _tty_out=False)
# check references
if any(str(msg_details) in s for s in ['esp-protocols/issues', 'BREAKING CHANGE', 'MAJOR CHANGE']):
if any(s in str(msg_details) for s in ['esp-protocols/issues', 'BREAKING CHANGE', 'MAJOR CHANGE']):
# Closes <issue>
closes = re.findall(r'Closes ([^\d]+/(\d+))', str(msg_details), re.MULTILINE)
if closes and closes[0] is not None:
@ -93,6 +93,13 @@ def main():
updated_changelog.write(orig_items)
git.add(filename)
# write the current changelog entry to a local text file (removing links, captions and extra newlines)
changelog = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', changelog)
changelog = re.sub(r'\#\#[\#\s]*(.+)', r'\1', changelog)
changelog = re.sub(r'\n\n', '\n', changelog)
with open(os.path.join(root_path, 'release_notes.txt'), 'w') as release_notes:
release_notes.write(changelog)
if __name__ == '__main__':
main()

View File

@ -29,6 +29,7 @@ DEFAULT:
# this section sets the default license for examples and unit tests of components
examples_and_unit_tests:
include:
- 'examples/**'
- 'components/**/examples/**'
- 'components/**/test/**'
- 'components/**/tests/**'
@ -57,7 +58,7 @@ slim_modem_examples:
ignore:
perform_check: no
include:
- 'components/**/docs/**'
- '**/docs/**'
- 'components/esp_modem/port/linux/**'
- 'components/asio/examples/**'
- 'components/mdns/**/esp_system_protocols_linux/**'

41
ci/detect_component_bump Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -e
if ! git show -s | grep -q '^Merge'; then
echo "Not a merge commit"
exit 0;
fi
for comp in `ls components`; do
if git log -1 -m --name-only --pretty="" | grep -q components/${comp}/idf_component.yml; then
echo "${comp}: Component version file has changed"
version=`grep version: components/${comp}/.cz.yaml`
version=${version#*version: }
tag_format=`grep tag_format: components/${comp}/.cz.yaml`
tag_format=${tag_format#*tag_format: }
eval tag=$tag_format
# check if the tag is already created
if [ $(git tag -l "$tag") ]; then
echo "${comp}: version (${tag}) already exits"
else
echo "${comp}: Component version has been updated to ${version}"
# creates release notes from the last entry (between first two "## sections")
awk '/^## \[/{a++};{if(a==1){print}}' components/${comp}/CHANGELOG.md > release_notes.md
echo "BUMP_VERSION=${version}"
echo "BUMP_COMPONENT=${comp}"
echo "BUMP_TAG=${tag}"
# export the findings to github env, so it could be used in other jobs
echo "BUMP_VERSION=${version}" >> "$GITHUB_ENV"
echo "BUMP_COMPONENT=${comp}" >> "$GITHUB_ENV"
echo "BUMP_TAG=${tag}" >> "$GITHUB_ENV"
exit 0;
fi
fi
done
echo "No changes in component version file"

View File

@ -0,0 +1 @@
DeprecationWarning: pkg_resources is deprecated as an API

View File

@ -1,6 +1,5 @@
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
INCLUDE_DIRS include
REQUIRES esp_event)
INCLUDE_DIRS include)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17

View File

@ -7,6 +7,7 @@
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <pthread.h>
void *create_tt(esp_timer_cb_t cb);
@ -37,3 +38,10 @@ esp_err_t esp_timer_delete(esp_timer_handle_t timer)
destroy_tt(timer);
return ESP_OK;
}
int64_t esp_timer_get_time(void)
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000 + spec.tv_sec * 1000000;
}

View File

@ -7,6 +7,12 @@
#include <stdbool.h>
#include <stdint.h>
#include "bsd/string.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_timer *esp_timer_handle_t;
@ -31,3 +37,9 @@ esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
esp_err_t esp_timer_delete(esp_timer_handle_t timer);
int64_t esp_timer_get_time(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,5 @@
idf_component_register(SRCS freertos_linux.c queue_unique_ptr.cpp
idf_component_register(SRCS freertos_linux.c
osal/queue.cpp osal/event_group.cpp osal/mutex.cpp
INCLUDE_DIRS include)
set(THREADS_PREFER_PTHREAD_FLAG ON)

View File

@ -0,0 +1,272 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "osal/osal_api.h"
static uint64_t s_semaphore_data = 0;
typedef enum queue_type_tag {
MUTEX_REC,
MUTEX,
SEMA,
QUEUE,
} queue_type_t;
struct generic_queue_handle {
queue_type_t type;
size_t item_size;
void *q;
};
static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint32_t len, uint32_t item_size)
{
struct generic_queue_handle *h = calloc(1, sizeof(struct generic_queue_handle));
h->item_size = len;
h->type = type;
switch (type) {
default:
case QUEUE:
case SEMA:
h->q = osal_queue_create();
break;
case MUTEX:
case MUTEX_REC:
h->q = osal_mutex_create();
break;
}
return h;
}
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
{
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
}
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
{
struct generic_queue_handle *h = xQueue;
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
}
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
{
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
}
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait)
{
struct generic_queue_handle *h = xQueue;
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
}
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
osal_mutex_give(h->q);
return pdTRUE;
}
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
osal_mutex_give(h->q);
return pdTRUE;
}
return pdFALSE;
}
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
osal_mutex_take(h->q);
return pdTRUE;
}
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
osal_mutex_take(h->q);
return pdTRUE;
}
return pdFALSE;
}
void vQueueDelete( QueueHandle_t xQueue )
{
struct generic_queue_handle *h = xQueue;
if (h->q) {
if (h->type == MUTEX || h->type == MUTEX_REC) {
osal_mutex_delete(h->q);
} else {
osal_queue_delete(h->q);
}
}
free(xQueue);
}
QueueHandle_t xSemaphoreCreateBinary(void)
{
QueueHandle_t sempaphore = xQueueCreate(1, 1);
return sempaphore;
}
QueueHandle_t xSemaphoreCreateMutex(void)
{
return (QueueHandle_t)create_generic_queue(MUTEX, 1, 1);
}
QueueHandle_t xSemaphoreCreateRecursiveMutex(void)
{
return (QueueHandle_t)create_generic_queue(MUTEX_REC, 1, 1);
}
void vTaskDelete(TaskHandle_t *task)
{
if (task == NULL) {
pthread_exit(0);
}
void *thread_rval = NULL;
pthread_join((pthread_t)task, &thread_rval);
}
void vTaskSuspend(void *task)
{
vTaskDelete(task);
}
TickType_t xTaskGetTickCount( void )
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
}
void vTaskDelay( const TickType_t xTicksToDelay )
{
usleep(xTicksToDelay * 1000);
}
void *pthread_task(void *params)
{
struct {
void *const param;
TaskFunction_t task;
bool started;
} *pthread_params = params;
void *const param = pthread_params->param;
TaskFunction_t task = pthread_params->task;
pthread_params->started = true;
task(param);
return NULL;
}
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
{
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
return pdTRUE;
}
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask)
{
pthread_t new_thread = (pthread_t)NULL;
pthread_attr_t attr;
struct {
void *const param;
TaskFunction_t task;
bool started;
} pthread_params = { .param = pvParameters, .task = pvTaskCode};
int res = pthread_attr_init(&attr);
assert(res == 0);
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
assert(res == 0);
res = pthread_create(&new_thread, &attr, pthread_task, &pthread_params);
assert(res == 0);
if (pvCreatedTask) {
*pvCreatedTask = (void *)new_thread;
}
// just wait till the task started so we can unwind params from the stack
while (pthread_params.started == false) {
usleep(1000);
}
return pdTRUE;
}
void xTaskNotifyGive(TaskHandle_t task)
{
}
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
{
return true;
}
TaskHandle_t xTaskGetCurrentTaskHandle(void)
{
return NULL;
}
EventGroupHandle_t xEventGroupCreate( void )
{
return osal_signal_create();
}
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
{
osal_signal_delete(xEventGroup);
}
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
return osal_signal_clear(xEventGroup, uxBitsToClear);
}
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
{
return osal_signal_get(xEventGroup);
}
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
{
return osal_signal_set(xEventGroup, uxBitsToSet);
}
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
{
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
}

View File

@ -10,3 +10,4 @@
#define ESP_TASK_PRIO_MAX 25
#define ESP_TASKD_EVENT_PRIO 5
#define ESP_TASKD_EVENT_STACK 1024

View File

@ -16,7 +16,9 @@
typedef void *SemaphoreHandle_t;
typedef void *QueueHandle_t;
typedef void *TaskHandle_t;
typedef void *EventGroupHandle_t;
typedef uint32_t TickType_t;
typedef TickType_t EventBits_t;
typedef void (*TaskFunction_t)( void * );
typedef unsigned int UBaseType_t;
@ -30,6 +32,5 @@ typedef int BaseType_t;
#define pdMS_TO_TICKS(tick) (tick)
uint32_t esp_get_free_heap_size(void);
uint32_t esp_random(void);
void vTaskSuspendAll(void);

View File

@ -0,0 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@ -3,11 +3,4 @@
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
//
// Created by david on 1/13/23.
//
#ifndef _QUEUE_H_
#define _QUEUE_H_
#endif //_QUEUE_H_
#pragma once

View File

@ -3,11 +3,4 @@
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
//
// Created by david on 1/13/23.
//
#ifndef _SEMAPHR_H_
#define _SEMAPHR_H_
#endif //_SEMAPHR_H_
#pragma once

View File

@ -7,6 +7,10 @@
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TaskHandle_t TaskHandle_t
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
@ -26,7 +30,7 @@ BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID);
void xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
TickType_t xTaskGetTickCount( void );
@ -35,11 +39,16 @@ void vQueueDelete( QueueHandle_t xQueue );
QueueHandle_t xSemaphoreCreateBinary(void);
QueueHandle_t xSemaphoreCreateMutex(void);
QueueHandle_t xSemaphoreCreateRecursiveMutex(void);
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue);
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask );
void vTaskDelete(TaskHandle_t *task);
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
@ -48,3 +57,27 @@ QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
void vTaskSuspend(void *task);
EventGroupHandle_t xEventGroupCreate( void );
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <mutex>
#include <condition_variable>
#include "osal_api.h"
class SignalGroup {
struct SignalGroupInternal {
std::condition_variable notify;
std::mutex m;
uint32_t flags{ 0 };
};
using SignalT = std::unique_ptr<SignalGroupInternal>;
public:
void set(uint32_t bits)
{
std::unique_lock<std::mutex> lock(event_group->m);
event_group->flags |= bits;
event_group->notify.notify_all();
}
uint32_t get()
{
return event_group->flags;
}
void clear(uint32_t bits)
{
std::unique_lock<std::mutex> lock(event_group->m);
event_group->flags &= ~bits;
event_group->notify.notify_all();
}
// waiting for all and clearing if set
bool wait(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] {
if ((flags & event_group->flags) == flags)
{
event_group->flags &= ~flags;
return true;
}
return false;
});
}
// waiting for any bit, not clearing them
bool wait_any(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; });
}
private:
SignalT event_group{std::make_unique<SignalGroupInternal>()};
};
void *osal_signal_create()
{
auto signal = new SignalGroup;
return signal;
}
void osal_signal_delete(void *s)
{
delete static_cast<SignalGroup *>(s);
}
uint32_t osal_signal_clear(void *s, uint32_t bits)
{
auto signal = static_cast<SignalGroup *>(s);
signal->clear(bits);
return signal->get();
}
uint32_t osal_signal_set(void *s, uint32_t bits)
{
auto signal = static_cast<SignalGroup *>(s);
signal->set(bits);
return signal->get();
}
uint32_t osal_signal_get(void *s)
{
auto signal = static_cast<SignalGroup *>(s);
return signal->get();
}
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout)
{
auto signal = static_cast<SignalGroup *>(s);
if (all) {
signal->wait(flags, timeout);
} else {
signal->wait_any(flags, timeout);
}
return signal->get();
}

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <queue>
#include <mutex>
#include "osal_api.h"
void *osal_mutex_create()
{
auto mut = new std::recursive_mutex();
return mut;
}
void osal_mutex_delete(void *mut)
{
delete static_cast<std::recursive_mutex *>(mut);
}
void osal_mutex_take(void *m)
{
auto mut = static_cast<std::recursive_mutex *>(m);
mut->lock();
}
void osal_mutex_give(void *m)
{
auto mut = static_cast<std::recursive_mutex *>(m);
mut->unlock();
}

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// queue api
void *osal_queue_create(void);
void osal_queue_delete(void *q);
bool osal_queue_send(void *q, uint8_t *data, size_t len);
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms);
// mutex api
void *osal_mutex_create(void);
void osal_mutex_delete(void *m);
void osal_mutex_take(void *m);
void osal_mutex_give(void *m);
// event groups
void *osal_signal_create(void);
void osal_signal_delete(void *s);
uint32_t osal_signal_clear(void *s, uint32_t bits);
uint32_t osal_signal_set(void *s, uint32_t bits);
uint32_t osal_signal_get(void *s);
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cstdint>
#include <vector>
#include <cstring>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "osal_api.h"
template <class T>
class Queue {
public:
void send(std::unique_ptr<T> t)
{
std::lock_guard<std::mutex> lock(m);
q.push(std::move(t));
c.notify_one();
}
std::unique_ptr<T> receive(std::chrono::milliseconds ms)
{
std::unique_lock<std::mutex> lock(m);
while (q.empty()) {
if (c.wait_for(lock, ms) == std::cv_status::timeout) {
return nullptr;
}
}
std::unique_ptr<T> val = std::move(q.front());
q.pop();
return val;
}
private:
std::queue<std::unique_ptr<T>> q{};
mutable std::mutex m{};
std::condition_variable c{};
};
using item_t = std::vector<uint8_t>;
void *osal_queue_create(void)
{
auto *q = new Queue<item_t>();
return q;
}
void osal_queue_delete(void *q)
{
auto *queue = static_cast<Queue<item_t> *>(q);
delete (queue);
}
bool osal_queue_send(void *q, uint8_t *data, size_t len)
{
auto v = std::make_unique<item_t>(len);
v->assign(data, data + len);
auto queue = static_cast<Queue<item_t> *>(q);
queue->send(std::move(v));
return true;
}
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms)
{
auto queue = static_cast<Queue<item_t> *>(q);
auto v = queue->receive(std::chrono::milliseconds(ms));
if (v != nullptr) {
memcpy(data, (void *)v->data(), len);
return true;
}
return false;
}

View File

@ -6,8 +6,8 @@ Asio is a cross-platform C++ library, see https://think-async.com/Asio/. It prov
## Examples
Get started with example test :example:`examples <examples/..>`:
Get started with examples [examples](https://github.com/espressif/esp-protocols/tree/master/components/asio/examples)
## Documentation
* View the full [html documentation](https://espressif.github.io/esp-protocols/asio/index.html)
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)

View File

@ -1,26 +0,0 @@
build-docs --target esp32 --language en
cp -rf _build/en/esp32/html .
rm -rf _build __pycache__
# Modifes some version and target fields of index.html
echo "<script type="text/javascript">
window.onload =(function() {
var myAnchor = document.getElementById('version-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'latest';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>" >> html/index.html

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,4 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -0,0 +1,8 @@
---
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.0.1
version_files:
- idf_component.yml

View File

@ -1,5 +1,46 @@
# Changelog
## [1.0.1](https://github.com/espressif/esp-protocols/commits/modem-v1.0.1)
### Bug Fixes
- Support UART clean build with IDF v5.2 ([e620eb5](https://github.com/espressif/esp-protocols/commit/e620eb5))
- enable code coverage publishing to github pages ([4910e89](https://github.com/espressif/esp-protocols/commit/4910e89))
- fix esp_modem build issue ([ab94566](https://github.com/espressif/esp-protocols/commit/ab94566))
- Example to use 1.0.0 ([afb6930](https://github.com/espressif/esp-protocols/commit/afb6930))
- Changelog to correctly pick references ([423e965](https://github.com/espressif/esp-protocols/commit/423e965))
### Updated
- docs(esp_modem): updated documents to show missed topics ([0534853](https://github.com/espressif/esp-protocols/commit/0534853))
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
## [1.0.0](https://github.com/espressif/esp-protocols/commits/modem-v1.0.0)
### Major changes
- Enable DTE to redefine on_read() and write(cmd) directly ([DTE to support sending and receiving raw data](https://github.com/espressif/esp-protocols/commit/bf114d3))
### Features
- Add mqtt example in AT-only mode ([7547267](https://github.com/espressif/esp-protocols/commit/7547267))
- DTE to support sending and receiving raw data ([bf114d3](https://github.com/espressif/esp-protocols/commit/bf114d3))
### Bug Fixes
- Changelog to correctly pick references ([c59e330](https://github.com/espressif/esp-protocols/commit/c59e330))
- AT-only example: support MQTT over TLS on BG96 ([31d4323](https://github.com/espressif/esp-protocols/commit/31d4323))
- AT-only example: initial adaptation to BG96 ([ceedcfc](https://github.com/espressif/esp-protocols/commit/ceedcfc))
- warning in ap-2-pppos example when using lwip/napt ([bf32e45](https://github.com/espressif/esp-protocols/commit/bf32e45))
- trigger PR jobs based on labels ([13eca12](https://github.com/espressif/esp-protocols/commit/13eca12))
- Reintroduce missing CHANGELOGs ([200cbb3](https://github.com/espressif/esp-protocols/commit/200cbb3), [#235](https://github.com/espressif/esp-protocols/issues/235))
- Use default UART CLK source ([d0814bf](https://github.com/espressif/esp-protocols/commit/d0814bf), [#241](https://github.com/espressif/esp-protocols/issues/241))
- run CI build job for all targets ([a089e0d](https://github.com/espressif/esp-protocols/commit/a089e0d))
### Updated
- Update examples for USB v1.1 ([610372f](https://github.com/espressif/esp-protocols/commit/610372f))
## [0.1.28](https://github.com/espressif/esp-protocols/commits/01c26c8)

View File

@ -47,15 +47,3 @@ if(${target} STREQUAL "linux")
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
set_target_properties(${COMPONENT_LIB} PROPERTIES COMPILE_FLAGS -Wno-format)
endif()
if(CONFIG_GCOV_ENABLED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
endif()

View File

@ -16,6 +16,17 @@ menu "esp-modem"
in command mode might come fragmented in rare cases so might need to retry
AT commands.
config ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
bool "Use inflatable buffer in DCE"
default n
help
If enabled we will process the ongoing AT command by growing the current
buffer (if we've run out the preconfigured buffer).
If disabled, we simply report a failure.
Use this if additional allocation is not a problem and you need to reliably process
all commands, usually with sporadically longer responses than the configured buffer.
Could be also used to defragment AT replies in CMUX mode if CMUX_DEFRAGMENT_PAYLOAD=n
config ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP
int "Delay in ms to wait before creating another virtual terminal"
default 0

View File

@ -17,5 +17,5 @@ Get started with one of the examples:
## Documentation
* Continue with esp-modem [brief overview](docs/README.md)
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
* Continue with esp-modem [brief overview](https://github.com/espressif/esp-protocols/tree/master/docs/esp_modem/en/README.rst)
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)

View File

@ -1,92 +0,0 @@
# ESP MODEM
This component is used to communicate with modems in the command mode (using AT commands), as well as the data mode
(over PPPoS protocol).
The modem device is modeled with a DCE (Data Communication Equipment) object, which is composed of:
* DTE (Data Terminal Equipment), which abstracts the terminal (currently only UART implemented).
* PPP Netif representing a network interface communicating with the DTE using PPP protocol.
* Module abstracting the specific device model and its commands.
```
+-----+
| DTE |--+
+-----+ | +-------+
+-->| DCE |
+-------+ | |o--- set_mode(command/data)
| Module|--->| |
+-------+ | |o--- send_commands
+->| |
+------+ | +-------+
| PPP |--+
| netif|------------------> network events
+------+
```
## Modem components
### DCE
This is the basic operational unit of the esp_modem component, abstracting a specific module in software,
which is basically configured by
* the I/O communication media (UART), defined by the DTE configuration
* the specific command library supported by the device model, defined with the module type
* network interface configuration (PPPoS config in lwip)
After the object is created, the application interaction with the DCE is in
* issuing specific commands to the modem
* switching between data and command mode
### DTE
Is an abstraction of the physical interface connected to the modem. Current implementation supports only UART
### PPP netif
Is used to attach the specific network interface to a network communication protocol used by the modem. Currently implementation supports only PPPoS protocol.
### Module
Abstraction of the specific modem device. Currently the component supports SIM800, BG96, SIM7600.
## Use cases
Users interact with the esp-modem using the DCE's interface, to basically
* Switch between command and data mode to connect to the internet via cellular network.
* Send various commands to the device (e.g. send SMS)
The applications typically register handlers for network events to receive notification on the network availability and
IP address changes.
Common use cases of the esp-modem are also listed as the examples:
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
* `examples/modem_console` -- is an example to exercise all possible module commands in a console application.
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
## Extensibility
### CMUX
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
after creating multiple virtual terminals, designating some of them solely to data mode, others solely to command mode.
### DTE's
Currently, we support only UART (and USB as a preview feature), but modern modules support other communication interfaces, such as USB, SPI.
### Other devices
Adding a new device is a must-have requirement for the esp-modem component. Different modules support different commands,
or some commands might have a different implementation. Adding a new device means to provide a new implementation
as a class derived from `GenericModule`, where we could add new commands or modify the existing ones.
## Configuration
Modem abstraction is configurable both compile-time and run-time.
### Component Kconfig
Compile-time configuration is provided using menuconfig. Please check the description for the CMUX mode configuration options.
### Runtime configuration
Is defined using standard configuration structures for `DTE` and `DCE` objects separately. Please find documentation of
* :cpp:class:`esp_modem_dte_config_t`
* :cpp:class:`esp_modem_dce_config_t`

View File

@ -1,23 +0,0 @@
# Cleanup the generated html
rm -rf html
# Cleans example and test build dirs (to reduce the component size before upload)
rm -rf ../examples/ap_to_pppos/build/ ../examples/simple_cmux_client/build/ ../examples/pppos_client/build/ ../examples/linux_modem/build/ ../examples/modem_console/build ../test/host_test/build/ ../test/target/build/
# Generate C++ API header of the DCE
cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_dce.hpp
# Generate C API header of the modem_api.h
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_api_commands.h
# RST with links to C++ API
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
# Run doxygen
doxygen
# Generate the docs
python -u -m sphinx.cmd.build -b html . html
# Cleanup the doxygen xml's and temporary headers
rm -rf xml esp_modem_api_commands.h esp_modem_dce.hpp cxx_api_links.rst

View File

@ -1,36 +0,0 @@
# Internal design
## Design decisions
* Use C++ with additional C API
* Use exceptions
- Use macro wrapper over `try-catch` blocks when exceptions off (use `abort()` if `THROW()`)
* Initializes and allocates in the constructor (might throw)
- easier code with exceptions ON, with exceptions OFF alloc/init failures are not treated as runtime error (program aborts)
- break down long initialization in constructor into more private methods
* Implements different devices using inheritance from `GenericModule`, which is the most general implementation of a common modem
- Internally uses templates with device specialization (modeled as `DCE<SpecificModule>`) which could be used as well for some special cases,
such as implantation of a minimal device (ModuleIf), add new AT commands (oOnly in compile time), or using the Module with DTE only (no DCE, no Netif) for sending AT commands without network
## DCE collaboration model
The diagram describes how the DCE class collaborates with DTE, PPP and the device abstraction
![DCE_architecture](DCE_DTE_collaboration.png)
## Terminal inheritance
Terminal is a class which can read or write data, and can handle callbacks when data are available. UART specialization
is provided implementing these method using the uart driver.
## CMUX terminal
The below diagram depicts the idea of using CMUX terminal mode using the CMuxInstance class which is a terminal
(it implements the basic read/write methods) interfacing arbitrary number of virtual terminals,
but at the same time it is also composed of CMux class, which consumes the original terminal and uses its read/write methods
to multiplex the terminal.
![CMUX Terminal](CMux_collaboration.png)

View File

@ -14,6 +14,7 @@
#include "esp_log.h"
#include "esp_idf_version.h"
#include "nvs_flash.h"
#include "lwip/netif.h"
#include "lwip/lwip_napt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"

View File

@ -1,6 +1,7 @@
# The following 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.5)
cmake_minimum_required(VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modem-console)

View File

@ -1,5 +1,6 @@
idf_component_register(SRCS "modem_console_main.cpp"
"console_helper.cpp"
"my_module_dce.cpp"
"httpget_handle.c"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash

View File

@ -16,17 +16,18 @@ menu "Example Configuration"
endchoice
choice EXAMPLE_MODEM_DEVICE
depends on EXAMPLE_SERIAL_CONFIG_UART
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
default EXAMPLE_MODEM_DEVICE_SHINY
help
Select modem device connected to the ESP DTE.
config EXAMPLE_MODEM_DEVICE_SHINY
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SHINY"
help
SHINY is a GSM/GPRS module.
It supports SHINY.
config EXAMPLE_MODEM_DEVICE_SIM800
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM800"
help
SIMCom SIM800L is a GSM/GPRS module.
@ -36,10 +37,12 @@ menu "Example Configuration"
help
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
config EXAMPLE_MODEM_DEVICE_SIM7000
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM7000"
help
SIM7000 is a Multi-Band LTE-FDD and GSM/GPRS/EDGE module.
config EXAMPLE_MODEM_DEVICE_SIM7070
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM7070"
help
SIM7070 is Multi-Band CAT M and NB IoT module.
@ -47,6 +50,11 @@ menu "Example Configuration"
bool "SIM7600"
help
SIM7600 is a Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module.
config EXAMPLE_MODEM_DEVICE_A7670
depends on EXAMPLE_SERIAL_CONFIG_USB
bool "A7670"
help
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
endchoice
config EXAMPLE_MODEM_PPP_APN

View File

@ -3,10 +3,10 @@ dependencies:
## Required IDF version
idf: ">=4.1.0"
espressif/esp_modem:
version: "^0.1.20"
version: "^1.0.0"
override_path: "../../../"
espressif/esp_modem_usb_dte:
version: "^1.0.0"
version: "^1.1.0"
rules:
- if: "idf_version >=4.4"
- if: "target in [esp32s2, esp32s3]"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -48,9 +48,9 @@
} } while (0)
/**
* Please update the default APN name here (this could be updated runtime)
* Default APN name is taken from Kconfig (this could be updated runtime)
*/
#define DEFAULT_APN "my_apn"
#define DEFAULT_APN CONFIG_EXAMPLE_MODEM_PPP_APN
#define GPIO_OUTPUT_PWRKEY (gpio_num_t)CONFIG_EXAMPLE_MODEM_PWRKEY_PIN
#define GPIO_OUTPUT_PIN_SEL (1ULL<<GPIO_OUTPUT_PWRKEY)
@ -89,6 +89,14 @@ void wakeup_modem(void)
vTaskDelay(pdMS_TO_TICKS(2000));
}
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
command_result handle_urc(uint8_t *data, size_t len)
{
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
return command_result::TIMEOUT;
}
#endif
extern "C" void app_main(void)
{
static RTC_RODATA_ATTR char apn_rtc[20] = DEFAULT_APN;
@ -122,19 +130,19 @@ extern "C" void app_main(void)
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
auto uart_dte = create_uart_dte(&dte_config);
#if CONFIG_EXAMPLE_MODEM_DEVICE_SHINY == 1
#if defined(CONFIG_EXAMPLE_MODEM_DEVICE_SHINY)
ESP_LOGI(TAG, "Initializing esp_modem for the SHINY module...");
auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
auto dce = create_BG96_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM800)
ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module...");
auto dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000 == 1
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000)
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7000 module...");
auto dce = create_SIM7000_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070 == 1
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070)
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7070 module...");
auto dce = create_SIM7070_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
@ -149,7 +157,18 @@ extern "C" void app_main(void)
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
while (1) {
exit_signal.clear(1);
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296, 2); // VID, PID and interface num of BG96 modem
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_BG96_USB_CONFIG();
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_SIM7600_USB_CONFIG();
#elif CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the A7670 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_A7670_USB_CONFIG();
#else
#error USB modem not selected
#endif
const esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_USB_CONFIG(usb_config);
ESP_LOGI(TAG, "Waiting for USB device connection...");
auto dte = create_usb_dte(&dte_config);
@ -159,7 +178,13 @@ extern "C" void app_main(void)
exit_signal.set(1);
}
});
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
std::unique_ptr<DCE> dce = create_BG96_dce(&dce_config, dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1 || CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
std::unique_ptr<DCE> dce = create_SIM7600_dce(&dce_config, dte, esp_netif);
#else
#error USB modem not selected
#endif
#else
#error Invalid serial connection to modem.
@ -286,8 +311,9 @@ extern "C" void app_main(void)
const ConsoleCommand GetOperatorName("get_operator_name", "reads the operator name", no_args, [&](ConsoleCommand * c) {
std::string operator_name;
int act;
ESP_LOGI(TAG, "Reading operator name...");
CHECK_ERR(dce->get_operator_name(operator_name), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
CHECK_ERR(dce->get_operator_name(operator_name, act), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
});
const struct GenericCommandArgs {
@ -339,6 +365,20 @@ extern "C" void app_main(void)
ESP_LOGI(TAG, "Resetting the module...");
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
});
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
static int cnt = 0;
if (++cnt % 2) {
ESP_LOGI(TAG, "Adding URC handler");
dce->set_on_read(handle_urc);
} else {
ESP_LOGI(TAG, "URC removed");
dce->set_on_read(nullptr);
}
return 0;
});
#endif
const struct SetApn {
SetApn(): apn(STR1, nullptr, nullptr, "<apn>", "APN (Access Point Name)") {}
CommandArgs apn;

View File

@ -0,0 +1,114 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Modem console example: Custom DCE
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <cstring>
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
#include "generate/esp_modem_command_declare.inc"
#include "my_module_dce.hpp"
using namespace esp_modem;
//
// Define preprocessor's forwarding to dce_commands definitions
//
// Helper macros to handle multiple arguments of declared API
#define ARGS0
#define ARGS1 , p1
#define ARGS2 , p1 , p2
#define ARGS3 , p1 , p2 , p3
#define ARGS4 , p1 , p2 , p3, p4
#define ARGS5 , p1 , p2 , p3, p4, p5
#define ARGS6 , p1 , p2 , p3, p4, p5, p6
#define _ARGS(x) ARGS ## x
#define ARGS(x) _ARGS(x)
#define CMD_OK (1)
#define CMD_FAIL (2)
//
// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
//
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
return_type Shiny::DCE::name(__VA_ARGS__) { return esp_modem::dce_commands::name(this ARGS(arg_nr) ); }
DECLARE_ALL_COMMAND_APIS(return_type name(...) )
#undef ESP_MODEM_DECLARE_DCE_COMMAND
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return Shiny::Factory::create(config, std::move(dte), netif);
}
/**
* @brief Definition of the command API, which makes the Shiny::DCE "command-able class"
* @param cmd Command to send
* @param got_line Recv line callback
* @param time_ms timeout in ms
* @param separator line break separator
* @return OK, FAIL or TIMEOUT
*/
command_result Shiny::DCE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator)
{
if (!handling_urc) {
return dte->command(cmd, got_line, time_ms, separator);
}
handle_cmd = got_line;
signal.clear(CMD_OK | CMD_FAIL);
esp_modem::DTE_Command command{cmd};
dte->write(command);
signal.wait_any(CMD_OK | CMD_FAIL, time_ms);
handle_cmd = nullptr;
if (signal.is_any(CMD_OK)) {
return esp_modem::command_result::OK;
}
if (signal.is_any(CMD_FAIL)) {
return esp_modem::command_result::FAIL;
}
return esp_modem::command_result::TIMEOUT;
}
/**
* @brief Handle received data
*
* @param data Data received from the device
* @param len Length of the data
* @return standard command return code (OK|FAIL|TIMEOUT)
*/
command_result Shiny::DCE::handle_data(uint8_t *data, size_t len)
{
if (std::memchr(data, '\n', len)) {
if (handle_urc) {
handle_urc(data, len);
}
if (handle_cmd) {
auto ret = handle_cmd(data, len);
if (ret == esp_modem::command_result::TIMEOUT) {
return command_result::TIMEOUT;
}
if (ret == esp_modem::command_result::OK) {
signal.set(CMD_OK);
}
if (ret == esp_modem::command_result::FAIL) {
signal.set(CMD_FAIL);
}
}
}
return command_result::TIMEOUT;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -11,6 +11,8 @@
#pragma once
#include <utility>
#include "cxx_include/esp_modem_dce_factory.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
@ -28,13 +30,84 @@ public:
}
};
namespace Shiny {
using namespace esp_modem;
class DCE : public esp_modem::DCE_T<MyShinyModem>, public CommandableIf {
public:
using DCE_T<MyShinyModem>::DCE_T;
command_result
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
{
return command(cmd, got_line, time_ms, '\n');
}
command_result
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator) override;
int write(uint8_t *data, size_t len) override
{
return dte->write(data, len);
}
void on_read(got_line_cb on_data) override
{
return dte->on_read(on_data);
}
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
esp_modem::return_type name(__VA_ARGS__);
DECLARE_ALL_COMMAND_APIS(forwards name(...))
#undef ESP_MODEM_DECLARE_DCE_COMMAND
void set_on_read(esp_modem::got_line_cb on_read_cb)
{
if (on_read_cb == nullptr) {
handling_urc = false;
handle_urc = nullptr;
dte->on_read(nullptr);
return;
}
handle_urc = std::move(on_read_cb);
dte->on_read([this](uint8_t *data, size_t len) {
this->handle_data(data, len);
return command_result::TIMEOUT;
});
handling_urc = true;
}
private:
got_line_cb handle_urc{nullptr};
got_line_cb handle_cmd{nullptr};
SignalGroup signal;
bool handling_urc {false};
command_result handle_data(uint8_t *data, size_t len);
};
class Factory: public ::esp_modem::dce_factory::Factory {
public:
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return build_generic_DCE<MyShinyModem, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
}
};
} // namespace Shiny
/**
* @brief Helper create method which employs the DCE factory for creating DCE objects templated by a custom module
* @return unique pointer of the resultant DCE
*/
std::unique_ptr<esp_modem::DCE> create_shiny_dce(const esp_modem::dce_config *config,
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return esp_modem::dce_factory::Factory::build_unique<MyShinyModem>(config, std::move(dte), netif);
}
esp_netif_t *netif);

View File

@ -0,0 +1,9 @@
# The following 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.8)
set(CMAKE_CXX_STANDARD 17)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modem_tcp_client)

View File

@ -0,0 +1,10 @@
# Modem TCP client
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)
### Supported IDF versions
This example is supported from IDF `v4.4`.

View File

@ -0,0 +1,11 @@
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)
endif()
idf_component_register(SRCS "modem_client.cpp"
"sock_dce.cpp"
"${device_srcs}"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,96 @@
menu "Example Configuration"
choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
help
Select modem device connected to the ESP DTE.
config EXAMPLE_MODEM_DEVICE_BG96
bool "BG96"
help
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
config EXAMPLE_MODEM_DEVICE_SIM7600
bool "SIM7600"
help
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
endchoice
config EXAMPLE_MODEM_APN
string "Set MODEM APN"
default "internet"
help
Set APN (Access Point Name), a logical name to choose data network
menu "UART Configuration"
config EXAMPLE_MODEM_UART_TX_PIN
int "TXD Pin Number"
default 25
range 0 31
help
Pin number of UART TX.
config EXAMPLE_MODEM_UART_RX_PIN
int "RXD Pin Number"
default 26
range 0 31
help
Pin number of UART RX.
config EXAMPLE_MODEM_UART_RTS_PIN
int "RTS Pin Number"
default 27
range 0 31
help
Pin number of UART RTS.
config EXAMPLE_MODEM_UART_CTS_PIN
int "CTS Pin Number"
default 23
range 0 31
help
Pin number of UART CTS.
config EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE
int "UART Event Task Stack Size"
range 2000 6000
default 2048
help
Stack size of UART event task.
config EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY
int "UART Event Task Priority"
range 3 22
default 5
help
Priority of UART event task.
config EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE
int "UART Event Queue Size"
range 10 40
default 30
help
Length of UART event queue.
config EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE
int "UART Pattern Queue Size"
range 10 40
default 20
help
Length of UART pattern queue.
config EXAMPLE_MODEM_UART_TX_BUFFER_SIZE
int "UART TX Buffer Size"
range 256 2048
default 512
help
Buffer size of UART TX buffer.
config EXAMPLE_MODEM_UART_RX_BUFFER_SIZE
int "UART RX Buffer Size"
range 256 2048
default 1024
help
Buffer size of UART RX buffer.
endmenu
endmenu

View File

@ -0,0 +1,140 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* PPPoS Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "sock_dce.hpp"
#include "esp_log.h"
#define BROKER_URL "mqtt.eclipseprojects.io"
static const char *TAG = "modem_client";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int GOT_DATA_BIT = BIT2;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, "/topic/esp-pppos", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "esp32-pppos", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
xEventGroupSetBits(event_group, GOT_DATA_BIT);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
break;
}
}
extern "C" void app_main(void)
{
/* Init and register system/core components */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
event_group = xEventGroupCreate();
/* Configure and create the UART DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
/* setup UART specific configuration based on kconfig options */
dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
dte_config.uart_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
dte_config.uart_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
dte_config.uart_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
dte_config.uart_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
dte_config.uart_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE * 2;
dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
auto dte = esp_modem::create_uart_dte(&dte_config);
assert(dte);
/* Configure the DCE */
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
/* create the DCE and initialize network manually (using AT commands) */
auto dce = sock_dce::create(&dce_config, std::move(dte));
if (!dce->init_network()) {
ESP_LOGE(TAG, "Failed to setup network");
return;
}
dce->init_sock(8883);
esp_mqtt_client_config_t mqtt_config = {};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
mqtt_config.session.message_retransmit_timeout = 10000;
#else
mqtt_config.uri = "mqtt://127.0.0.1";
mqtt_config.message_retransmit_timeout = 10000;
#endif
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
if (!dce->start(BROKER_URL, 8883)) {
ESP_LOGE(TAG, "Failed to start DCE");
return;
}
while (1) {
while (dce->perform_sock()) {
ESP_LOGV(TAG, "...performing");
}
ESP_LOGE(TAG, "Loop exit.. retrying");
// handle disconnections errors
if (!dce->init_network()) {
ESP_LOGE(TAG, "Failed to reinit network");
return;
}
if (!dce->start("test.mosquitto.org", 1883)) {
ESP_LOGI(TAG, "Network reinitialized, retrying");
}
}
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
#include "cxx_include/esp_modem_types.hpp"
#include "socket_commands.inc"
namespace sock_commands {
//using namespace esp_modem;
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
#undef ESP_MODEM_DECLARE_DCE_COMMAND
}

View File

@ -0,0 +1,348 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <charconv>
#include <cstring>
#include <sys/socket.h>
#include "sock_commands.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "sock_dce.hpp"
static const char *TAG = "sock_commands";
namespace sock_commands {
using namespace esp_modem;
command_result net_open(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string out;
auto ret = dce_commands::generic_get_string(t, "AT+QISTATE?\r", out, 1000);
if (ret != command_result::OK) {
return ret;
}
if (out.find("+QISTATE: 0") != std::string::npos) {
ESP_LOGV(TAG, "%s", out.data() );
ESP_LOGD(TAG, "Already there");
return command_result::FAIL;
} else if (out.empty()) {
return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000);
}
return command_result::FAIL;
}
command_result net_close(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
esp_modem::Task::Delay(1000);
return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000);
}
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string ip_open = R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
auto ret = dce_commands::generic_command(t, ip_open, "+QIOPEN: 0,0", "ERROR", timeout);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "%s Failed", __func__ );
return ret;
}
return command_result::OK;
}
command_result tcp_close(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__ );
return dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
}
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
{
ESP_LOGV(TAG, "%s", __func__ );
assert(0); // Remove when fix done
return command_result::FAIL;
}
command_result tcp_recv(CommandableIf *t, uint8_t *data, size_t len, size_t &out_len)
{
ESP_LOGV(TAG, "%s", __func__ );
assert(0); // Remove when fix done
return command_result::FAIL;
}
command_result get_ip(CommandableIf *t, std::string &ip)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string out;
auto ret = dce_commands::generic_get_string(t, "AT+QIACT?\r", out, 5000);
if (ret != command_result::OK) {
return ret;
}
auto pos = out.find("+QIACT: 1");
auto property = 0;
while (pos != std::string::npos) {
// Looking for: +QIACT: <contextID>,<context_state>,<context_type>,<IP_address>
if (property++ == 3) { // ip is after 3rd comma (as a 4rd property of QIACT string)
ip = out.substr(++pos);
// strip quotes if present
auto quote1 = ip.find('"');
auto quote2 = ip.rfind('"');
if (quote1 != std::string::npos && quote2 != std::string::npos) {
ip = ip.substr(quote1 + 1, quote2 - 1);
}
return command_result::OK;
}
pos = out.find(',', ++pos);
}
return command_result::FAIL;
}
} // sock_commands
namespace sock_dce {
void Responder::start_sending(size_t len)
{
data_to_send = len;
send_stat = 0;
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
}
void Responder::start_receiving(size_t len)
{
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
}
bool Responder::start_connecting(std::string host, int port)
{
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
return true;
}
Responder::ret Responder::recv(uint8_t *data, size_t len)
{
const int MIN_MESSAGE = 6;
size_t actual_len = 0;
auto *recv_data = (char *)data;
if (data_to_recv == 0) {
const std::string_view head = "+QIRD: ";
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
if (head_pos == nullptr) {
return ret::FAIL;
}
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
if (next_nl == nullptr) {
return ret::FAIL;
}
if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "cannot convert");
return ret::FAIL;
}
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
if (actual_len == 0) {
ESP_LOGD(TAG, "no data received");
return ret::FAIL;
}
if (actual_len > buffer_size) {
ESP_LOGE(TAG, "TOO BIG");
return ret::FAIL;
}
recv_data = next_nl + 1;
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
if (actual_len > first_data_len) {
::send(sock, recv_data, first_data_len, 0);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
::send(sock, recv_data, actual_len, 0);
} else if (data_to_recv > len) { // continue sending
::send(sock, recv_data, len, 0);
data_to_recv -= len;
return ret::NEED_MORE_DATA;
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
::send(sock, recv_data, data_to_recv, 0);
actual_len = data_to_recv;
}
// "OK" after the data
char *last_pos = nullptr;
if (actual_len + 1 + 2 /* OK */ > len) {
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
if (last_pos == nullptr || last_pos[1] != 'K') {
data_to_recv = 0;
return ret::FAIL;
}
}
if (last_pos != nullptr && (char *)data + len - last_pos - 2 > MIN_MESSAGE) {
// check for async replies after the Recv header
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos);
check_async_replies(status::RECEIVING, response);
}
// check if some other data?
start_receiving(0);
data_to_recv = 0;
return ret::OK;
}
Responder::ret Responder::send(uint8_t *data, size_t len)
{
if (send_stat < 3) {
if (memchr(data, '>', len) == NULL) {
if (send_stat++ < 2) {
return Responder::ret::NEED_MORE_DATA;
}
ESP_LOGE(TAG, "Missed >");
return ret::FAIL;
}
auto written = dte->write(&buffer[0], data_to_send);
if (written != data_to_send) {
ESP_LOGE(TAG, "written %d (%d)...", written, len);
return ret::FAIL;
}
data_to_send = 0;
send_stat = 3;
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::send(std::string_view response)
{
if (send_stat == 3) {
if (response.find("SEND OK") != std::string::npos) {
send_cmd("AT+QISEND=0,0\r");
send_stat++;
return ret::IN_PROGRESS;
} else if (response.find("SEND FAIL") != std::string::npos) {
ESP_LOGE(TAG, "Sending buffer full");
return ret::FAIL;
} else if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to sent");
return ret::FAIL;
}
} else if (send_stat == 4) {
constexpr std::string_view head = "+QISEND: ";
if (response.find(head) != std::string::npos) {
// Parsing +QISEND: <total_send_length>,<ackedbytes>,<unackedbytes>
size_t head_pos = response.find(head);
response = response.substr(head_pos + head.size());
int pos, property = 0;
int total = 0, ack = 0, unack = 0;
while ((pos = response.find(',')) != std::string::npos) {
auto next_comma = (char *)memchr(response.data(), ',', response.size());
// extract value
size_t value;
if (std::from_chars(response.data(), next_comma, value).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "cannot convert");
return ret::FAIL;
}
switch (property++) {
case 0: total = value;
break;
case 1: ack = value;
break;
default:
return ret::FAIL;
}
response = response.substr(pos + 1);
}
if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) {
return ret::FAIL;
}
if (ack < total) {
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
if (total - ack > 64) {
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
return ret::NEED_MORE_TIME;
}
}
send_stat = 0;
return ret::OK;
} else if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to check sending");
return ret::FAIL;
}
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::connect(std::string_view response)
{
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
ESP_LOGI(TAG, "Connected!");
return ret::OK;
}
if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to open");
return ret::FAIL;
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Got data on modem!");
} else if (response.find("+QIRD: ") != std::string::npos) {
static constexpr std::string_view head = "+QIRD: ";
size_t head_pos = response.find(head);
// Parsing +QIURC: <total_receive_length>,<have_read_length>,<unread_length>
response = response.substr(head_pos + head.size());
int next_cr = response.find('\r');
if (next_cr != std::string::npos) {
response = response.substr(next_cr - 2, next_cr);
if (response.find(",0") != std::string::npos) {
ESP_LOGV(TAG, "Receiving done");
} else {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Got data on modem!");
}
}
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
return ret::FAIL;
}
if (state == status::SENDING) {
return send(response);
} else if (state == status::CONNECTING) {
return connect(response);
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
{
if (state == status::SENDING) {
return send(data, len);
}
if (state == status::RECEIVING) {
return recv(data, len);
}
return Responder::ret::IN_PROGRESS;
}
status Responder::pending()
{
send_cmd("AT+QISEND=0,0\r");
return status::SENDING;
}
} // sock_dce

View File

@ -0,0 +1,378 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <charconv>
#include <cstring>
#include <sys/socket.h>
#include "sock_commands.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "sock_dce.hpp"
static const char *TAG = "sock_commands";
namespace sock_commands {
using namespace esp_modem;
command_result net_open(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string response;
auto ret = dce_commands::generic_get_string(term, "AT+NETOPEN?\r", response, 1000);
if (ret != command_result::OK) {
return ret;
}
ESP_LOGV(TAG, "%s", response.data() );
if (response.find("+NETOPEN: 1") != std::string::npos) {
ESP_LOGD(TAG, "Already there");
ret = command_result::OK;
} else if (response.find("+NETOPEN: 0") != std::string::npos) {
ESP_LOGD(TAG, "Need to setup");
ret = dce_commands::generic_command(term, "AT+NETOPEN\r", "+NETOPEN: 1", "+NETOPEN: 0", 10000);
} else {
return command_result::FAIL;
}
if (ret != command_result::OK) {
return ret;
}
return dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 5000);
}
command_result net_close(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
return dce_commands::generic_command(term, "AT+NETCLOSE\r", "+NETCLOSE:", "ERROR", 30000);
}
command_result tcp_open(CommandableIf *term, const std::string &host, int port, int timeout)
{
ESP_LOGV(TAG, "%s", __func__ );
auto ret = dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 50000);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "Setting Rx mode failed!");
return ret;
}
ESP_LOGV(TAG, "%s", __func__ );
std::string ip_open = R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
ret = dce_commands::generic_command(term, ip_open, "+CIPOPEN: 0,0", "ERROR", timeout);
if (ret != command_result::OK) {
ESP_LOGE(TAG, "%s Failed", __func__ );
return ret;
}
return command_result::OK;
}
command_result tcp_close(CommandableIf *term)
{
ESP_LOGV(TAG, "%s", __func__ );
return dce_commands::generic_command(term, "AT+CIPCLOSE=0\r", "+CIPCLOSE:", "ERROR", 10000);
}
command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string send = "AT+CIPSEND=0," + std::to_string(len) + "\r";
auto ret = term->command(send, [&](uint8_t *data, size_t len) {
std::string_view response((char *)data, len);
ESP_LOGI(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
if (response.find('>') != std::string::npos) {
return command_result::OK;
}
return command_result::TIMEOUT;
}, 50000, '>');
if (ret != command_result::OK) {
return ret;
}
ret = command_result::TIMEOUT;
ESP_LOGW(TAG, "Before setting...");
term->on_read([&ret](uint8_t *cmd_data, size_t cmd_len) {
std::string_view response((char *)cmd_data, cmd_len);
ESP_LOGW(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+CIPSEND:") != std::string::npos) {
ret = command_result::OK;
} else if (response.find("ERROR") != std::string::npos) {
ret = command_result::FAIL;
}
return ret;
});
ESP_LOGW(TAG, "Before writing...");
auto written = term->write(data, len);
if (written != len) {
ESP_LOGE(TAG, "written %d (%d)...", written, len);
return command_result::FAIL;
}
uint8_t ctrl_z = '\x1A';
term->write(&ctrl_z, 1);
int count = 0;
while (ret == command_result::TIMEOUT && count++ < 1000 ) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
term->on_read(nullptr);
return ret;
}
command_result tcp_recv(CommandableIf *term, uint8_t *data, size_t len, size_t &out_len)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string out;
auto ret = dce_commands::generic_get_string(term, "AT+CIPRXGET=4,0\r", out);
if (ret != command_result::OK) {
return ret;
}
constexpr std::string_view pattern = "+CIPRXGET: 4,0,";
if (out.find(pattern) == std::string::npos) {
return command_result::FAIL;
}
size_t data_len;
if (std::from_chars(out.data() + pattern.size(), out.data() + out.size(), data_len).ec == std::errc::invalid_argument) {
return command_result::FAIL;
}
ESP_LOGD(TAG, "size=%d", data_len);
if (data_len == 0) {
out_len = data_len;
return command_result::OK;
}
return term->command("AT+CIPRXGET=2,0,100\r", [&](uint8_t *cmd_data, size_t cmd_len) {
char pattern[] = "+CIPRXGET: 2,0,";
ESP_LOG_BUFFER_HEXDUMP(TAG, cmd_data, cmd_len, ESP_LOG_DEBUG);
char *pos = strstr((char *)cmd_data, pattern);
if (pos == nullptr) {
return command_result::FAIL;
}
auto p1 = memchr(pos + sizeof(pattern) - 1, ',', 4);
if (p1 == nullptr) {
return command_result::FAIL;
}
*(char *)p1 = '\0';
size_t actual_len = atoi(pos + sizeof(pattern) - 1);
ESP_LOGD(TAG, "actual len=%d", actual_len);
pos = strchr((char *)p1 + 1, '\n');
if (pos == nullptr) {
ESP_LOGE(TAG, "not found");
return command_result::FAIL;
}
if (actual_len > len) {
ESP_LOGE(TAG, "TOO BIG");
return command_result::FAIL;
}
out_len = actual_len;
memcpy(data, pos + 1, actual_len);
pos = strstr((char *)pos + 1 + actual_len, "OK");
if (pos == nullptr) {
ESP_LOGE(TAG, "ok NOT FOUND");
return command_result::FAIL;
}
return command_result::OK;
}, 50000);
}
command_result get_ip(CommandableIf *term, std::string &ip)
{
std::string resp;
auto ret = dce_commands::generic_get_string(term, "AT+IPADDR\r", resp, 5000);
if (ret != command_result::OK) {
return ret;
}
ip = resp;
return command_result::OK;
}
command_result set_rx_mode(CommandableIf *term, int mode)
{
return dce_commands::generic_command(term, "AT+CIPRXGET=" + std::to_string(mode) + "\r", "OK", "ERROR", 5000);
}
} // sock_commands
namespace sock_dce {
void Responder::start_sending(size_t len)
{
data_to_send = len;
send_stat = 0;
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
}
void Responder::start_receiving(size_t len)
{
send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r");
}
bool Responder::start_connecting(std::string host, int port)
{
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
return true;
}
Responder::ret Responder::recv(uint8_t *data, size_t len)
{
const int MIN_MESSAGE = 6;
size_t actual_len = 0;
auto *recv_data = (char *)data;
if (data_to_recv == 0) {
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
if (head_pos == nullptr) {
return ret::FAIL;
}
if (head_pos - (char *)data > MIN_MESSAGE) {
// check for async replies before the Recv header
std::string_view response((char *)data, head_pos - (char *)data);
check_async_replies(status::RECEIVING, response);
}
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
if (next_comma == nullptr) {
return ret::FAIL;
}
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "cannot convert");
return ret::FAIL;
}
auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */);
if (next_nl == nullptr) {
ESP_LOGE(TAG, "not found");
return ret::FAIL;
}
if (actual_len > buffer_size) {
ESP_LOGE(TAG, "TOO BIG");
return ret::FAIL;
}
size_t total_len = 0;
if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "cannot convert");
return ret::FAIL;
}
read_again = (total_len > 0);
recv_data = next_nl + 1;
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
if (actual_len > first_data_len) {
::send(sock, recv_data, first_data_len, 0);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
::send(sock, recv_data, actual_len, 0);
} else if (data_to_recv > len) { // continue sending
::send(sock, recv_data, len, 0);
data_to_recv -= len;
return ret::NEED_MORE_DATA;
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
::send(sock, recv_data, data_to_recv, 0);
actual_len = data_to_recv;
}
// "OK" after the data
char *last_pos = nullptr;
if (actual_len + 1 + 2 /* OK */ > len) {
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
if (last_pos == nullptr || last_pos[1] != 'K') {
data_to_recv = 0;
return ret::FAIL;
}
}
if (last_pos != nullptr && (char *)data + len - last_pos - 2 > MIN_MESSAGE) {
// check for async replies after the Recv header
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2);
check_async_replies(status::RECEIVING, response);
}
data_to_recv = 0;
if (read_again) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
}
return ret::OK;
}
Responder::ret Responder::send(uint8_t *data, size_t len)
{
if (send_stat == 0) {
if (memchr(data, '>', len) == NULL) {
ESP_LOGE(TAG, "Missed >");
return ret::FAIL;
}
auto written = dte->write(&buffer[0], data_to_send);
if (written != data_to_send) {
ESP_LOGE(TAG, "written %d (%d)...", written, len);
return ret::FAIL;
}
data_to_send = 0;
uint8_t ctrl_z = '\x1A';
dte->write(&ctrl_z, 1);
send_stat++;
return ret::IN_PROGRESS;
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::send(std::string_view response)
{
if (send_stat == 1) {
if (response.find("+CIPSEND:") != std::string::npos) {
send_stat = 0;
return ret::OK;
}
if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to sent");
send_stat = 0;
return ret::FAIL;
}
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::connect(std::string_view response)
{
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
ESP_LOGI(TAG, "Connected!");
return ret::OK;
}
if (response.find("ERROR") != std::string::npos) {
ESP_LOGE(TAG, "Failed to open");
return ret::FAIL;
}
return Responder::ret::IN_PROGRESS;
}
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
{
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
if (response.find("+CIPRXGET: 1") != std::string::npos) {
uint64_t data_ready = 1;
write(data_ready_fd, &data_ready, sizeof(data_ready));
ESP_LOGD(TAG, "Got data on modem!");
}
if (state == status::SENDING) {
return send(response);
} else if (state == status::CONNECTING) {
return connect(response);
}
return ret::IN_PROGRESS;
}
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
{
if (state == status::SENDING) {
return send(data, len);
}
if (state == status::RECEIVING) {
return recv(data, len);
}
return Responder::ret::IN_PROGRESS;
}
status Responder::pending()
{
return status::PENDING;
}
} // sock_dce

View File

@ -0,0 +1,326 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <charconv>
#include <sys/socket.h>
#include "esp_vfs.h"
#include "esp_vfs_eventfd.h"
#include "sock_dce.hpp"
namespace sock_dce {
constexpr auto const *TAG = "sock_dce";
bool DCE::perform_sock()
{
if (listen_sock == -1) {
ESP_LOGE(TAG, "Listening socket not ready");
close_sock();
return false;
}
if (sock == -1) { // no active socket, need to accept one first
return accept_sock();
}
// we have a socket, let's check the status
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 500000,
};
if (state == status::PENDING) {
vTaskDelay(pdMS_TO_TICKS(500));
state = at.pending();
return true;
}
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
FD_SET(data_ready_fd, &fdset);
int s = select(std::max(sock, data_ready_fd) + 1, &fdset, nullptr, nullptr, &tv);
if (s == 0) {
ESP_LOGV(TAG, "perform select timeout...");
return true;
} else if (s < 0) {
ESP_LOGE(TAG, "select error %d", errno);
close_sock();
return false;
}
if (FD_ISSET(sock, &fdset) && !sock_to_at()) {
return false;
}
if (FD_ISSET(data_ready_fd, &fdset) && !at_to_sock()) {
return false;
}
return true;
}
void DCE::perform_at(uint8_t *data, size_t len)
{
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
switch (at.process_data(state, data, len)) {
case Responder::ret::OK:
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
state = status::FAILED;
signal.set(IDLE);
return;
case Responder::ret::NEED_MORE_DATA:
return;
case Responder::ret::IN_PROGRESS:
break;
case Responder::ret::NEED_MORE_TIME:
state = status::PENDING;
return;
}
std::string_view response((char *)data, len);
switch (at.check_async_replies(state, response)) {
case Responder::ret::OK:
state = status::IDLE;
signal.set(IDLE);
return;
case Responder::ret::FAIL:
state = status::FAILED;
signal.set(IDLE);
return;
case Responder::ret::NEED_MORE_TIME:
state = status::PENDING;
return;
case Responder::ret::NEED_MORE_DATA:
case Responder::ret::IN_PROGRESS:
break;
}
}
void DCE::close_sock()
{
if (sock > 0) {
close(sock);
sock = -1;
}
dte->on_read(nullptr);
const int retries = 5;
int i = 0;
while (net_close() != esp_modem::command_result::OK) {
if (i++ > retries) {
ESP_LOGE(TAG, "Failed to close network");
return;
}
esp_modem::Task::Delay(1000);
}
}
bool DCE::at_to_sock()
{
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
ESP_LOGD(TAG, "select read: modem data available %x", data);
if (!signal.wait(IDLE, 1000)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
}
if (state != status::IDLE) {
ESP_LOGE(TAG, "Unexpected state %d", state);
close_sock();
return false;
}
state = status::RECEIVING;
at.start_receiving(at.get_buf_len());
return true;
}
bool DCE::sock_to_at()
{
ESP_LOGD(TAG, "socket read: data available");
if (!signal.wait(IDLE, 1000)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
}
if (state != status::IDLE) {
ESP_LOGE(TAG, "Unexpected state %d", state);
close_sock();
return false;
}
state = status::SENDING;
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
if (len < 0) {
ESP_LOGE(TAG, "read error %d", errno);
close_sock();
return false;
} else if (len == 0) {
ESP_LOGE(TAG, "EOF %d", errno);
close_sock();
return false;
}
ESP_LOG_BUFFER_HEXDUMP(TAG, at.get_buf(), len, ESP_LOG_VERBOSE);
at.start_sending(len);
return true;
}
bool DCE::accept_sock()
{
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 500000,
};
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(listen_sock, &fdset);
int s = select(listen_sock + 1, &fdset, nullptr, nullptr, &tv);
if (s > 0 && FD_ISSET(listen_sock, &fdset)) {
struct sockaddr_in source_addr = {};
socklen_t addr_len = sizeof(source_addr);
sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
return false;
}
ESP_LOGD(TAG, "Socket accepted!");
FD_ZERO(&fdset);
return true;
} else if (s == 0) {
return true;
}
return false;
}
void DCE::init_sock(int port)
{
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);
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return;
}
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
struct sockaddr_in addr = { };
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
// inet_aton("127.0.0.1", &addr.sin_addr);
int err = bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return;
}
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
return;
}
}
bool DCE::start(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;
});
if (!at.start_connecting(host, port)) {
ESP_LOGE(TAG, "Unable to start connecting");
dte->on_read(nullptr);
return false;
}
state = status::CONNECTING;
return true;
}
bool DCE::init_network()
{
dte->on_read(nullptr);
const int retries = 5;
int i = 0;
while (sync() != esp_modem::command_result::OK) {
if (i++ > retries) {
ESP_LOGE(TAG, "Failed to sync up");
return false;
}
esp_modem::Task::Delay(1000);
}
ESP_LOGD(TAG, "Modem in sync");
i = 0;
while (setup_data_mode() != true) {
if (i++ > retries) {
ESP_LOGE(TAG, "Failed to setup pdp/data");
return false;
}
esp_modem::Task::Delay(1000);
}
ESP_LOGD(TAG, "PDP configured");
i = 0;
while (net_open() != esp_modem::command_result::OK) {
if (i++ > retries) {
ESP_LOGE(TAG, "Failed to open network");
return false;
}
net_close();
esp_modem::Task::Delay(1000);
}
ESP_LOGD(TAG, "Network opened");
i = 0;
std::string ip_addr;
while (get_ip(ip_addr) != esp_modem::command_result::OK) {
if (i++ > retries) {
ESP_LOGE(TAG, "Failed obtain an IP address");
return false;
}
esp_modem::Task::Delay(5000);
}
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
return true;
}
class Factory: public ::esp_modem::dce_factory::Factory {
public:
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
{
return esp_modem::dce_factory::Factory::build_module_T<DCE, std::unique_ptr<DCE>>(config, std::move(dte));
}
};
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
{
return Factory::create(config, std::move(dte));
}
// Helper macros to handle multiple arguments of declared API
#define ARGS0
#define ARGS1 , p1
#define ARGS2 , p1 , p2
#define ARGS3 , p1 , p2 , p3
#define EXPAND_ARGS(x) ARGS ## x
#define ARGS(x) EXPAND_ARGS(x)
//
// Repeat all declarations and forward to the AT commands defined in ::sock_commands namespace
//
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
esp_modem::return_type DCE::name(__VA_ARGS__) { return sock_commands::name(dte.get() ARGS(arg_nr) ); }
DECLARE_SOCK_COMMANDS(return_type name(...) )
#undef ESP_MODEM_DECLARE_DCE_COMMAND
} // namespace sock_dce

View File

@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include <cxx_include/esp_modem_dce_factory.hpp>
#include "socket_commands.inc"
#include "sock_commands.hpp"
#pragma once
namespace sock_dce {
enum class status {
IDLE,
CONNECTING,
SENDING,
RECEIVING,
FAILED,
PENDING
};
class Responder {
public:
enum class ret {
OK, FAIL, IN_PROGRESS, NEED_MORE_DATA, NEED_MORE_TIME
};
Responder(int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg):
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);
void start_sending(size_t len);
void start_receiving(size_t len);
bool start_connecting(std::string host, int port);
status pending();
uint8_t *get_buf()
{
return &buffer[0];
}
size_t get_buf_len()
{
return buffer_size;
}
private:
static constexpr size_t buffer_size = 512;
ret recv(uint8_t *data, size_t len);
ret send(uint8_t *data, size_t len);
ret send(std::string_view response);
ret connect(std::string_view response);
void send_cmd(std::string_view command)
{
dte->write((uint8_t *) command.begin(), command.size());
}
std::array<uint8_t, buffer_size> buffer;
size_t data_to_recv = 0;
bool read_again = false;
int &sock;
int &data_ready_fd;
int send_stat = 0;
size_t data_to_send = 0;
std::shared_ptr<esp_modem::DTE> &dte;
};
class DCE : public ::esp_modem::GenericModule {
using esp_modem::GenericModule::GenericModule;
public:
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
esp_modem::return_type name(__VA_ARGS__);
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
#undef ESP_MODEM_DECLARE_DCE_COMMAND
bool init_network();
bool start(std::string host, int port);
void init_sock(int port);
bool perform_sock();
private:
esp_modem::SignalGroup signal;
void close_sock();
bool accept_sock();
bool sock_to_at();
bool at_to_sock();
void perform_at(uint8_t *data, size_t len);
status state{status::IDLE};
static constexpr uint8_t IDLE = 1;
Responder at{sock, data_ready_fd, dte};
int sock {-1};
int listen_sock {-1};
int data_ready_fd {-1};
};
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte);
}

View File

@ -0,0 +1,58 @@
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "generate/esp_modem_command_declare_helper.inc"
#define DECLARE_SOCK_COMMANDS(...) \
/**
* @brief Opens network in AT command mode
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result, 0) \
\
/**
* @brief Closes network in AT command mode
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result, 0) \
\
/**
* @brief Opens a TCP connection
* @param[in] host Host name or IP address to connect to
* @param[in] port Port number
* @param[in] timeout Connection timeout
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, 3, STRING_IN(p1, host), INT_IN(p2, port), INT_IN(p3, timeout)) \
\
/**
* @brief Closes opened TCP socket
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result, 0) \
\
/**
* @brief Gets modem IP address
* @param[out] addr String representation of modem's IP
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, 1, STRING_OUT(p1, addr)) \
\
/**
* @brief Sets Rx mode
* @param[in] mode 0=auto, 1=manual
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, 1, INT_IN(p1, mode))

View File

@ -15,12 +15,12 @@ menu "Example Configuration"
endchoice
choice EXAMPLE_MODEM_DEVICE
depends on EXAMPLE_SERIAL_CONFIG_UART
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
help
Select modem device connected to the ESP DTE.
config EXAMPLE_MODEM_DEVICE_SIM800
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM800"
help
SIMCom SIM800L is a GSM/GPRS module.
@ -30,10 +30,12 @@ menu "Example Configuration"
help
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
config EXAMPLE_MODEM_DEVICE_SIM7000
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM7000"
help
SIM7000 is a Multi-Band LTE-FDD and GSM/GPRS/EDGE module.
config EXAMPLE_MODEM_DEVICE_SIM7070
depends on EXAMPLE_SERIAL_CONFIG_UART
bool "SIM7070"
help
SIM7070 is Multi-Band CAT M and NB IoT module.
@ -41,6 +43,11 @@ menu "Example Configuration"
bool "SIM7600"
help
SIM7600 is a Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module.
config EXAMPLE_MODEM_DEVICE_A7670
depends on EXAMPLE_SERIAL_CONFIG_USB
bool "A7670"
help
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
endchoice
config EXAMPLE_MODEM_PPP_APN

View File

@ -3,10 +3,10 @@ dependencies:
## Required IDF version
idf: ">=4.1.0"
espressif/esp_modem:
version: "^0.1.23"
version: "^1.0.0"
override_path: "../../../"
espressif/esp_modem_usb_dte:
version: "^1.0.0"
version: "^1.1.0"
rules:
- if: "idf_version >=4.4"
- if: "target in [esp32s2, esp32s3]"

View File

@ -208,14 +208,28 @@ void app_main(void)
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
while (1) {
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296, 2); // VID, PID and interface num of BG96 modem
struct esp_modem_usb_term_config usb_config = ESP_MODEM_BG96_USB_CONFIG();
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_BG96;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_SIM7600_USB_CONFIG();
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_SIM7600;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the A7670 module...");
struct esp_modem_usb_term_config usb_config = ESP_MODEM_A7670_USB_CONFIG();
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_SIM7600;
#else
#error USB modem not selected
#endif
const esp_modem_dte_config_t dte_usb_config = ESP_MODEM_DTE_DEFAULT_USB_CONFIG(usb_config);
ESP_LOGI(TAG, "Waiting for USB device connection...");
esp_modem_dce_t *dce = esp_modem_new_dev_usb(ESP_MODEM_DCE_BG96, &dte_usb_config, &dce_config, esp_netif);
esp_modem_dce_t *dce = esp_modem_new_dev_usb(usb_dev_type, &dte_usb_config, &dce_config, esp_netif);
assert(dce);
esp_modem_set_error_cb(dce, usb_terminal_error_handler);
vTaskDelay(pdMS_TO_TICKS(1000)); // Although the DTE should be ready after USB enumeration, sometimes it fails to respond without this delay
ESP_LOGI(TAG, "Modem connected, waiting 10 seconds for boot...");
vTaskDelay(pdMS_TO_TICKS(10000)); // Give DTE some time to boot
#else
#error Invalid serial connection to modem.

View File

@ -1,6 +1,7 @@
# The following 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.5)
cmake_minimum_required(VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
set(EXTRA_COMPONENT_DIRS "../..")

View File

@ -175,6 +175,9 @@ extern "C" void app_main(void)
#endif
assert(dce);
/* Try to connect to the network and publish an mqtt topic */
StatusHandler handler;
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
if (command_result::OK != dce->set_flow_control(2, 2)) {
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
@ -215,8 +218,6 @@ extern "C" void app_main(void)
}
#endif
/* Try to connect to the network and publish an mqtt topic */
StatusHandler handler;
if (!handler.wait_for(StatusHandler::IP_Event, 60000)) {
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
return;

View File

@ -1,4 +1,4 @@
version: "0.1.28"
version: "1.0.1"
description: esp modem
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
dependencies:

View File

@ -22,6 +22,19 @@ namespace dce_commands {
* @{
*/
/**
* @brief Generic AT command to be send with pass and fail phrases
*
* @param t Commandable object (anything that can accept commands)
* @param command Command to be sent do the commandable object
* @param pass_phrase String to be present in the reply to pass this command
* @param fail_phrase String to be present in the reply to fail this command
* @param timeout_ms Timeout in ms
*/
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms);
/**
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc
*/

View File

@ -69,6 +69,10 @@ public:
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
}
explicit Creator(std::shared_ptr<DTE> dte): dte(std::move(dte)), device(nullptr), netif(nullptr)
{
}
~Creator()
{
if (device != nullptr) {
@ -232,6 +236,14 @@ public:
return nullptr;
}
template <typename T_Module>
static std::unique_ptr<DCE> create_unique_dce_from(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
}
private:
ModemType m;

View File

@ -29,6 +29,13 @@ class CMux;
* @{
*/
struct DTE_Command {
DTE_Command(const std::string &cmd): data((uint8_t *)cmd.c_str()), len(cmd.length()) {}
uint8_t *data;
size_t len;
};
/**
* DTE (Data Terminal Equipment) class
*/
@ -54,7 +61,9 @@ public:
* @param len Data len to write
* @return number of bytes written
*/
int write(uint8_t *data, size_t len);
int write(uint8_t *data, size_t len) override;
int write(DTE_Command command);
/**
* @brief Reading from the underlying terminal
@ -70,6 +79,15 @@ public:
*/
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
/**
* @brief Sets read callback for manual command processing
* Note that this API also locks the command API, which can only be used
* after you remove the callback by dte->on_read(nullptr)
*
* @param on_data Function to be called when a command response is available
*/
void on_read(got_line_cb on_data) override;
/**
* @brief Sets DTE error callback
* @param f Function to be called on DTE error
@ -111,10 +129,10 @@ protected:
}
friend class Scoped<DTE>; /*!< Declaring "Scoped<DTE> lock(dte)" locks this instance */
private:
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
void exit_cmux_internal(); /*!< Cleanup CMUX */
Lock internal_lock{}; /*!< Locks DTE operations */
unique_buffer buffer; /*!< DTE buffer */
@ -122,9 +140,73 @@ private:
std::shared_ptr<Terminal> primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */
std::shared_ptr<Terminal> secondary_term; /*!< Secondary terminal for this DTE */
modem_mode mode; /*!< DTE operation mode */
SignalGroup signal; /*!< Event group used to signal request-response operations */
command_result result; /*!< Command result of the currently exectuted command */
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
/**
* @brief Implements an extra buffer that is used to capture partial reads from underlying terminals
* when we run out of the standard buffer
*/
struct extra_buffer {
extra_buffer(): buffer(nullptr) {}
~extra_buffer()
{
delete buffer;
}
std::vector<uint8_t> *buffer;
size_t consumed{0};
void grow(size_t need_size);
void deflate()
{
grow(0);
consumed = 0;
}
[[nodiscard]] uint8_t *begin() const
{
return &buffer->at(0);
}
[[nodiscard]] uint8_t *current() const
{
return &buffer->at(0) + consumed;
}
} inflatable;
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
/**
* @brief Set internal command callbacks to the underlying terminal.
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
*/
void set_command_callbacks();
/**
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
*/
struct command_cb {
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
got_line_cb got_line; /*!< Supplied command callback */
Lock line_lock{}; /*!< Command callback locking mechanism */
char separator{}; /*!< Command reply separator (end of line/processing unit) */
command_result result{}; /*!< Command return code */
SignalGroup signal; /*!< Event group used to signal request-response operations */
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
bool wait_for_line(uint32_t time_ms); /*!< Waiting for command processing */
void set(got_line_cb l, char s = '\n') /*!< Sets the command callback atomically */
{
Scoped<Lock> lock(line_lock);
if (l) {
// if we set the line callback, we have to reset the signal and the result
signal.clear(GOT_LINE);
result = command_result::TIMEOUT;
}
got_line = std::move(l);
separator = s;
}
void give_up() /*!< Reports other than timeout error when processing replies (out of buffer) */
{
result = command_result::FAIL;
signal.set(GOT_LINE);
}
} command_cb; /*!< Command callback utility class */
};
/**

View File

@ -54,9 +54,10 @@ public:
*/
void stop();
private:
void receive(uint8_t *data, size_t len);
private:
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len);
static esp_err_t esp_modem_post_attach(esp_netif_t *esp_netif, void *args);

View File

@ -80,6 +80,9 @@ public:
*/
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) = 0;
virtual int write(uint8_t *data, size_t len) = 0;
virtual void on_read(got_line_cb on_data) = 0;
};
/**

View File

@ -1,4 +1,4 @@
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -11,36 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifndef _ESP_MODEM_COMMAND_DECLARE_INC_
#define _ESP_MODEM_COMMAND_DECLARE_INC_
#include "generate/esp_modem_command_declare_helper.inc"
// Parameters
// * handle different parameters for C++ and C API
// * make parameter unique names, so they could be easily referenced and forwarded
#define _ARG(param, name) param
#define INT_IN(param, name) int _ARG(param, name)
#ifdef __cplusplus
#include <string>
#define STRING_IN(param, name) const std::string& _ARG(param, name)
#define STRING_OUT(param, name) std::string& _ARG(param, name)
#define BOOL_IN(param, name) const bool _ARG(param, name)
#define BOOL_OUT(param, name) bool& _ARG(param, name)
#define INT_OUT(param, name) int& _ARG(param, name)
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) struct_name& p1
#else
#define STRING_IN(param, name) const char* _ARG(param, name)
#define STRING_OUT(param, name) char* _ARG(param, name)
#define BOOL_IN(param, name) const bool _ARG(param, name)
#define BOOL_OUT(param, name) bool* _ARG(param, name)
#define INT_OUT(param, name) int* _ARG(param, name)
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) esp_modem_ ## struct_name ## _t* p1
#endif
#define DECLARE_ALL_COMMAND_APIS(...) \
/**
* @brief Sends the initial AT sequence to sync up with the device
@ -331,5 +306,3 @@ public:
#endif
#endif
#endif // _ESP_MODEM_COMMAND_DECLARE_INC_

View File

@ -0,0 +1,26 @@
// Parameters
// * handle different parameters for C++ and C API
// * make parameter unique names, so they could be easily referenced and forwarded
#define _ARG(param, name) param
#define INT_IN(param, name) int _ARG(param, name)
#ifdef __cplusplus
#include <string>
#define STRING_IN(param, name) const std::string& _ARG(param, name)
#define STRING_OUT(param, name) std::string& _ARG(param, name)
#define BOOL_IN(param, name) const bool _ARG(param, name)
#define BOOL_OUT(param, name) bool& _ARG(param, name)
#define INT_OUT(param, name) int& _ARG(param, name)
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) struct_name& p1
#else
#define STRING_IN(param, name) const char* _ARG(param, name)
#define STRING_OUT(param, name) char* _ARG(param, name)
#define BOOL_IN(param, name) const bool _ARG(param, name)
#define BOOL_OUT(param, name) bool* _ARG(param, name)
#define INT_OUT(param, name) int* _ARG(param, name)
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
#define STRUCT_OUT(struct_name, p1) esp_modem_ ## struct_name ## _t* p1
#endif

View File

@ -21,10 +21,17 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
netif.stop();
auto signal = std::make_shared<SignalGroup>();
std::weak_ptr<SignalGroup> weak_signal = signal;
dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
// post the transitioning data to the network layers if it contains PPP SOF marker
if (memchr(data, 0x7E, len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (PPP)", data, len, ESP_LOG_DEBUG);
netif.receive(data, len);
}
// treat the transitioning data as a textual message if it contains a newline char
if (memchr(data, '\n', len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
std::string_view response((char *) data, len);
for (auto &it : pass)
@ -39,6 +46,7 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
});
netif.wait_until_ppp_exits();
if (!signal->wait(1, 2000)) {
dte.set_read_cb(nullptr);
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
return false;
}

View File

@ -17,53 +17,118 @@ static const size_t dte_default_buffer_size = 1000;
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF) {}
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
DTE::DTE(std::unique_ptr<Terminal> terminal):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF) {}
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE) {}
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE) {}
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
void DTE::set_command_callbacks()
{
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
Scoped<Lock> l(command_cb.line_lock);
if (command_cb.got_line == nullptr) {
return false;
}
if (data) {
// For terminals which post data directly with the callback (CMUX)
// we cannot defragment unless we allocate, but
// we'll try to process the data on the actual buffer
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
if (inflatable.consumed != 0) {
inflatable.grow(inflatable.consumed + len);
std::memcpy(inflatable.current(), data, len);
data = inflatable.begin();
}
if (command_cb.process_line(data, inflatable.consumed, len)) {
return true;
}
// at this point we're sure that the data processing hasn't finished,
// and we have to grow the inflatable buffer (if enabled) or give up
if (inflatable.consumed == 0) {
inflatable.grow(len);
std::memcpy(inflatable.begin(), data, len);
}
inflatable.consumed += len;
return false;
#else
if (command_cb.process_line(data, 0, len)) {
return true;
}
// cannot inflate and the processing hasn't finishes in the first iteration -> report a failure
command_cb.give_up();
return true;
#endif
}
// data == nullptr: Terminals which request users to read current data
// we're able to use DTE's buffer to defragment it; as long as we consume less that the buffer size
if (buffer.size > buffer.consumed) {
data = buffer.get();
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
if (command_cb.process_line(data, buffer.consumed, len)) {
return true;
}
buffer.consumed += len;
return false;
}
// we have used the entire DTE's buffer, need to use the inflatable buffer to continue
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
if (inflatable.consumed == 0) {
inflatable.grow(buffer.size + len);
std::memcpy(inflatable.begin(), buffer.get(), buffer.size);
inflatable.consumed = buffer.size;
} else {
inflatable.grow(inflatable.consumed + len);
}
len = primary_term->read(inflatable.current(), len);
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
return true;
}
inflatable.consumed += len;
return false;
#else
// cannot inflate -> report a failure
command_cb.give_up();
return true;
#endif
});
}
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
{
Scoped<Lock> l(internal_lock);
result = command_result::TIMEOUT;
signal.clear(GOT_LINE);
primary_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) {
if (!data) {
data = buffer.get();
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
} else {
buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment
}
if (memchr(data + buffer.consumed, separator, len)) {
result = got_line(data, buffer.consumed + len);
if (result == command_result::OK || result == command_result::FAIL) {
signal.set(GOT_LINE);
return true;
}
}
buffer.consumed += len;
return false;
});
Scoped<Lock> l1(internal_lock);
command_cb.set(got_line, separator);
primary_term->write((uint8_t *)command.c_str(), command.length());
auto got_lf = signal.wait(GOT_LINE, time_ms);
if (got_lf && result == command_result::TIMEOUT) {
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
}
command_cb.wait_for_line(time_ms);
command_cb.set(nullptr);
buffer.consumed = 0;
primary_term->set_read_cb(nullptr);
return result;
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
inflatable.deflate();
#endif
return command_cb.result;
}
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
@ -76,12 +141,22 @@ bool DTE::exit_cmux()
if (!cmux_term->deinit()) {
return false;
}
exit_cmux_internal();
return true;
}
void DTE::exit_cmux_internal()
{
if (!cmux_term) {
return;
}
auto ejected = cmux_term->detach();
// return the ejected terminal and buffer back to this DTE
primary_term = std::move(ejected.first);
buffer = std::move(ejected.second);
secondary_term = primary_term;
return true;
set_command_callbacks();
}
bool DTE::setup_cmux()
@ -90,14 +165,21 @@ bool DTE::setup_cmux()
if (cmux_term == nullptr) {
return false;
}
if (!cmux_term->init()) {
exit_cmux_internal();
cmux_term = nullptr;
return false;
}
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
if (primary_term == nullptr) {
return false;
}
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
secondary_term = std::make_unique<CMuxInstance>(cmux_term, 1);
if (primary_term == nullptr || secondary_term == nullptr) {
exit_cmux_internal();
cmux_term = nullptr;
return false;
}
set_command_callbacks();
return true;
}
@ -119,6 +201,7 @@ bool DTE::set_mode(modem_mode m)
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
// mode stays the same, but need to swap terminals (as command has been switched)
secondary_term.swap(primary_term);
set_command_callbacks();
} else {
mode = m;
}
@ -161,6 +244,7 @@ bool DTE::set_mode(modem_mode m)
// manual CMUX transitions: Swap terminals
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
secondary_term.swap(primary_term);
set_command_callbacks();
return true;
}
mode = modem_mode::UNDEF;
@ -169,6 +253,10 @@ bool DTE::set_mode(modem_mode m)
void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
{
if (f == nullptr) {
set_command_callbacks();
return;
}
on_data = std::move(f);
secondary_term->set_read_cb([this](uint8_t *data, size_t len) {
if (!data) { // if no data available from terminal callback -> need to explicitly read some
@ -202,6 +290,69 @@ int DTE::write(uint8_t *data, size_t len)
return secondary_term->write(data, len);
}
int DTE::write(DTE_Command command)
{
return primary_term->write(command.data, command.len);
}
void DTE::on_read(got_line_cb on_read_cb)
{
if (on_read_cb == nullptr) {
primary_term->set_read_cb(nullptr);
internal_lock.unlock();
return;
}
internal_lock.lock();
primary_term->set_read_cb([this, on_read_cb](uint8_t *data, size_t len) {
if (!data) {
data = buffer.get();
len = primary_term->read(data, buffer.size);
}
auto res = on_read_cb(data, len);
if (res == command_result::OK || res == command_result::FAIL) {
primary_term->set_read_cb(nullptr);
internal_lock.unlock();
return true;
}
return false;
});
}
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
{
if (memchr(data + consumed, separator, len)) {
result = got_line(data, consumed + len);
if (result == command_result::OK || result == command_result::FAIL) {
signal.set(GOT_LINE);
return true;
}
}
return false;
}
bool DTE::command_cb::wait_for_line(uint32_t time_ms)
{
auto got_lf = signal.wait(command_cb::GOT_LINE, time_ms);
if (got_lf && result == command_result::TIMEOUT) {
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
}
return got_lf;
}
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
void DTE::extra_buffer::grow(size_t need_size)
{
if (need_size == 0) {
delete buffer;
buffer = nullptr;
} else if (buffer == nullptr) {
buffer = new std::vector<uint8_t>(need_size);
} else {
buffer->resize(need_size);
}
}
#endif
/**
* Implemented here to keep all headers C++11 compliant
*/

View File

@ -65,9 +65,7 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
void Netif::receive(uint8_t *data, size_t len)
{
if (signal.is_any(PPP_STARTED)) {
esp_netif_receive(driver.base.netif, data, len, nullptr);
}
esp_netif_receive(driver.base.netif, data, len, nullptr);
}
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
@ -89,8 +87,8 @@ void Netif::start()
receive(data, len);
return true;
});
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
signal.set(PPP_STARTED);
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
}
void Netif::stop()

View File

@ -8,6 +8,11 @@
#include "esp_modem_config.h"
#include "uart_resource.hpp"
#ifndef UART_HW_FIFO_LEN
// to build with IDF <= v5.1
#define UART_HW_FIFO_LEN(uart_nr) UART_FIFO_LEN
#endif
namespace esp_modem {
uart_resource::~uart_resource()
@ -18,7 +23,7 @@ uart_resource::~uart_resource()
}
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t *event_queue, int fd)
: port(-1)
: port(UART_NUM_MAX)
{
esp_err_t res;
@ -44,9 +49,9 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
ESP_MODEM_THROW_IF_ERROR(res, "config uart gpio failed");
/* Set flow control threshold */
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_HW_FIFO_LEN(config->port_num) - 8);
} else if (config->flow_control == ESP_MODEM_FLOW_CONTROL_SW) {
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_FIFO_LEN - 8);
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_HW_FIFO_LEN(config->port_num) - 8);
}
ESP_MODEM_THROW_IF_ERROR(res, "config uart flow control failed");

View File

@ -64,7 +64,6 @@ public:
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
{
ESP_MODEM_THROW_IF_FALSE(signal.wait(TASK_PARAMS, 1000), "Failed to set UART task params");
on_read = std::move(f);
}
@ -91,7 +90,6 @@ private:
static const size_t TASK_INIT = BIT0;
static const size_t TASK_START = BIT1;
static const size_t TASK_STOP = BIT2;
static const size_t TASK_PARAMS = BIT3;
QueueHandle_t event_queue;
uart_resource uart;
@ -118,9 +116,7 @@ void UartTerminal::task()
return; // exits to the static method where the task gets deleted
}
while (signal.is_any(TASK_START)) {
signal.set(TASK_PARAMS);
if (get_event(event, 100)) {
signal.clear(TASK_PARAMS);
switch (event.type) {
case UART_DATA:
uart_get_buffered_data_len(uart.port, &len);

View File

@ -13,15 +13,3 @@ idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_COMPILER_CXX_EXCEPTIONS")
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
target_link_options(${esp_modem} INTERFACE -fsanitize=address -fsanitize=undefined)
if(CONFIG_GCOV_ENABLED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
endif()

View File

@ -12,3 +12,12 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_EXTENSIONS ON
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
if(CONFIG_GCOV_ENABLED)
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
target_compile_options(${esp_modem} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
target_link_options(${esp_modem} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
endif()

View File

@ -0,0 +1,13 @@
# esp_mqtt_cxx
[![Component Registry](https://components.espressif.com/components/espressif/esp_mqtt_cxx/badge.svg)](https://components.espressif.com/components/espressif/esp_mqtt_cxx)
The ESP MQTT client is a wrapper over the esp_mqtt client with the goal of providing a higher level API.
## Examples
Get started with [examples](https://github.com/espressif/esp-protocols/tree/master/components/esp_mqtt_cxx/examples)
## Documentation
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/docs/latest/esp_mqtt_cxx/index.html)

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