Compare commits

...

99 Commits

Author SHA1 Message Date
b8f3423dc0 Merge pull request #367 from david-cermak/test/sim800_cmux
fix(modem): Release v1.0.3: Fixes CMUX on SIM800
2023-09-26 19:56:06 +02:00
4e178735cd bump(modem): 1.0.2 -> 1.0.3
1.0.3
Bug Fixes
- Fix to allow MSC frame (SIM800 CMUX) after v1.0.2 (8d5947e, #366)
- Add CMUX example to target tests (4f2ebaa)
2023-09-26 15:50:42 +02:00
8d5947e5f1 fix(modem): Fix to allow MSC frame (SIM800 CMUX) after v1.0.2
Fixes the regression from 8edbac69 that added more strict conditions
for CMUX protocol, but didn't allow MSC frames that might be sent during
initializaiton by SIM800.

Closes https://github.com/espressif/esp-protocols/issues/366
2023-09-26 15:48:30 +02:00
4f2ebaa753 fix(modem): Add CMUX example to target tests 2023-09-26 15:30:49 +02:00
0db29f28db Merge pull request #365 from david-cermak/bump/esp_modem
bump(modem): 1.0.1 -> 1.0.2
2023-09-26 06:15:37 +02:00
223647e975 Merge pull request #326 from gabsuren/fix/fix_format_warnings
fix(common): removed Wno-format flag and fixed formatting warnings (IDFGH-7768)
2023-09-25 20:54:08 +04:00
c48e44205d fix(common): removed Wno-format flag and fixed formatting warnings 2023-09-25 19:55:44 +04:00
b9bdcf612a bump(modem): 1.0.1 -> 1.0.2
1.0.2
Features
- Add factory method for simple creation of custom DCEs (4cf9e50)
- Support custom transport in AT TCP client example (ae629ed)
Bug Fixes
- Fix host test race with async read and d'structor (c89d42f)
- More error handling in cmux protocol (8edbac6)
- Fix netif to set PPP config in C++ way (5287432)
- Fix vfs terminal not to reset read_cb() automatically (25a35e2)
- Fix netif data race causing PPP startup delays (c8c0507, #308)
- Added support for inflatable buffer (cb6e03a, #272)
- Fix LoadProhibited after failed CMUX initialization (IDFGH-10845) (60c87dd)
2023-09-25 16:20:59 +02:00
19baa7d43e Merge pull request #362 from david-cermak/fix/modem_cmux
fix(modem): More error handling in cmux protocol
2023-09-25 14:38:28 +02:00
c89d42f272 fix(modem): Fix host test race with async read and d'structor
Loopback terminal uses std::async to inject test data to the test
terminal. There was a hazardeous condition when destructing the terminal
while async batch_read() was in progress. Adding a signal and waiting
for destruction solves the issue.
2023-09-25 14:15:48 +02:00
8edbac6974 fix(modem): More error handling in cmux protocol
Add error path to all CMUX protocol potential issues, checks for
consistency and add recovery.
2023-09-22 08:39:31 +02:00
163122ff54 Merge pull request #361 from david-cermak/fix/modem_cosmetic
fix(modem): Update vfs terminal; Removed designated init
2023-09-22 08:37:52 +02:00
4b7d1943ac fix(ci): fix host test failure for esp_modem 2023-09-22 08:00:12 +02:00
bac742df80 fix(mqtt_cxx): Removes meaningless printf on subscribed handler (#358)
Removes the printf since the information isn't in the event.

Closes https://github.com/espressif/esp-protocols/issues/356
2023-09-21 08:42:28 +02:00
5287432197 fix(modem): Fix netif to set PPP config in C++ way
If the config struct contains more items (or more items will be added in
future) this init will not compile cleanly
2023-09-20 11:38:04 +02:00
25a35e20a3 fix(modem): Fix vfs terminal not to reset read_cb() automatically
This change is needed for the fs terminal to work correctly again, after
merging 44bae24c78 which shifted
responsibility of thread safety from a terminal to the DTE. (Before it
was the terminal, which removed this callback safely when processing completed,
and thus we had to check the callback return value). Not the DTE layers
are responsible for thread safety and the return value indicates
success/failure from processing.
Also uses the default clock, the same way as UART terminal.
2023-09-20 11:37:36 +02:00
12bacdc3a0 Merge pull request #359 from euripedesrocha/fix/missing_event_callback_t
fix(mqtt_cxx): Removes unused type for configuration
2023-09-20 08:35:05 +02:00
839c79d460 fix(mqtt_cxx): Removes unused type for configuration
Event type was introduced in the original version and wasn't used in the
code. In 5.1 the leftover type was removed from esp_mqtt breaking this
wrapper.
2023-09-19 12:54:16 +02:00
13b29c2291 Merge pull request #349 from gabsuren/mdns/update_versioin_1_2_1
bump(mdns): 1.2.0 -> 1.2.1
2023-09-07 13:24:52 +04:00
0cb1156480 bump(mdns): 1.2.0 -> 1.2.1
1.2.1
Features
- Allow setting length of mDNS action queue in menuconfig (28cd898)
Bug Fixes
- fix build issue if CONFIG_ESP_WIFI_ENABLED disabled (24f7031)
- added idf_component.yml for examples (d273e10)
- added guard check for null pointer (71bb461)
2023-08-29 16:57:18 +04:00
6c7259fa7a Merge pull request #336 from gabsuren/mdns/fix_build_issue
fix(mdns): fix build issue if CONFIG_ESP_WIFI_ENABLED disabled (IDFGH-9699)
2023-08-29 14:15:01 +04:00
24f7031012 fix(mdns): fix build issue if CONFIG_ESP_WIFI_ENABLED disabled 2023-08-29 13:41:26 +04:00
62d809ac60 Merge pull request #309 from david-cermak/feat/modem_tcp_client_work
feat(modem): Support custom transport in AT TCP client example
2023-08-28 13:43:46 +02:00
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
d4925f2bd6 fix(modem): Per review comments 2023-08-17 13:17:20 +02:00
ae629ed3a9 feat(modem): Support custom transport in AT TCP client example 2023-08-17 13:11:51 +02: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
184 changed files with 4289 additions and 738 deletions

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

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

@ -12,7 +12,7 @@ jobs:
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

View File

@ -22,6 +22,8 @@ jobs:
example: modem_tcp_client
- idf_ver: "release-v4.3"
example: modem_tcp_client
- idf_ver: "release-v4.4"
example: modem_tcp_client
include:
- idf_ver: "release-v4.2"
skip_config: usb
@ -62,7 +64,7 @@ jobs:
COMP_DIR: esp-protocols/components/esp_modem
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
uses: actions/checkout@v3
with:
path: esp-protocols
@ -91,6 +93,8 @@ jobs:
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
@ -102,11 +106,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
@ -126,10 +130,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:
@ -142,7 +145,6 @@ jobs:
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
@ -150,28 +152,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

@ -15,7 +15,7 @@ jobs:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32c3"]
test: [ { app: pppd, path: test/target }, { app: sim800_c3, path: examples/pppos_client } ]
test: [ { app: pppd, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
@ -54,11 +54,11 @@ jobs:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32c3"]
test: [ { app: pppd, path: test/target }, { app: sim800_c3, path: examples/pppos_client } ]
test: [ { app: pppd, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
needs: build_esp_modem_tests
runs-on:
- self-hosted
- BrnoRPI-GH006
- modem
env:
TEST_DIR: components/esp_modem/${{ matrix.test.path }}
steps:

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

@ -10,7 +10,7 @@ env:
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 : ${{ secrets.DOCS_DEPLOY_PATH }}
DOCS_DEPLOY_PATH_ORIG : ${{ secrets.DOCS_DEPLOY_PATH }}
jobs:
publish:
@ -43,27 +43,51 @@ jobs:
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
# cd $GITHUB_WORKSPACE/docs
# ./generate_docs
# - name: Deploying generated docs
# if: always()
# 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
# export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs
# deploy-docs
# - name: Upload components to component service
# uses: espressif/github-actions/upload_components@master
# with:
# directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio;components/esp_mqtt_cxx"
# namespace: "espressif"
# api_token: ${{ secrets.IDF_COMPONENT_API_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 }}

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

@ -13,45 +13,32 @@ jobs:
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:
@ -64,32 +51,36 @@ jobs:
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']
@ -62,7 +62,7 @@ repos:
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

@ -1,5 +1,11 @@
# Collection of protocol components for ESP-IDF
## 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

View File

@ -26,20 +26,29 @@ if __name__ == '__main__':
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=False,
recursive=args.recursive,
target=args.target,
build_dir='build_@t_@w',
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=not args.delete,
manifest_files=args.manifests,
@ -54,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)
)

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/**'

View File

@ -9,7 +9,7 @@ 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 has been updated"
echo "${comp}: Component version file has changed"
version=`grep version: components/${comp}/.cz.yaml`
version=${version#*version: }
@ -17,20 +17,25 @@ if git log -1 -m --name-only --pretty="" | grep -q components/${comp}/idf_compon
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
# 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}"
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"
# 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;
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

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

@ -9,6 +9,11 @@
#include <stdint.h>
#include "bsd/string.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_timer *esp_timer_handle_t;
typedef void (*esp_timer_cb_t)(void *arg);
@ -32,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

@ -27,7 +27,6 @@ idf_component_register(SRCS ${asio_sources}
INCLUDE_DIRS "asio/asio/include" "port/include"
PRIV_INCLUDE_DIRS ${asio_priv_includes}
REQUIRES lwip)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
if(CONFIG_ASIO_SSL_SUPPORT)
if(CONFIG_ASIO_USE_ESP_WOLFSSL)

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

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

View File

@ -1,5 +1,44 @@
# Changelog
## [1.0.3](https://github.com/espressif/esp-protocols/commits/modem-v1.0.3)
### Bug Fixes
- Fix to allow MSC frame (SIM800 CMUX) after v1.0.2 ([8d5947e](https://github.com/espressif/esp-protocols/commit/8d5947e), [#366](https://github.com/espressif/esp-protocols/issues/366))
- Add CMUX example to target tests ([4f2ebaa](https://github.com/espressif/esp-protocols/commit/4f2ebaa))
## [1.0.2](https://github.com/espressif/esp-protocols/commits/modem-v1.0.2)
### Features
- Add factory method for simple creation of custom DCEs ([4cf9e50](https://github.com/espressif/esp-protocols/commit/4cf9e50))
- Support custom transport in AT TCP client example ([ae629ed](https://github.com/espressif/esp-protocols/commit/ae629ed))
### Bug Fixes
- Fix host test race with async read and d'structor ([c89d42f](https://github.com/espressif/esp-protocols/commit/c89d42f))
- More error handling in cmux protocol ([8edbac6](https://github.com/espressif/esp-protocols/commit/8edbac6))
- Fix netif to set PPP config in C++ way ([5287432](https://github.com/espressif/esp-protocols/commit/5287432))
- Fix vfs terminal not to reset read_cb() automatically ([25a35e2](https://github.com/espressif/esp-protocols/commit/25a35e2))
- Fix netif data race causing PPP startup delays ([c8c0507](https://github.com/espressif/esp-protocols/commit/c8c0507), [#308](https://github.com/espressif/esp-protocols/issues/308))
- Added support for inflatable buffer ([cb6e03a](https://github.com/espressif/esp-protocols/commit/cb6e03a), [#272](https://github.com/espressif/esp-protocols/issues/272))
- Fix LoadProhibited after failed CMUX initialization (IDFGH-10845) ([60c87dd](https://github.com/espressif/esp-protocols/commit/60c87dd))
## [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

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
@ -25,4 +36,13 @@ menu "esp-modem"
The typical reason for failing SABM request without a delay is that
some devices (SIM800) send MSC requests just after opening a new DLCI.
config ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
bool "CMUX to support only short payloads (<128 bytes)"
default n
help
If enabled, the CMUX protocol would only use 1 byte size field.
You can use this option for devices that support only short CMUX payloads
to make the protocol more robust on noisy environments or when underlying
transport gets corrupted often (for example by Rx buffer overflows)
endmenu

View File

@ -5,4 +5,3 @@ idf_component_register(SRCS "modem_console_main.cpp"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -173,7 +173,7 @@ extern "C" void app_main(void)
ESP_LOGI(TAG, "Waiting for USB device connection...");
auto dte = create_usb_dte(&dte_config);
dte->set_error_cb([&](terminal_error err) {
ESP_LOGI(TAG, "error handler %d", err);
ESP_LOGI(TAG, "error handler %d", (int)err);
if (err == terminal_error::DEVICE_GONE) {
exit_signal.set(1);
}
@ -482,7 +482,7 @@ extern "C" void app_main(void)
// wait for exit
exit_signal.wait_any(1, UINT32_MAX);
s_repl->del(s_repl);
ESP_LOGI(TAG, "Exiting...%d", esp_get_free_heap_size());
ESP_LOGI(TAG, "Exiting...%" PRIu32, esp_get_free_heap_size());
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
// USB example runs in a loop to demonstrate hot-plugging and sudden disconnection features.
} // while (1)

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
*/
@ -28,7 +28,7 @@ static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
ESP_LOGI(TAG, "%" PRIu32 " bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
@ -57,7 +57,7 @@ static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
} else {
ESP_LOGI(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
ESP_LOGI(TAG, "%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 " packet loss, time %" PRIu32 "ms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks

View File

@ -1,3 +1,2 @@
idf_component_register(SRCS "modem_psm.c"
INCLUDE_DIRS ".")

View File

@ -1,7 +1,6 @@
# 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 "../..")

View File

@ -5,6 +5,10 @@
## Overview
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)
This example could be used in two different configurations:
1) Custom TCP transport: Implements a TCP transport in form of AT commands and uses it as custom transport for mqtt client.
2) Localhost listener: Uses standard transports to connect and forwards socket layer data from the client to the modem using AT commands.
### Supported IDF versions
This example is supported from IDF `v4.4`.
This example is supported from IDF `v5.0`.

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS mbedtls_wrap.cpp
tls_transport.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)

View File

@ -0,0 +1,9 @@
# Custom transports
This component is a placeholder of custom transports. It contains mbedTLS cxx wrapper which could be used to create a custom TLS layer on any kind of IO function.
# List of Transports
## TLS Transport
TLS layer on top of any custom transport. Very similar to `ssl_transport` (standard IDF transport), but this is customizable and could work on any kind of transport, not only the BSD socket based tcp transport (like `ssl_transport`).

View File

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <utility>
#include <span>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
using const_buf = std::span<const unsigned char>;
class Tls {
public:
enum class is_server : bool {};
enum class do_verify : bool {};
Tls();
virtual ~Tls();
bool init(is_server server, do_verify verify);
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
[[nodiscard]] bool set_ca_cert(const_buf crt);
virtual int send(const unsigned char *buf, size_t len) = 0;
virtual int recv(unsigned char *buf, size_t len) = 0;
size_t get_available_bytes();
protected:
mbedtls_ssl_context ssl_{};
mbedtls_x509_crt public_cert_{};
mbedtls_pk_context pk_key_{};
mbedtls_x509_crt ca_cert_{};
mbedtls_ssl_config conf_{};
mbedtls_ctr_drbg_context ctr_drbg_{};
mbedtls_entropy_context entropy_{};
virtual void delay() {}
private:
static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
static int bio_read(void *ctx, unsigned char *buf, size_t len);
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
};

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_transport.h"
/**
* @brief Initializes TLS transport based on mbetls wrapper
*
* @param parent Transport on top of which we run TLS layer
* @return Transport handle on success
*/
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent);

View File

@ -0,0 +1,134 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ssl.h"
#include "mbedtls_wrap.hpp"
bool Tls::init(is_server server, do_verify verify)
{
const char pers[] = "mbedtls_wrapper";
mbedtls_entropy_init(&entropy_);
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
int ret = mbedtls_ssl_config_defaults(&conf_, server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
if (ret) {
print_error("mbedtls_ssl_config_defaults", ret);
return false;
}
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
mbedtls_ssl_conf_authmode(&conf_, verify == do_verify{true} ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
if (ret) {
print_error("mbedtls_ssl_conf_own_cert", ret);
return false;
}
if (verify == do_verify{true}) {
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
}
ret = mbedtls_ssl_setup(&ssl_, &conf_);
if (ret) {
print_error("mbedtls_ssl_setup", ret);
return false;
}
return true;
}
void Tls::print_error(const char *function, int error_code)
{
static char error_buf[100];
mbedtls_strerror(error_code, error_buf, sizeof(error_buf));
printf("%s() returned -0x%04X\n", function, -error_code);
printf("-0x%04X: %s\n", -error_code, error_buf);
}
int Tls::handshake()
{
int ret = 0;
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, nullptr);
while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
print_error( "mbedtls_ssl_handshake returned", ret );
return -1;
}
delay();
}
return ret;
}
int Tls::bio_write(void *ctx, const unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->send(buf, len);
}
int Tls::bio_read(void *ctx, unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->recv(buf, len);
}
int Tls::write(const unsigned char *buf, size_t len)
{
return mbedtls_ssl_write( &ssl_, buf, len );
}
int Tls::read(unsigned char *buf, size_t len)
{
return mbedtls_ssl_read( &ssl_, buf, len );
}
bool Tls::set_own_cert(const_buf crt, const_buf key)
{
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.data(), crt.size());
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
ret = mbedtls_pk_parse_key(&pk_key_, key.data(), key.size(), nullptr, 0);
if (ret < 0) {
print_error("mbedtls_pk_parse_keyfile", ret);
return false;
}
return true;
}
bool Tls::set_ca_cert(const_buf crt)
{
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.data(), crt.size());
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
return true;
}
Tls::Tls()
{
mbedtls_x509_crt_init(&public_cert_);
mbedtls_pk_init(&pk_key_);
mbedtls_x509_crt_init(&ca_cert_);
}
int Tls::mbedtls_pk_parse_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen, const unsigned char *pwd, size_t pwdlen)
{
return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen, nullptr, nullptr);
}
size_t Tls::get_available_bytes()
{
return ::mbedtls_ssl_get_bytes_avail(&ssl_);
}
Tls::~Tls()
{
::mbedtls_ssl_config_free(&conf_);
::mbedtls_ssl_free(&ssl_);
::mbedtls_pk_free(&pk_key_);
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
}

View File

@ -0,0 +1,137 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_transport.h"
#include "mbedtls_wrap.hpp"
static const char *TAG = "tls_transport";
class TlsTransport: public Tls {
public:
explicit TlsTransport(esp_transport_handle_t parent) : Tls(), transport_(parent) {}
int send(const unsigned char *buf, size_t len) override;
int recv(unsigned char *buf, size_t len) override;
static bool set_func(esp_transport_handle_t tls_transport);
private:
esp_transport_handle_t transport_{};
int connect(const char *host, int port, int timeout_ms);
void delay() override;
struct transport {
static int connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms);
static int read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms);
static int write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms);
static int close(esp_transport_handle_t t);
static int poll_read(esp_transport_handle_t t, int timeout_ms);
static int poll_write(esp_transport_handle_t t, int timeout_ms);
static int destroy(esp_transport_handle_t t);
};
};
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent)
{
esp_transport_handle_t ssl = esp_transport_init();
auto *tls = new TlsTransport(parent);
esp_transport_set_context_data(ssl, tls);
TlsTransport::set_func(ssl);
return ssl;
}
int TlsTransport::send(const unsigned char *buf, size_t len)
{
return esp_transport_write(transport_, reinterpret_cast<const char *>(buf), len, 0);
}
int TlsTransport::recv(unsigned char *buf, size_t len)
{
int ret = esp_transport_read(transport_, reinterpret_cast<char *>(buf), len, 0);
if (ret == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return ret == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN ? 0 : ret;
}
bool TlsTransport::set_func(esp_transport_handle_t tls_transport)
{
return esp_transport_set_func(tls_transport, TlsTransport::transport::connect, TlsTransport::transport::read, TlsTransport::transport::write, TlsTransport::transport::close, TlsTransport::transport::poll_read, TlsTransport::transport::poll_write, TlsTransport::transport::destroy) == ESP_OK;
}
int TlsTransport::connect(const char *host, int port, int timeout_ms)
{
return esp_transport_connect(transport_, host, port, timeout_ms);
}
void TlsTransport::delay()
{
vTaskDelay(pdMS_TO_TICKS(500));
}
int TlsTransport::transport::connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
tls->init(is_server{false}, do_verify{false});
auto ret = tls->connect(host, port, timeout_ms);
if (ret < 0) {
return ret;
}
return tls->handshake();
}
int TlsTransport::transport::read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
if (tls->get_available_bytes() <= 0) {
int poll = esp_transport_poll_read(t, timeout_ms);
if (poll == -1) {
return ERR_TCP_TRANSPORT_CONNECTION_FAILED;
}
if (poll == 0) {
return ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
}
}
return tls->read(reinterpret_cast<unsigned char *>(buffer), len);
}
int TlsTransport::transport::write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll;
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
ESP_LOGW(TAG, "Poll timeout or error timeout_ms=%d", timeout_ms);
return poll;
}
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return tls->write(reinterpret_cast<const unsigned char *>(buffer), len);
}
int TlsTransport::transport::close(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_close(tls->transport_);
}
int TlsTransport::transport::poll_read(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_read(tls->transport_, timeout_ms);
}
int TlsTransport::transport::poll_write(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_write(tls->transport_, timeout_ms);
}
int TlsTransport::transport::destroy(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_destroy(tls->transport_);
}

View File

@ -6,6 +6,6 @@ endif()
idf_component_register(SRCS "modem_client.cpp"
"sock_dce.cpp"
"tcp_transport_at.cpp"
"${device_srcs}"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -1,5 +1,10 @@
menu "Example Configuration"
config EXAMPLE_CUSTOM_TCP_TRANSPORT
bool "Custom TCP transport"
help
Use custom TCP transport to connect to MQTT broker
choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96

View File

@ -15,14 +15,17 @@
#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"
#include "tcp_transport_mbedtls.h"
#include "tcp_transport_at.h"
#define BROKER_URL "mqtt.eclipseprojects.io"
#define BROKER_PORT 8883
static const char *TAG = "modem_client";
static EventGroupHandle_t event_group = NULL;
@ -31,7 +34,7 @@ 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_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRId32, 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;
@ -70,6 +73,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
}
}
extern "C" void app_main(void)
{
@ -101,24 +105,29 @@ extern "C" void app_main(void)
/* create the DCE and initialize network manually (using AT commands) */
auto dce = sock_dce::create(&dce_config, std::move(dte));
if (!dce->init_network()) {
if (!dce->init()) {
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.broker.address.port = BROKER_PORT;
mqtt_config.session.message_retransmit_timeout = 10000;
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
dce->start_listening(BROKER_PORT);
#else
mqtt_config.uri = "mqtt://127.0.0.1";
mqtt_config.message_retransmit_timeout = 10000;
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
esp_transport_handle_t at = esp_transport_at_init(dce.get());
esp_transport_handle_t ssl = esp_transport_tls_init(at);
mqtt_config.network.transport = ssl;
#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)) {
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
ESP_LOGE(TAG, "Failed to start DCE");
return;
}
@ -128,13 +137,16 @@ extern "C" void app_main(void)
}
ESP_LOGE(TAG, "Loop exit.. retrying");
// handle disconnections errors
if (!dce->init_network()) {
if (!dce->init()) {
ESP_LOGE(TAG, "Failed to reinit network");
return;
}
if (!dce->start("test.mosquitto.org", 1883)) {
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
ESP_LOGI(TAG, "Network reinitialized, retrying");
}
}
#else
vTaskDelay(portMAX_DELAY);
#endif
}

View File

@ -13,8 +13,6 @@
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__);

View File

@ -6,7 +6,6 @@
#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"
@ -132,7 +131,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
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) {
if (head_pos == recv_data + len) {
return ret::FAIL;
}
@ -160,17 +159,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
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);
on_read(recv_data, first_data_len);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
::send(sock, recv_data, actual_len, 0);
on_read(recv_data, actual_len);
} else if (data_to_recv > len) { // continue sending
::send(sock, recv_data, len, 0);
on_read(recv_data, len);
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);
on_read(recv_data, data_to_recv);
actual_len = data_to_recv;
}

View File

@ -6,7 +6,6 @@
#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"
@ -218,7 +217,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
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) {
if (head_pos == recv_data + len) {
return ret::FAIL;
}
@ -246,7 +245,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
ESP_LOGE(TAG, "TOO BIG");
return ret::FAIL;
}
size_t total_len = 0;
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;
@ -255,17 +254,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
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);
on_read(recv_data, first_data_len);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
::send(sock, recv_data, actual_len, 0);
on_read(recv_data, actual_len);
} else if (data_to_recv > len) { // continue sending
::send(sock, recv_data, len, 0);
on_read(recv_data, len);
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);
on_read(recv_data, data_to_recv);
actual_len = data_to_recv;
}

View File

@ -120,14 +120,14 @@ 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);
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, 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);
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
close_sock();
return false;
}
@ -145,7 +145,7 @@ bool DCE::sock_to_at()
return false;
}
if (state != status::IDLE) {
ESP_LOGE(TAG, "Unexpected state %d", state);
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
close_sock();
return false;
}
@ -192,14 +192,8 @@ bool DCE::accept_sock()
return false;
}
void DCE::init_sock(int port)
void DCE::start_listening(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);
@ -228,7 +222,7 @@ void DCE::init_sock(int port)
}
bool DCE::start(std::string host, int port)
bool DCE::connect(std::string host, int port)
{
dte->on_read(nullptr);
tcp_close();
@ -245,8 +239,14 @@ bool DCE::start(std::string host, int port)
return true;
}
bool DCE::init_network()
bool DCE::init()
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
esp_vfs_eventfd_register(&config);
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
dte->on_read(nullptr);
const int retries = 5;
int i = 0;

View File

@ -9,6 +9,7 @@
#include <cxx_include/esp_modem_dce_factory.hpp>
#include "socket_commands.inc"
#include "sock_commands.hpp"
#include <sys/socket.h>
#pragma once
@ -46,9 +47,36 @@ public:
{
return buffer_size;
}
void clear_offsets()
{
actual_read = 0;
}
size_t data_available()
{
return actual_read;
}
size_t has_data()
{
return total_len;
}
private:
static constexpr size_t buffer_size = 512;
bool on_read(char *data, size_t len)
{
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
::send(sock, data, len, 0);
#else
::memcpy(&buffer[actual_read], data, len);
actual_read += len;
#endif
return true;
}
ret recv(uint8_t *data, size_t len);
ret send(uint8_t *data, size_t len);
ret send(std::string_view response);
@ -59,6 +87,9 @@ private:
}
std::array<uint8_t, buffer_size> buffer;
size_t data_to_recv = 0;
size_t actual_read = 0;
size_t total_len = 0;
bool read_again = false;
int &sock;
int &data_ready_fd;
@ -78,13 +109,101 @@ esp_modem::return_type name(__VA_ARGS__);
#undef ESP_MODEM_DECLARE_DCE_COMMAND
bool init_network();
bool start(std::string host, int port);
bool init();
bool connect(std::string host, int port);
void init_sock(int port);
void start_listening(int port);
bool perform_sock();
void set_idle()
{
signal.set(IDLE);
}
bool wait_to_idle(uint32_t ms)
{
if (!signal.wait(IDLE, ms)) {
ESP_LOGE("dce", "Failed to get idle");
return false;
}
if (state != status::IDLE) {
ESP_LOGE("dce", "Unexpected state %d", static_cast<int>(state));
return false;
}
return true;
}
int sync_recv(char *buffer, int len, int timeout_ms)
{
if (!wait_to_idle(timeout_ms)) {
return 0;
}
at.clear_offsets();
state = status::RECEIVING;
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
int max_len = std::min(len, (int)at.get_buf_len());
at.start_receiving(max_len);
if (!signal.wait(IDLE, 500 + timeout_ms)) {
return 0;
}
int ret = at.data_available();
if (ret > 0) {
memcpy(buffer, at.get_buf(), ret);
}
set_idle();
return ret;
}
int sync_send(const char *buffer, size_t len, int timeout_ms)
{
int len_to_send = std::min(len, at.get_buf_len());
if (!wait_to_idle(timeout_ms)) {
return -1;
}
state = status::SENDING;
memcpy(at.get_buf(), buffer, len_to_send);
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
at.start_sending(len_to_send);
if (!signal.wait(IDLE, timeout_ms + 1000)) {
if (state == status::PENDING) {
state = status::IDLE;
} else {
return -1;
}
}
set_idle();
return len_to_send;
}
int wait_to_read(uint32_t ms)
{
if (at.has_data() > 0) {
ESP_LOGD("dce", "Data buffered in modem (len=%d)", at.has_data());
return 1;
}
struct timeval tv = {
.tv_sec = static_cast<time_t>(ms / 1000),
.tv_usec = 0,
};
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(data_ready_fd, &fdset);
int s = select(data_ready_fd + 1, &fdset, nullptr, nullptr, &tv);
if (s == 0) {
return 0;
} else if (s < 0) {
ESP_LOGE("dce", "select error %d", errno);
return -1;
}
if (FD_ISSET(data_ready_fd, &fdset)) {
ESP_LOGD("dce", "select read: modem data available");
return 1;
}
return -1;
}
private:
esp_modem::SignalGroup signal;

View File

@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "sock_dce.hpp"
#include "esp_log.h"
#include "esp_transport.h"
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
if (!dce->connect(host, port)) {
return -1;
}
if (dce->wait_to_idle(timeout_ms)) {
dce->set_idle();
return 1;
}
return -1;
}
static int tcp_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
auto ret = dce->wait_to_read(1000);
if (ret > 0) {
return dce->sync_recv(buffer, len, timeout_ms);
}
return ret;
}
static int tcp_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->sync_send(buffer, len, timeout_ms);
}
static int base_close(esp_transport_handle_t t)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->tcp_close() == esp_modem::command_result::OK ? 0 : -1;
}
static int base_poll_read(esp_transport_handle_t t, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->wait_to_read(timeout_ms);
}
static int base_poll_write(esp_transport_handle_t t, int timeout_ms)
{
// expect the socket is always writable
return 1;
}
static int base_destroy(esp_transport_handle_t transport)
{
return 0;
}
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce)
{
esp_transport_handle_t at = esp_transport_init();
if (!at) {
return nullptr;
}
esp_transport_set_context_data(at, (void *)dce);
esp_transport_set_func(at, tcp_connect, tcp_read, tcp_write, base_close, base_poll_read, base_poll_write, base_destroy);
return at;
}

View File

@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_transport.h"
#include "sock_dce.hpp"
/**
* @brief Initializes TCP transport based on AT commands
*
* @param DCE
* @return Transport handle on success
*/
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce);

View File

@ -1,3 +1,2 @@
idf_component_register(SRCS "pppos_client_main.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -60,7 +60,7 @@ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED
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_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
@ -102,7 +102,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
ESP_LOGI(TAG, "PPP state changed event %" PRIu32, event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = event_data;
@ -114,7 +114,7 @@ static void on_ppp_changed(void *arg, esp_event_base_t event_base,
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "IP event! %d", event_id);
ESP_LOGD(TAG, "IP event! %" PRIu32, event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;

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

@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
from __future__ import print_function, unicode_literals
def test_cmux_connection(dut):
"""
steps:
1. initializes connection with SIM800
2. checks we get an IP
3. checks for the MQTT events
"""
# Check the sequence of connecting, publishing, disconnecting
dut.expect('Modem has correctly entered multiplexed')
# Check for MQTT connection and the data event
dut.expect('TOPIC: /topic/esp-modem')
dut.expect('DATA: Hello modem')

View File

@ -0,0 +1,17 @@
CONFIG_IDF_TARGET="esp32c3"
# Override some defaults to enable PPP
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y
CONFIG_LWIP_PPP_PAP_SUPPORT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_PPP_ENABLE_IPV6=n
CONFIG_EXAMPLE_MODEM_UART_TX_PIN=4
CONFIG_EXAMPLE_MODEM_UART_RX_PIN=5
CONFIG_EXAMPLE_MODEM_DEVICE_SIM800=y
CONFIG_EXAMPLE_MODEM_DEVICE_BG96=n
CONFIG_EXAMPLE_MODEM_PPP_APN="lpwa.vodafone.com"
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP32_PANIC_PRINT_HALT=y
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_EXAMPLE_CLOSE_CMUX_AT_END=y

View File

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

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -86,9 +86,30 @@ public:
*/
int write(int i, uint8_t *data, size_t len);
/**
* @brief Recovers the protocol
*
* This restarts the CMUX state machine, which could have been in a wrong state due to communication
* issue on a lower layer.
*
* @return true on success
*/
bool recover();
private:
enum class protocol_mismatch_reason {
MISSED_LEAD_SOF,
MISSED_TRAIL_SOF,
WRONG_CRC,
UNEXPECTED_HEADER,
UNEXPECTED_DATA,
READ_BEHIND_BUFFER,
UNKNOWN
};
static uint8_t fcs_crc(const uint8_t frame[6]); /*!< Utility to calculate FCS CRC */
void data_available(uint8_t *data, size_t len); /*!< Called when valid data available */
bool data_available(uint8_t *data, size_t len); /*!< Called when valid data available (returns false on unexpected data format) */
void send_sabm(size_t i); /*!< Sending initial SABM */
void send_disconnect(size_t i); /*!< Sending closing request for each virtual or control terminal */
bool on_cmux_data(uint8_t *data, size_t len); /*!< Called from terminal layer when raw CMUX protocol data available */
@ -105,6 +126,7 @@ private:
bool on_header(CMuxFrame &frame);
bool on_payload(CMuxFrame &frame);
bool on_footer(CMuxFrame &frame);
void recover_protocol(protocol_mismatch_reason reason);
std::function<bool(uint8_t *data, size_t len)> read_cb[MAX_TERMINALS_NUM]; /*!< Function pointers to read callbacks */
std::shared_ptr<Terminal> term; /*!< The original terminal */

View File

@ -84,6 +84,11 @@ public:
return mode.set(dte.get(), device.get(), netif, m);
}
bool recover()
{
return dte->recover();
}
protected:
std::shared_ptr<DTE> dte;
std::shared_ptr<SpecificModule> device;

View File

@ -236,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

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -79,6 +79,13 @@ 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;
/**
@ -108,6 +115,13 @@ public:
*/
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
/**
* @brief Allows this DTE to recover from a generic connection issue
*
* @return true if success
*/
bool recover();
protected:
/**
* @brief Allows for locking the DTE
@ -122,10 +136,11 @@ 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 */
void handle_error(terminal_error err); /*!< Performs internal error handling */
[[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 */
@ -133,9 +148,82 @@ 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 */
std::function<void(terminal_error err)> user_error_cb; /*!< user callback on error event from attached terminals */
#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 */
{
return signal.wait_any(command_cb::GOT_LINE, time_ms);
}
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;
} else {
// if we clear the line callback, we check consistency (since we've locked the line processing)
if (signal.is_any(command_cb::GOT_LINE) && result == command_result::TIMEOUT) {
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
}
}
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

@ -14,7 +14,7 @@
.stop_bits = UART_STOP_BITS_1, \
.parity = UART_PARITY_DISABLE, \
.flow_control = ESP_MODEM_FLOW_CONTROL_NONE,\
.source_clk = UART_SCLK_APB, \
.source_clk = ESP_MODEM_DEFAULT_UART_CLK, \
.baud_rate = 115200, \
.tx_io_num = 25, \
.rx_io_num = 26, \

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -113,7 +113,7 @@ struct CMux::CMuxFrame {
}
};
void CMux::data_available(uint8_t *data, size_t len)
bool CMux::data_available(uint8_t *data, size_t len)
{
if (data && (type & FT_UIH) == FT_UIH && len > 0 && dlci > 0) { // valid payload on a virtual term
int virtual_term = dlci - 1;
@ -128,32 +128,38 @@ void CMux::data_available(uint8_t *data, size_t len)
#else
read_cb[virtual_term](data, len);
#endif
} else {
return false;
}
} else if (data == nullptr && type == 0x73 && len == 0) { // notify the initial SABM command
} else if (data == nullptr && type == (FT_UA | PF) && len == 0) { // notify the initial SABM command
Scoped<Lock> l(lock);
sabm_ack = dlci;
} else if (data == nullptr) {
} else if (data == nullptr && dlci > 0) {
int virtual_term = dlci - 1;
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
#ifdef DEFRAGMENT_CMUX_PAYLOAD
read_cb[virtual_term](payload_start, total_payload_size);
#endif
} else {
return false;
}
} else if ((type & FT_UIH) == FT_UIH && dlci == 0) { // notify the internal DISC command
if (len > 0 && (data[0] & 0xE1) == 0xE1) {
if ((len > 0 && (data[0] & 0xE1) == 0xE1) || (data == nullptr)) {
// Not a DISC, ignore (MSC frame)
return;
return true;
}
Scoped<Lock> l(lock);
sabm_ack = dlci;
} else {
return false;
}
return true;
}
bool CMux::on_init(CMuxFrame &frame)
{
if (frame.ptr[0] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed leading SOF, recovering...");
state = cmux_state::RECOVER;
recover_protocol(protocol_mismatch_reason::MISSED_LEAD_SOF);
return true;
}
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
@ -206,6 +212,7 @@ bool CMux::on_header(CMuxFrame &frame)
}
size_t payload_offset = std::min(frame.len, 4 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
#ifndef ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
if ((frame_header[3] & 1) == 0) {
if (frame_header_offset + frame.len <= 4) {
frame_header_offset += frame.len;
@ -215,12 +222,21 @@ bool CMux::on_header(CMuxFrame &frame)
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
payload_len = frame_header[4] << 7;
frame_header_offset += payload_offset - 1; // rewind frame_header back to hold only 6 bytes size
} else {
} else
#endif // ! ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
{
payload_len = 0;
frame_header_offset += payload_offset;
}
dlci = frame_header[1] >> 2;
type = frame_header[2];
// Sanity check for expected values of DLCI and type,
// since CRC could be evaluated after the frame payload gets received
if (dlci > MAX_TERMINALS_NUM || (frame_header[1] & 0x01) == 0 ||
(((type & FT_UIH) != FT_UIH) && type != (FT_UA | PF) ) ) {
recover_protocol(protocol_mismatch_reason::UNEXPECTED_HEADER);
return true;
}
payload_len += (frame_header[3] >> 1);
frame.advance(payload_offset);
state = cmux_state::PAYLOAD;
@ -232,12 +248,18 @@ bool CMux::on_payload(CMuxFrame &frame)
ESP_LOGD("CMUX", "Payload frame: dlci:%02x type:%02x payload:%d available:%d", dlci, type, payload_len, frame.len);
if (frame.len < payload_len) { // payload
state = cmux_state::PAYLOAD;
data_available(frame.ptr, frame.len); // partial read
if (!data_available(frame.ptr, frame.len)) { // partial read
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
payload_len -= frame.len;
return false;
} else { // complete
if (payload_len > 0) {
data_available(&frame.ptr[0], payload_len); // rest read
if (!data_available(&frame.ptr[0], payload_len)) { // rest read
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
}
frame.advance((payload_len));
state = cmux_state::FOOTER;
@ -257,16 +279,23 @@ bool CMux::on_footer(CMuxFrame &frame)
footer_offset = std::min(frame.len, 6 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, footer_offset);
if (frame_header[5] != SOF_MARKER) {
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
payload_start = nullptr;
total_payload_size = 0;
state = cmux_state::RECOVER;
recover_protocol(protocol_mismatch_reason::MISSED_TRAIL_SOF);
return true;
}
#ifdef ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
uint8_t crc = 0xFF - fcs_crc(frame_header);
if (crc != frame_header[4]) {
recover_protocol(protocol_mismatch_reason::WRONG_CRC);
return true;
}
#endif
frame.advance(footer_offset);
state = cmux_state::INIT;
frame_header_offset = 0;
data_available(nullptr, 0);
if (!data_available(nullptr, 0)) {
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
payload_start = nullptr;
total_payload_size = 0;
}
@ -280,7 +309,28 @@ bool CMux::on_cmux_data(uint8_t *data, size_t actual_len)
auto data_to_read = buffer.size - 128; // keep 128 (max CMUX payload) backup buffer)
if (payload_start) {
data = payload_start + total_payload_size;
data_to_read = payload_len + 2;
auto data_end = buffer.get() + buffer.size;
data_to_read = payload_len + 2; // 2 -- CMUX protocol footer
if (data + data_to_read >= data_end) {
ESP_LOGW("CUMX", "Failed to defragment longer payload (payload=%d)", payload_len);
// If you experience this error, your device uses longer payloads while
// the configured buffer is too small to defragment the payload properly.
// To resolve this issue you can:
// * Either increase `dte_buffer_size`
// * Or disable `ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD` in menuconfig
// Attempts to process the data accumulated so far (rely on upper layers to process correctly)
data_available(nullptr, 0);
if (payload_len > total_payload_size) {
payload_start = nullptr;
total_payload_size = 0;
} else {
// cannot continue with this payload, give-up and recover the protocol
recover_protocol(protocol_mismatch_reason::READ_BEHIND_BUFFER);
}
data_to_read = buffer.size;
data = buffer.get();
}
} else {
data = buffer.get();
}
@ -435,3 +485,19 @@ std::pair<std::shared_ptr<Terminal>, unique_buffer> CMux::detach()
{
return std::make_pair(std::move(term), std::move(buffer));
}
void esp_modem::CMux::recover_protocol(protocol_mismatch_reason reason)
{
ESP_LOGW("CMUX", "Restarting CMUX state machine (reason: %d)", static_cast<int>(reason));
payload_start = nullptr;
total_payload_size = 0;
frame_header_offset = 0;
state = cmux_state::RECOVER;
}
bool CMux::recover()
{
Scoped<Lock> l(lock);
recover_protocol(protocol_mismatch_reason::UNKNOWN);
return true;
}

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

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,53 +17,131 @@ 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
});
primary_term->set_error_cb([this](terminal_error err) {
if (user_error_cb) {
user_error_cb(err);
}
handle_error(err);
});
secondary_term->set_error_cb([this](terminal_error err) {
if (user_error_cb) {
user_error_cb(err);
}
handle_error(err);
});
}
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 +154,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 +178,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 +214,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 +257,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 +266,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
@ -184,8 +285,8 @@ void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
void DTE::set_error_cb(std::function<void(terminal_error err)> f)
{
secondary_term->set_error_cb(f);
primary_term->set_error_cb(f);
user_error_cb = std::move(f);
set_command_callbacks();
}
int DTE::read(uint8_t **d, size_t len)
@ -230,6 +331,49 @@ void DTE::on_read(got_line_cb on_read_cb)
});
}
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::recover()
{
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
return cmux_term->recover();
}
return false;
}
void DTE::handle_error(terminal_error err)
{
if (err == terminal_error::BUFFER_OVERFLOW ||
err == terminal_error::CHECKSUM_ERROR ||
err == terminal_error::UNEXPECTED_CONTROL_FLOW) {
recover();
}
}
#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

@ -49,9 +49,9 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
// check if PPP error events are enabled, if not, do enable the error occurred/state changed
// to notify the modem layer when switching modes
esp_netif_ppp_config_t ppp_config = { .ppp_phase_event_enabled = true, // assuming phase enabled, as earlier IDFs
.ppp_error_event_enabled = false
}; // don't provide cfg getters so we enable both events
esp_netif_ppp_config_t ppp_config = { };
ppp_config.ppp_phase_event_enabled = true; // assuming phase enabled, as earlier IDFs
ppp_config.ppp_error_event_enabled = false; // don't provide cfg getters so we enable both events
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
esp_netif_ppp_get_params(esp_netif, &ppp_config);
#endif // ESP-IDF >= v4.4
@ -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

@ -122,9 +122,7 @@ void FdTerminal::task()
} else {
if (FD_ISSET(f.fd, &rfds)) {
if (on_read_priv) {
if (on_read_priv(nullptr, 0)) {
on_read_priv = nullptr;
}
on_read_priv(nullptr, 0);
}
}
}

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

@ -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
*/
@ -21,6 +21,7 @@ void LoopbackTerm::stop()
int LoopbackTerm::write(uint8_t *data, size_t len)
{
if (inject_by) { // injection test: ignore what we write, but respond with injected data
signal.clear(1);
auto ret = std::async(&LoopbackTerm::batch_read, this);
async_results.push_back(std::move(ret));
return len;
@ -66,6 +67,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
data_len = response.length();
loopback_data.resize(data_len);
memcpy(&loopback_data[0], &response[0], data_len);
signal.clear(1);
auto ret = std::async(on_read, nullptr, data_len);
return len;
}
@ -81,6 +83,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
loopback_data.resize(data_len + len);
memcpy(&loopback_data[data_len], data, len);
data_len += len;
signal.clear(1);
auto ret = std::async(on_read, nullptr, data_len);
return len;
}
@ -102,9 +105,15 @@ int LoopbackTerm::read(uint8_t *data, size_t len)
return read_len;
}
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96), inject_by(0) {}
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96), inject_by(0)
{
init_signal();
}
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false), inject_by(0) {}
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false), inject_by(0)
{
init_signal();
}
int LoopbackTerm::inject(uint8_t *data, size_t len, size_t injected_by, size_t delay_before, size_t delay_after)
{
@ -132,6 +141,29 @@ void LoopbackTerm::batch_read()
}
Task::Delay(delay_after_inject);
}
signal.set(1);
}
LoopbackTerm::~LoopbackTerm() = default;
LoopbackTerm::~LoopbackTerm()
{
data_len = 0;
signal.wait(1, INT32_MAX); // wait "very long" to let the std::async() finish
}
void LoopbackTerm::init_signal()
{
// This indicates, that we can safely exit
// we clear the signal upon an async operation, so the destructor needs to wait until
// it's finished
signal.set(1);
}
void LoopbackTerm::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
{
user_on_read = std::move(f);
on_read = [this](uint8_t *data, size_t len) {
auto ret = user_on_read(data, len);
signal.set(1);
return ret;
};
}

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
*/
@ -31,11 +31,7 @@ public:
int read(uint8_t *data, size_t len) override;
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
{
Scoped<Lock> lock(on_read_guard);
on_read = std::move(f);
}
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override;
private:
enum class status_t {
@ -43,8 +39,10 @@ private:
STOPPED
};
void batch_read();
std::function<bool(uint8_t *data, size_t len)> user_on_read;
status_t status;
SignalGroup signal;
void init_signal();
std::vector<uint8_t> loopback_data;
size_t data_len;
bool pin_ok;

View File

@ -8,5 +8,3 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

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
*/
@ -27,7 +27,7 @@ static void on_modem_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == IP_EVENT) {
ESP_LOGD(TAG, "IP event! %d", event_id);
ESP_LOGD(TAG, "IP event! %" PRId32, event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
@ -61,7 +61,7 @@ static void on_modem_event(void *arg, esp_event_base_t event_base,
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
ESP_LOGI(TAG, "PPP state changed event %" PRId32, event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = static_cast<esp_netif_t *>(event_data);

View File

@ -4,5 +4,3 @@ idf_component_register(SRCS "esp_mqtt_cxx.cpp"
INCLUDE_DIRS "include"
REQUIRES mqtt
)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -153,7 +153,7 @@ Client::Client(esp_mqtt_client_config_t const &config) : handler(esp_mqtt_clien
void Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) noexcept
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id);
auto *event = static_cast<esp_mqtt_event_t *>(event_data);
auto &client = *static_cast<Client *>(handler_args);
switch (event->event_id) {
@ -215,7 +215,6 @@ void Client::on_disconnected(esp_mqtt_event_handle_t const event)
}
void Client::on_subscribed(esp_mqtt_event_handle_t const event)
{
printf("Subscribed to %.*s\r\n", event->topic_len, event->topic);
}
void Client::on_unsubscribed(esp_mqtt_event_handle_t const event)
{

View File

@ -1,3 +1,2 @@
idf_component_register(SRCS "mqtt_ssl_example.cpp"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,5 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/esp_mqtt_cxx:
version: "^0.1.0"

View File

@ -54,7 +54,7 @@ namespace mqtt = idf::mqtt;
extern "C" void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);

View File

@ -1,3 +1,2 @@
idf_component_register(SRCS "mqtt_tcp_example.cpp"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

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