Compare commits

..

48 Commits

Author SHA1 Message Date
cf845cc23e Merge pull request #453 from espressif-abhikroy/component/console_cmd_wifi
feat(console): Console for runtime wifi configuration
2023-12-22 23:54:09 +11:00
a532e76ec1 bump(console): First version [1.0.0]
1.0.0
Features
- Console for runtime wifi configuration (194d1179)
2023-12-22 23:48:44 +11:00
194d1179bf feat(console): Console for runtime wifi configuration 2023-12-22 23:44:16 +11:00
aadda22961 Merge pull request #461 from 0xFEEDC0DE64/fix-websocket-client-fin
fix(websocket): continuation after FIN in websocket client (#460) (IDFGH-11761)
2023-12-22 14:45:51 +04:00
a6845f4cdd Merge pull request #456 from david-cermak/fix/modem_pdp_context_timeout
fix(modem): Fixed set_pdp_context() command timeout
2023-12-21 07:21:31 +01:00
774d1c75e6 fix: continuation after FIN in websocket client (#460) 2023-12-19 17:00:17 +01:00
1d80cbc179 fix(modem): Fixed set_pdp_context() command timeout
Closes https://github.com/espressif/esp-protocols/issues/455
2023-12-19 16:43:59 +01:00
82c2cf8936 Merge pull request #451 from david-cermak/bump/modem_1.0.5
bump(modem): 1.0.4 -> 1.0.5
2023-12-06 09:54:37 +01:00
997cff5338 bump(modem): 1.0.4 -> 1.0.5
1.0.5
Major changes
- Added support for implementing user defined modules in standard C-API (Support for custom modules with C-API)
Bug Fixes
- Added test injecting unexpected replies (b220d1e, #426)
- Fixed inconsistent state on data after OK (bf99f28, #426)
- TLS example: Added restore session support in mbedtls-wrap (79d38e5)
- Fixed examples to show netif on ppp-changed event (a70b197)
- remove unused GNU Make based buildsystem files (d6b6f63)
- specify override_path in example manifest files (5b78da4)
- Added test-cases that exercise mode transitions (aff571d)
- Fixed mode transitions between any state and UNDEF (93cb2ca, #320)
- Fixed API docs within doxygen comments (020b407)
- Support for custom modules with C-API (0254d50)
- Fix CRLF issue with esp_modem_c_api.cpp (2661b4d)
Updated
- ci(common): Created reusable action for host and coverage tests (9ad04de)
2023-12-06 09:22:39 +01:00
ed8bbf2666 Merge pull request #447 from david-cermak/fix/modem_command_state_consistent_test
fix(modem): Added test injecting unexpected replies
2023-12-06 09:20:27 +01:00
b220d1ee5e fix(modem): Added test injecting unexpected replies
This test exhibits an issue of getting unexpected data just after
completing a command. The DTE rewrites the state to TIMEOUT, but the
signal has been already triggered, thus results in an unexpected state.

Related to Closes https://github.com/espressif/esp-protocols/issues/426
2023-12-06 09:09:47 +01:00
671190bc6d Merge pull request #446 from david-cermak/fix/modem_command_state_consistency
fix(modem): Fixed inconsistent state on data after OK
2023-12-06 09:06:23 +01:00
1bf6843b3e Merge pull request #332 from espressif-abhikroy/components/console_cmd_ifconfig
Added component: Console for runtime network interface configuration
2023-11-30 08:23:15 +01:00
b78569860d Merge pull request #438 from espressif-abhikroy/components/console_cmd_ping
bump(console): First version [1.0.0]
2023-11-30 08:19:43 +01:00
27f955abfa bump(console): First version [1.0.0]
1.0.0
Features
- Added ping command to console component (7babdeb9)
2023-11-30 18:14:59 +11:00
bf99f287bc fix(modem): Fixed inconsistent state on data after OK
If we received response in two chunks and the first one completes the
command (result=OK or FAIL), and the second chunk pre-empts command processing,
then we performed processing again rewritting the result back to
TIMEOUT. This would invalidate the command, but also causes an
exception: ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE)
Fixed by checking if the processing already finished in process_line().

Closes https://github.com/espressif/esp-protocols/issues/426
2023-11-29 20:24:53 +01:00
909e8d9494 Merge pull request #441 from david-cermak/fix/apply_examples_idf_component_refs
fix(common): Re-applied refs to common comps idf_component.yml
2023-11-28 17:44:13 +01:00
0c6bb4bedc Merge pull request #360 from david-cermak/fix/modem_example_mbedtls_wrapper
fix(modem): TLS example: Add restore session support in mbedtls wrapper
2023-11-28 17:12:10 +01:00
afbca5343e Merge pull request #416 from david-cermak/fix/modem_undef_mode
fix(modem): Fixed mode transitions between any state and UNDEF
2023-11-28 17:11:43 +01:00
3f49583761 Merge pull request #443 from gabsuren/websocket_1.2.1
bump(websocket): 1.2.0 -> 1.2.1
2023-11-28 17:20:14 +04:00
c6448c3bd1 bump(websocket): 1.2.0 -> 1.2.1
1.2.1
Bug Fixes
- consider failure if return value of `esp_websocket_client_send_with_exact_opcode` less than 0 (f523b4d)
- fix of return value for `esp_websocket_client_send_with_opcode` API (ba33588)
2023-11-28 16:42:55 +04:00
f523b4dc84 fix(websocket): consider failure if return value of esp_websocket_client_send_with_exact_opcode less than 0 2023-11-28 16:40:10 +04:00
6fda4c134e Merge pull request #442 from gabsuren/fix/websocket_return_opcode
fix(websocket): fix of return value for `esp_websocket_client_send_with_opcode` API (IDF-8745)
2023-11-28 15:51:27 +04:00
ba33588008 fix(websocket): fix of return value for esp_websocket_client_send_with_opcode API 2023-11-28 12:37:31 +04:00
9fe44a4504 fix(common): Re-applie refs to common comps idf_component.yml
This reverts commit 74fc228cdf.
This reverts commit b176d3abbb.
This reverts commit f9e0281a04.
2023-11-28 09:32:52 +01:00
9758379921 Merge pull request #440 from david-cermak/fix/revert_example_common_comp_refs
Temporarily revert IDF component references in examples
2023-11-28 08:15:28 +01:00
f52a38ba34 bump(websocket): 1.1.0 -> 1.2.0
1.2.0
Features
- Added new API `esp_websocket_client_append_header` (39e9725)
- Added new APIs to support fragmented messages transmission (fae80e2)
Bug Fixes
- Reference common component from IDF (74fc228)
- Revert referencing protocol_examples_common from IDF (b176d3a)
- reference protocol_examples_common from IDF (025ede1)
- specify override_path in example manifests (d5e7898)
- Return status code correctly on esp_websocket_client_send_with_opcode (ac8f1de)
- Fix pytest exclusion, gitignore, and changelog checks (2696221)
2023-11-28 07:42:49 +01:00
74fc228cdf fix(common): Reference common component from IDF 2023-11-28 07:42:28 +01:00
b176d3abbb fix(websocket): Revert referencing protocol_examples_common from IDF
This reverts commit 025ede12e8.
2023-11-28 07:23:32 +01:00
f9e0281a04 fix(asio): Revert referencing protocol_examples_common from IDF
This reverts commit 09abb18be6.
2023-11-28 07:21:56 +01:00
76a4a9f97a bump(console): First version [1.0.0]
1.0.0
Features
- Console for runtime network interface configuration and monitoring (8bab1420)
2023-11-24 22:12:33 +11:00
8bab1420d3 feat(console): Console for runtime network interface configuration and monitoring 2023-11-24 20:07:33 +11:00
9c555b90d4 Merge pull request #385 from espressif-abhikroy/components/console_cmd_ping
feat(console): Added ping command to console component
2023-11-23 19:58:33 +11:00
79d38e54f2 fix(modem): TLS example: Added restore session support in mbedtls-wrap
Reusable component in modem_tcp_client example implements a simple
mbedtls wrapper. This update add support for mbedtls deinit() and for
saving and restoring TLS session.
2023-11-23 09:56:01 +01:00
7babdeb970 feat(console): Added ping command to console component 2023-11-23 01:17:43 +11:00
7faa97450b Merge pull request #434 from david-cermak/bugfix/modem_examples_print_netif
fix(modem): Fixed examples to show netif on ppp-changed event
2023-11-16 17:52:03 +01:00
cc40cc1f84 Merge pull request #413 from david-cermak/feat/new_modem_cmd
Added support for implementing user defined modules in standard C-API
2023-11-16 17:51:28 +01:00
a70b197d02 fix(modem): Fixed examples to show netif on ppp-changed event 2023-11-16 10:10:02 +01:00
f50edd40b5 Merge pull request #425 from euripedesrocha/asio_contributing_guide
bump(asio): 1.28.0~0 -> 1.28.2~0
2023-11-14 14:50:47 +01:00
28f1e430fd bump(asio): 1.28.0~0 -> 1.28.2~0
1.28.2~0

Bug Fixes
- reference protocol_examples_common from IDF ([09abb18](https://github.com/espressif/esp-protocols/commit/09abb18))
- specify override_path in example manifest files ([1d8923c](https://github.com/espressif/esp-protocols/commit/1d8923c))

Updated
- docs(asio): Updates asio docs ([ce9337d](https://github.com/espressif/esp-protocols/commit/ce9337d))
2023-11-14 13:48:12 +01:00
93f5f7bf58 Merge pull request #427 from gabsuren/fix/mdns_config
mdns: enable CONFIG_ESP_WIFI_ENABLED if CONFIG_SOC_WIFI_SUPPORTED is also enabled (for ESP-IDF <= 5.1)
2023-11-14 16:24:10 +04:00
d20a718320 fix(mdns): enable CONFIG_ESP_WIFI_ENABLED if CONFIG_SOC_WIFI_SUPPORTED is also enabled (for ESP-IDF <= 5.1) 2023-11-14 14:48:42 +04:00
5396de42d0 docs(modem): Added documentation about mode transitions
Closes https://github.com/espressif/esp-protocols/issues/318
2023-11-07 12:35:06 +01:00
aff571df2c fix(modem): Added test-cases that exercise mode transitions 2023-11-07 12:35:06 +01:00
93cb2caadb fix(modem): Fixed mode transitions between any state and UNDEF
Closes https://github.com/espressif/esp-protocols/issues/320
2023-11-07 12:35:00 +01:00
020b407472 fix(modem): Fixed API docs within doxygen comments 2023-11-02 15:46:38 +01:00
0254d50128 fix(modem): Support for custom modules with C-API
MAJOR CHANGE: Added support for implementing user defined modules in standard C-API
2023-11-02 13:35:07 +01:00
2661b4d28c fix(modem): Fix CRLF issue with esp_modem_c_api.cpp 2023-11-02 13:35:07 +01:00
78 changed files with 3173 additions and 481 deletions

View File

@ -0,0 +1,32 @@
name: "console_cmd_ifconfig: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_console_cmd_ifconfig:
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/console_cmd_ifconfig/examples" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@ -0,0 +1,32 @@
name: "console_cmd_ping: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_console_cmd_ping:
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/console_cmd_ping/examples" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@ -0,0 +1,32 @@
name: "console_cmd_wifi: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_console_cmd_wifi:
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/console_cmd_wifi/examples" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app

View File

@ -94,5 +94,8 @@ jobs:
components/esp_websocket_client;
components/mdns;
components/console_simple_init;
components/console_cmd_ping;
components/console_cmd_ifconfig;
components/console_cmd_wifi;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

View File

@ -29,9 +29,8 @@ e.g.
## Creating a new component
Steps:
1. Add a file named .cz.yaml to the root of the component.
Once the commit containing a new component is ready follow the steps below:
1. Add a file named .cz.yaml to the root of the component and add it to your commit.
The template for .cz.yaml should look like this:
```
@ -50,6 +49,8 @@ commitizen:
Replace [component], [version] and [scope] with the specific component name, version and scope you are working with. This command will help you bump the version of the component with the provided details.
Note: It is crucial to adhere to the above steps when introducing a new component. Never merge the code for a new component without first implementing a bump commit.
## Release process
When releasing a new component version we have to:

View File

@ -37,3 +37,11 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
### console_simple_init
* Brief introduction [README](components/console_simple_init/README.md)
### console_cmd_ping
* Brief introduction [README](components/console_cmd_ping/README.md)
### console_cmd_ifconfig
* Brief introduction [README](components/console_cmd_ifconfig/README.md)

View File

@ -1,5 +1,16 @@
# Changelog
## [1.28.2~0](https://github.com/espressif/esp-protocols/commits/asio-1.28.2_0)
### Bug Fixes
- reference protocol_examples_common from IDF ([09abb18](https://github.com/espressif/esp-protocols/commit/09abb18))
- specify override_path in example manifest files ([1d8923c](https://github.com/espressif/esp-protocols/commit/1d8923c))
### Updated
- docs(asio): Updates asio docs ([ce9337d](https://github.com/espressif/esp-protocols/commit/ce9337d))
## [1.28.0~0](https://github.com/espressif/esp-protocols/commits/asio-1.28.0~0)
### Features

View File

@ -0,0 +1,40 @@
# Information for Contributors
Contributions in the form of pull requests, issue reports, and feature requests are welcome!
## Updating ASIO
ASIO is managed as a submodule, to updated the version first the [espressif](github.com/espressif/asio) ASIO fork must be updated.
## Release process
When releasing a new component version we have to:
* Update the submodule reference
* Update the version number
* Update the changelog
And the automation process takes care of the last steps:
* Create the version tag in this repository
* Deploy the component to component registry
* Update the documentation
This process needs to be manually handled for ASIO component since commitizen doesn't accept the versioning schema used.
* Increment manually the version in the [manifest file](idf_component.yml)
* Export environment variables for changelog generation:
- CZ_PRE_CURRENT_TAG_VERSION
- CZ_PRE_NEW_TAG_VERSION
- CZ_PRE_NEW_VERSION
* Run `python ../../ci/changelog.py asio` from this directory to generate the change log
* Check the updated `CHANGELOG.md`
* Commit the changes with the adequated message format.
```
bump(asio): $current_version -> $new_version
$Changelog for the version
```
* Create a PR
Once the PR is merged, the CI job tags the merge commit, creates a new release, builds and deploys documentation and the new component to the component registry

View File

@ -1,4 +1,4 @@
version: "1.28.0~0"
version: "1.28.2~0"
description: Cross-platform C++ library for network and I/O programming
url: https://github.com/espressif/esp-protocols/tree/master/components/asio
issues: https://github.com/espressif/esp-protocols/issues

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(console): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py console_cmd_ifconfig
tag_format: console_cmd_ifconfig-v$version
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ifconfig-v1.0.0)
### Features
- Console for runtime network interface configuration and monitoring ([8bab1420](https://github.com/espressif/esp-protocols/commit/8bab1420))

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "console_ifconfig.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_netif console esp_eth
WHOLE_ARCHIVE)

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,64 @@
# Console command ifconfig
The component offers a console with a command that enables runtime network interface configuration and monitoring for any example project.
## API
### Steps to enable console in an example code:
1. Add this component to your project using ```idf.py add-dependency``` command.
2. In the main file of the example, add the following line:
```c
#include "console_ifconfig.h"
```
3. Ensure esp-netif and NVS flash is initialized and default event loop is created in your app_main():
```c
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
```
4. In your app_main() function, add the following line as the last line:
```c
ESP_ERROR_CHECK(console_cmd_init()); // Initialize console
// Register all plugin command added to your project
ESP_ERROR_CHECK(console_cmd_all_register());
// To register only ifconfig command skip calling console_cmd_all_register()
ESP_ERROR_CHECK(console_cmd_ifconfig_register());
ESP_ERROR_CHECK(console_cmd_start()); // Start console
```
### Adding a plugin command or component:
To add a plugin command or any component from IDF component manager into your project, simply include an entry within the `idf_component.yml` file.
For more details refer [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html)
## Suported command:
### Ifconfig:
```
ifconfig help: Prints the help text for all ifconfig commands
ifconfig netif create/destroy <ethernet handle id>/<iface>: Create or destroy a network interface with the specified ethernet handle or interface name
ifconfig eth init/deinit/show: Initialize, deinitialize and display a list of available ethernet handle
ifconfig: Display a list of all esp_netif interfaces along with their information
ifconfig <iface>: Provide the details of the named interface
ifconfig <iface> default: Set the specified interface as the default interface
ifconfig <iface> ip6: Enable IPv6 on the specified interface
ifconfig <iface> up: Enable the specified interface
ifconfig <iface> down: Disable the specified interface
ifconfig <iface> link <up/down>: Enable or disable the link of the specified interface
ifconfig <iface> napt <enable/disable>: Enable or disable NAPT on the specified interface.
ifconfig <iface> ip <ipv4 addr>: Set the IPv4 address of the specified interface
ifconfig <iface> mask <ipv4 addr>: Set the subnet mask of the specified interface
ifconfig <iface> gw <ipv4 addr>: Set the default gateway of the specified interface
ifconfig <iface> staticip: Enables static ip
ifconfig <iface> dhcp server <enable/disable>: Enable or disable the DHCP server.(Note: DHCP server is not supported yet)
ifconfig <iface> dhcp client <enable/disable>: Enable or disable the DHCP client.
Note: Disabling the DHCP server and client enables the use of static IP configuration.
```

View File

@ -0,0 +1,668 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_console.h"
#include "esp_event.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "lwip/ip6.h"
#include "lwip/opt.h"
#include "ethernet_init.h"
#include "console_ifconfig.h"
#if IP_NAPT
#include "lwip/lwip_napt.h"
#endif
/**
* Static registration of this plugin is achieved by defining the plugin description
* structure and placing it into .console_cmd_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = {
.name = "console_cmd_ifconfig",
.plugin_regd_fn = &console_cmd_ifconfig_register
};
typedef struct netif_op_t {
char *name;
esp_err_t (*operation)(struct netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
int arg_cnt;
int start_index;
char *help;
int netif_flag;
} netif_op_t;
static esp_err_t ifcfg_help_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_print_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_lwip_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_basic_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_ip_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_napt_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_addr_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_netif_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static esp_err_t ifcfg_eth_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif);
static const char *TAG = "console_ifconfig";
netif_op_t cmd_list[] = {
{.name = "help", .operation = ifcfg_help_op, .arg_cnt = 2, .start_index = 1, .netif_flag = false, .help = "ifconfig help: Prints the help text for all ifconfig commands"},
{.name = "netif", .operation = ifcfg_netif_op, .arg_cnt = 4, .start_index = 1, .netif_flag = false, .help = "ifconfig netif create/destroy <ethernet handle id>/<iface>: Create or destroy a network interface with the specified ethernet handle or interface name"},
{.name = "eth", .operation = ifcfg_eth_op, .arg_cnt = 3, .start_index = 1, .netif_flag = false, .help = "ifconfig eth init/deinit/show: Initialize, deinitialize and display a list of available ethernet handle"},
{.name = "ifconfig", .operation = ifcfg_print_op, .arg_cnt = 1, .start_index = 0, .netif_flag = false, .help = "ifconfig: Display a list of all esp_netif interfaces along with their information"},
{.name = "ifconfig", .operation = ifcfg_print_op, .arg_cnt = 2, .start_index = 0, .netif_flag = true, .help = "ifconfig <iface>: Provide the details of the named interface"},
{.name = "default", .operation = ifcfg_basic_op, .arg_cnt = 3, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> default: Set the specified interface as the default interface"},
{.name = "ip6", .operation = ifcfg_basic_op, .arg_cnt = 3, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> ip6: Enable IPv6 on the specified interface"},
{.name = "up", .operation = ifcfg_lwip_op, .arg_cnt = 3, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> up: Enable the specified interface"},
{.name = "down", .operation = ifcfg_lwip_op, .arg_cnt = 3, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> down: Disable the specified interface"},
{.name = "link", .operation = ifcfg_lwip_op, .arg_cnt = 4, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> link <up/down>: Enable or disable the link of the specified interface"},
{.name = "napt", .operation = ifcfg_napt_op, .arg_cnt = 4, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> napt <enable/disable>: Enable or disable NAPT on the specified interface."},
{.name = "ip", .operation = ifcfg_ip_op, .arg_cnt = 4, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> ip <ipv4 addr>: Set the IPv4 address of the specified interface"},
{.name = "mask", .operation = ifcfg_ip_op, .arg_cnt = 4, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> mask <ipv4 addr>: Set the subnet mask of the specified interface"},
{.name = "gw", .operation = ifcfg_ip_op, .arg_cnt = 4, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> gw <ipv4 addr>: Set the default gateway of the specified interface"},
{.name = "staticip", .operation = ifcfg_addr_op, .arg_cnt = 3, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> staticip: Enables static ip"},
{.name = "dhcp", .operation = ifcfg_addr_op, .arg_cnt = 5, .start_index = 2, .netif_flag = true, .help = "ifconfig <iface> dhcp server <enable/disable>: Enable or disable the DHCP server.(Note: DHCP server is not supported yet)\n ifconfig <iface> dhcp client <enable/disable>: Enable or disable the DHCP client.\nNote: Disabling the DHCP server and client enables the use of static IP configuration."},
};
static esp_err_t ifcfg_help_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
int cmd_count = sizeof(cmd_list) / sizeof(cmd_list[0]);
for (int i = 0; i < cmd_count; i++) {
if ((cmd_list[i].help != NULL) && (strlen(cmd_list[i].help) != 0)) {
printf(" %s\n", cmd_list[i].help);
}
}
return ESP_OK;
}
static esp_netif_t *get_esp_netif_from_ifname(char *if_name)
{
esp_netif_t *esp_netif = NULL;
esp_err_t ret = ESP_FAIL;
char interface[10];
/* Get interface details and obtain the global IPv6 address */
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
while ((esp_netif = esp_netif_next(esp_netif)) != NULL) {
#else
while ((esp_netif = esp_netif_next_unsafe(esp_netif)) != NULL) {
#endif
ret = esp_netif_get_netif_impl_name(esp_netif, interface);
if ((ESP_FAIL == ret) || (NULL == esp_netif)) {
ESP_LOGE(TAG, "No interface available");
return NULL;
}
if (!strcmp(interface, if_name)) {
return esp_netif;
}
}
return NULL;
}
static esp_err_t ifcfg_basic_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
/* Set Default */
if (!strcmp("default", argv[self->start_index])) {
esp_netif_set_default_netif(esp_netif);
return ESP_OK;
}
/* Enable IPv6 on this interface */
if (!strcmp("ip6", argv[self->start_index])) {
ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(esp_netif));
return ESP_OK;
}
return ESP_FAIL;
}
static esp_err_t ifcfg_lwip_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
struct netif *lwip_netif = esp_netif_get_netif_impl(esp_netif);
if (NULL == lwip_netif) {
ESP_LOGE(TAG, "lwip interface %s not available", argv[1]);
return ESP_OK;
}
/* Enable/Disable Interface */
if (!strcmp("up", argv[self->start_index])) {
netif_set_up(lwip_netif);
return ESP_OK;
}
if (!strcmp("down", argv[self->start_index])) {
netif_set_down(lwip_netif);
return ESP_OK;
}
/* Enable/Disable link */
if (!strcmp("link", argv[self->start_index])) {
if (!strcmp("up", argv[self->start_index + 1])) {
netif_set_link_up(lwip_netif);
}
if (!strcmp("down", argv[self->start_index + 1])) {
netif_set_down(lwip_netif);
netif_set_link_down(lwip_netif);
}
return ESP_OK;
}
return ESP_FAIL;
}
static esp_err_t ifcfg_ip_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
esp_netif_ip_info_t ip_info = {0};
esp_netif_dhcpc_stop(esp_netif);
esp_netif_get_ip_info(esp_netif, &ip_info);
if (!strcmp("ip", argv[self->start_index])) {
ESP_LOGI(TAG, "Setting ip: %s", argv[self->start_index + 1]);
inet_aton(argv[self->start_index + 1], &ip_info.ip.addr);
esp_netif_set_ip_info(esp_netif, &ip_info);
return ESP_OK;
} else if (!strcmp("mask", argv[self->start_index])) {
ESP_LOGI(TAG, "Setting mask: %s", argv[self->start_index + 1]);
inet_aton(argv[self->start_index + 1], &ip_info.netmask.addr);
esp_netif_set_ip_info(esp_netif, &ip_info);
return ESP_OK;
} else if (!strcmp("gw", argv[self->start_index])) {
ESP_LOGI(TAG, "Setting gw: %s", argv[self->start_index + 1]);
inet_aton(argv[self->start_index + 1], &ip_info.gw.addr);
esp_netif_set_ip_info(esp_netif, &ip_info);
return ESP_OK;
}
return ESP_FAIL;
}
#if IP_NAPT
static esp_err_t set_napt(char *if_name, bool state)
{
esp_netif_t *esp_netif = NULL;
esp_err_t ret = ESP_FAIL;
char interface[10];
/* Get interface details and own global ipv6 address */
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
esp_netif = esp_netif_next(esp_netif);
#else
esp_netif = esp_netif_next_unsafe(esp_netif);
#endif
ret = esp_netif_get_netif_impl_name(esp_netif, interface);
if ((ESP_FAIL == ret) || (NULL == esp_netif)) {
ESP_LOGE(TAG, "No interface available");
return ESP_FAIL;
}
if (!strcmp(interface, if_name)) {
struct netif *lwip_netif = esp_netif_get_netif_impl(esp_netif);
ip_napt_enable_netif(lwip_netif, state);
return ESP_OK;
}
}
return ESP_FAIL;
}
#endif
static esp_err_t ifcfg_napt_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
#if IP_NAPT
if (!strcmp("napt", argv[self->start_index])) {
ESP_LOGI(TAG, "Setting napt %s on %s", argv[self->start_index + 1], argv[1]);
if (!strcmp(argv[self->start_index + 1], "enable")) {
return set_napt(argv[1], true);
} else if (!strcmp(argv[self->start_index + 1], "disable")) {
return set_napt(argv[1], false);
} else {
ESP_LOGI(TAG, "Invalid argument: %s", argv[self->start_index + 1]);
}
return ESP_FAIL;
}
#endif
ESP_LOGE(TAG, "NAPT not enabled in menuconfig");
return ESP_OK;
}
static esp_err_t ifcfg_addr_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
if (!strcmp("staticip", argv[self->start_index])) {
esp_netif_dhcpc_stop(esp_netif);
//esp_netif_dhcps_stop(esp_netif);
return ESP_OK;
} else if (!strcmp("server", argv[self->start_index + 1])) { // Server
if (!strcmp("enable", argv[self->start_index + 2])) {
ESP_LOGW(TAG, "DHCP Server configuration is not supported yet."); // TBD
//esp_netif_dhcps_start(esp_netif);
return ESP_OK;
} else if (!strcmp("disable", argv[self->start_index + 2])) {
ESP_LOGW(TAG, "DHCP Server configuration is not supported yet."); // TBD
//esp_netif_dhcps_stop(esp_netif);
return ESP_OK;
}
ESP_LOGE(TAG, "Invalid argument");
return ESP_FAIL;
} else if (!strcmp("client", argv[self->start_index + 1])) { // Client
if (!strcmp("enable", argv[self->start_index + 2])) {
esp_netif_dhcpc_start(esp_netif);
return ESP_OK;
} else if (!strcmp("disable", argv[self->start_index + 2])) {
esp_netif_dhcpc_stop(esp_netif);
return ESP_OK;
}
ESP_LOGE(TAG, "Invalid argument");
return ESP_FAIL;
}
return ESP_FAIL;
}
static void print_iface_details(esp_netif_t *esp_netif)
{
esp_netif_ip_info_t ip_info;
uint8_t mac[NETIF_MAX_HWADDR_LEN];
char interface[10];
int ip6_addrs_count = 0;
esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES];
esp_err_t ret = ESP_FAIL;
esp_netif_dhcp_status_t status;
struct netif *lwip_netif = esp_netif_get_netif_impl(esp_netif);
/* Print Interface Name and Number */
ret = esp_netif_get_netif_impl_name(esp_netif, interface);
if ((ESP_FAIL == ret) || (NULL == esp_netif)) {
ESP_LOGE(TAG, "No interface available");
return;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
if (esp_netif_get_default_netif() == esp_netif) {
ESP_LOGI(TAG, "Interface Name: %s (DEF)", interface);
} else {
ESP_LOGI(TAG, "Interface Name: %s", interface);
}
#else
ESP_LOGI(TAG, "Interface Name: %s", interface);
#endif
ESP_LOGI(TAG, "Interface Number: %d", lwip_netif->num);
/* Print MAC address */
esp_netif_get_mac(esp_netif, mac);
ESP_LOGI(TAG, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1],
mac[2], mac[3], mac[4], mac[5]);
/* Print DHCP status */
if (ESP_OK == esp_netif_dhcps_get_status(esp_netif, &status)) {
ESP_LOGI(TAG, "DHCP Server Status: %s", (status == ESP_NETIF_DHCP_STARTED) || (status == ESP_NETIF_DHCP_STOPPED) ? "enabled" : "disabled");
} else if ((ESP_OK == esp_netif_dhcpc_get_status(esp_netif, &status))) {
if (ESP_NETIF_DHCP_STOPPED == status) {
ESP_LOGI(TAG, "Static IP");
} else {
ESP_LOGI(TAG, "DHCP Client Status: %s", status ? "enabled" : "disabled");
}
}
/* Print IP Info */
esp_netif_get_ip_info(esp_netif, &ip_info);
ESP_LOGI(TAG, "IP: " IPSTR ", MASK: " IPSTR ", GW: " IPSTR, IP2STR(&(ip_info.ip)), IP2STR(&(ip_info.netmask)), IP2STR(&(ip_info.gw)));
#if IP_NAPT
/* Print NAPT status*/
ESP_LOGI(TAG, "NAPT: %s", lwip_netif->napt ? "enabled" : "disabled");
#endif
/* Print IPv6 Address */
ip6_addrs_count = esp_netif_get_all_ip6(esp_netif, ip6);
for (int j = 0; j < ip6_addrs_count; ++j) {
ESP_LOGI(TAG, "IPv6 address: " IPV6STR, IPV62STR(ip6[j]));
}
/* Print Interface and Link Status*/
ESP_LOGI(TAG, "Interface Status: %s", esp_netif_is_netif_up(esp_netif) ? "UP" : "DOWN");
ESP_LOGI(TAG, "Link Status: %s\n", netif_is_link_up(lwip_netif) ? "UP" : "DOWN");
}
static esp_err_t ifcfg_print_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
/* Print interface details */
if (2 == argc) {
print_iface_details(esp_netif);
return ESP_OK;
}
/* Get interface details and own global ipv6 address of all interfaces */
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
esp_netif = esp_netif_next(esp_netif);
#else
esp_netif = esp_netif_next_unsafe(esp_netif);
#endif
print_iface_details(esp_netif);
}
return ESP_OK;
}
/* Maximum number of interface that can be added */
#define MAX_ETH_NETIF_COUNT (10)
typedef enum {
UNINITIALIZED = 0,
ETH_INITIALIZED = 1,
NETIF_CREATED = 2,
NETIF_DESTROYED = 3,
ETH_DEINITIALIZED = 4
} iface_state;
typedef struct {
esp_netif_t *esp_netif;
esp_eth_handle_t *eth_handle;
esp_eth_netif_glue_handle_t eth_glue;
iface_state state;
} iface_desc;
static iface_desc iface_list[MAX_ETH_NETIF_COUNT];
static uint8_t netif_count;
static uint8_t eth_init_flag = false;
static uint8_t eth_port_cnt_g = 0;
static esp_err_t get_netif_config(uint16_t id, esp_netif_config_t *eth_cfg_o)
{
/* Create new default instance of esp-netif for Ethernet */
char *if_key;
if (asprintf(&if_key, "IFC_ETH%d", id) == -1) {
return ESP_FAIL;
}
esp_netif_inherent_config_t *esp_eth_base_config = malloc(sizeof(esp_netif_inherent_config_t));
if (NULL == esp_eth_base_config) {
return ESP_FAIL;
}
*esp_eth_base_config = (esp_netif_inherent_config_t)ESP_NETIF_INHERENT_DEFAULT_ETH();
esp_eth_base_config->if_key = if_key;
eth_cfg_o->base = esp_eth_base_config;
eth_cfg_o->driver = NULL;
eth_cfg_o->stack = ESP_NETIF_NETSTACK_DEFAULT_ETH;
return ESP_OK;
}
static void free_config(esp_netif_config_t *eth_cfg)
{
if ((NULL != eth_cfg) && (NULL != eth_cfg->base)) {
free((void *)(eth_cfg->base->if_key));
free((void *)(eth_cfg->base));
}
}
static esp_err_t ifcfg_netif_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
int eth_handle_id = atoi(argv[self->start_index + 2]);
if (!strcmp(argv[self->start_index + 1], "create")) {
/* Validate ethernet handle */
if ((eth_handle_id + 1 > eth_port_cnt_g) || (eth_handle_id < 0)) {
ESP_LOGE(TAG, "Invalid ethernet handle: %s", argv[self->start_index + 2]);
return ESP_FAIL;
}
esp_netif_config_t eth_cfg;
ESP_ERROR_CHECK(get_netif_config(eth_handle_id, &eth_cfg));
for (int i = 0; i < MAX_ETH_NETIF_COUNT; i++) {
if (iface_list[i].state == ETH_INITIALIZED) {
esp_netif = esp_netif_new(&eth_cfg);
if (esp_netif == NULL) {
ESP_LOGE(TAG, "Interface with key %s already exists", argv[self->start_index + 2]);
return ESP_FAIL;
}
iface_list[i].eth_glue = esp_eth_new_netif_glue(iface_list[i].eth_handle);
if (iface_list[i].eth_glue == NULL) {
ESP_LOGE(TAG, "%s: eth_glue is NULL", __func__);
esp_netif_destroy(esp_netif);
return ESP_FAIL;
}
iface_list[i].esp_netif = esp_netif;
ESP_ERROR_CHECK(esp_netif_attach(iface_list[i].esp_netif, iface_list[i].eth_glue));
// start Ethernet driver state machine
ESP_ERROR_CHECK(esp_eth_start(iface_list[i].eth_handle));
free_config(&eth_cfg);
iface_list[i].state = NETIF_CREATED;
netif_count++;
break;
}
}
return ESP_OK;
} else if (!strcmp(argv[self->start_index + 1], "destroy")) {
esp_netif = get_esp_netif_from_ifname(argv[self->start_index + 2]);
if (NULL == esp_netif) {
ESP_LOGE(TAG, "interface %s not available", argv[1]);
return ESP_FAIL;
}
for (int i = 0; i < MAX_ETH_NETIF_COUNT; i++) {
if (esp_netif == iface_list[i].esp_netif) {
if (iface_list[i].state == NETIF_CREATED) {
esp_eth_stop(iface_list[i].eth_handle);
esp_eth_del_netif_glue(iface_list[i].eth_glue);
esp_netif_destroy(iface_list[i].esp_netif);
iface_list[i].state = NETIF_DESTROYED;
netif_count--;
return ESP_OK;
} else {
ESP_LOGE(TAG, "Netif is not in created state");
return ESP_FAIL;
}
break;
}
}
ESP_LOGE(TAG, "Something is very wrong. Unauthorized Interface.");
return ESP_FAIL;
}
return ESP_FAIL;
}
static void print_eth_info(eth_dev_info_t eth_info, int id)
{
if (eth_info.type == ETH_DEV_TYPE_INTERNAL_ETH) {
printf("Internal(%s): pins: %2d,%2d, Id: %d\n", eth_info.name, eth_info.pin.eth_internal_mdc, eth_info.pin.eth_internal_mdio, id);
} else if (eth_info.type == ETH_DEV_TYPE_SPI) {
printf(" SPI(%s): pins: %2d,%2d, Id: %d\n", eth_info.name, eth_info.pin.eth_spi_cs, eth_info.pin.eth_spi_int, id);
} else {
printf("ethernet handle id(ETH_DEV_TYPE_UNKNOWN): %d\n", id);
}
}
static esp_err_t ifcfg_eth_op(netif_op_t *self, int argc, char *argv[], esp_netif_t *esp_netif)
{
static esp_eth_handle_t *eth_handle_g = NULL;
eth_dev_info_t eth_info;
if (!strcmp(argv[self->start_index + 1], "init")) {
/* Check if ethernet is initialized */
if (eth_init_flag == false) {
// Initialize Ethernet driver
if (ethernet_init_all(&eth_handle_g, &eth_port_cnt_g) != ESP_OK) {
ESP_LOGE(TAG, "Unable to initialize ethernet");
return ESP_FAIL;
}
eth_init_flag = true;
for (int i = 0; i < eth_port_cnt_g; i++) {
for (int j = 0; j < MAX_ETH_NETIF_COUNT; j++) {
if (iface_list[j].state == UNINITIALIZED) {
iface_list[j].eth_handle = eth_handle_g[i];
iface_list[j].state = ETH_INITIALIZED;
break;
}
}
}
if (eth_port_cnt_g > MAX_ETH_NETIF_COUNT) {
ESP_LOGW(TAG, "Not all ethernet ports can be assigned a network interface.\nPlease reconfigure MAX_ETH_NETIF_COUNT to a higher value.");
}
} else {
ESP_LOGW(TAG, "Ethernet already initialized");
}
/* Display available ethernet handles */
for (int i = 0; i < eth_port_cnt_g; i++) {
eth_info = ethernet_init_get_dev_info(iface_list[i].eth_handle);
print_eth_info(eth_info, i);
}
} else if (!strcmp(argv[self->start_index + 1], "show")) {
/* Check if ethernet is initialized */
if (eth_init_flag == false) {
// Initialize Ethernet driver
ESP_LOGE(TAG, "Ethernet is not initialized.");
return ESP_OK;
}
/* Display available ethernet handles */
for (int i = 0; i < eth_port_cnt_g; i++) {
eth_info = ethernet_init_get_dev_info(iface_list[i].eth_handle);
print_eth_info(eth_info, i);
}
} else if (!strcmp(argv[self->start_index + 1], "deinit")) {
/* Check if ethernet is initialized */
if (eth_init_flag == false) {
// Initialize Ethernet driver
ESP_LOGE(TAG, "Ethernet is not initialized.");
return ESP_OK;
}
/* Stop and Deinit ethernet here */
ethernet_deinit_all(eth_handle_g);
eth_port_cnt_g = 0;
eth_init_flag = false;
} else {
return ESP_FAIL;
}
return ESP_OK;
}
/* handle 'ifconfig' command */
static int do_cmd_ifconfig(int argc, char **argv)
{
esp_netif_t *esp_netif = NULL;
int cmd_count = sizeof(cmd_list) / sizeof(cmd_list[0]);
netif_op_t cmd;
for (int i = 0; i < cmd_count; i++) {
cmd = cmd_list[i];
if (argc < cmd.start_index + 1) {
continue;
}
if (!strcmp(cmd.name, argv[cmd.start_index])) {
/* Get interface for eligible commands */
if (cmd.netif_flag == true) {
esp_netif = get_esp_netif_from_ifname(argv[1]);
if (NULL == esp_netif) {
ESP_LOGE(TAG, "interface %s not available", argv[1]);
return 0;
}
}
if (cmd.arg_cnt == argc) {
if (cmd.operation != NULL) {
if (cmd.operation(&cmd, argc, argv, esp_netif) != ESP_OK) {
ESP_LOGE(TAG, "Usage:\n%s", cmd.help);
return 0;
}
}
return 0;
}
}
}
ESP_LOGE(TAG, "Command not available");
return 1;
}
/**
* @brief Registers the ifconfig command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_ifconfig_register(void)
{
esp_err_t ret;
esp_console_cmd_t command = {
.command = "ifconfig",
.help = "Command for network interface configuration and monitoring\nFor more info run 'ifconfig help'",
.func = &do_cmd_ifconfig
};
ret = esp_console_cmd_register(&command);
if (ret) {
ESP_LOGE(TAG, "Unable to register ifconfig");
}
return ret;
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "console_simple_init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers the ifconfig command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_ifconfig_register(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ifconfig-basic)

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "ifconfig-basic.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,6 @@
dependencies:
idf:
version: ">=5.0"
console_cmd_ifconfig:
version: "*"
override_path: '../../../'

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "esp_netif.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "console_ifconfig.h"
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_cmd_ifconfig_register());
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
}

View File

@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.esp32
def test_examples_ifconfig_command(dut):
dut.expect('esp>', timeout=30)
dut.write('ifconfig eth init')
dut.expect(r'Internal\(IP101\): pins:', timeout=30)
dut.write('ifconfig netif create 0')
dut.expect(r'ethernet_init: Ethernet\(IP101\[23,18\]\) Link Up', timeout=30)
dut.write('ifconfig')
dut.expect('console_ifconfig: Interface Name: en1', timeout=5)
dut.write('ifconfig netif destroy en1')
dut.expect('esp>', timeout=5)
dut.write('ifconfig eth deinit')
dut.expect('esp>', timeout=5)
pass

View File

@ -0,0 +1,12 @@
version: 1.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ifconfig
description: The component offers a console that enables runtime network interface configuration and monitoring.
dependencies:
idf:
version: '>=5.0'
espressif/console_simple_init:
version: '>=1.1.0'
override_path: '../console_simple_init'
public: true
espressif/ethernet_init:
version: '>=0.0.7'

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(console): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py console_cmd_ping
tag_format: console_cmd_ping-v$version
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.0.0)
### Features
- Added ping command to console component ([7babdeb9](https://github.com/espressif/esp-protocols/commit/7babdeb9))

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "console_ping.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_netif console
WHOLE_ARCHIVE)

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,55 @@
# Console command ping
The component provides a console where the 'ping' command can be executed.
## API
### Steps to enable console in an example code:
1. Add this component to your project using ```idf.py add-dependency``` command.
2. In the main file of the example, add the following line:
```c
#include "console_ping.h"
```
3. Ensure esp-netif and NVS flash is initialized and default event loop is created in your app_main():
```c
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
```
4. In your app_main() function, add the following line as the last line:
```c
ESP_ERROR_CHECK(console_cmd_init()); // Initialize console
// Register all plugin command added to your project
ESP_ERROR_CHECK(console_cmd_all_register());
// To register only ifconfig command skip calling console_cmd_all_register()
ESP_ERROR_CHECK(console_cmd_ping_register());
ESP_ERROR_CHECK(console_cmd_start()); // Start console
```
### Adding a plugin command or component:
To add a plugin command or any component from IDF component manager into your project, simply include an entry within the `idf_component.yml` file.
For more details refer [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html)
## Suported command:
### ping:
```
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
send ICMP ECHO_REQUEST to network hosts
-W, --timeout=<t> Time to wait for a response, in seconds
-i, --interval=<t> Wait interval seconds between sending each packet
-s, --size=<n> Specify the number of data bytes to be sent
-c, --count=<n> Stop after sending count packets
-Q, --tos=<n> Set Type of Service related bits in IP datagrams
-T, --ttl=<n> Set Time to Live related bits in IP datagrams
<host> Host address
```

View File

@ -0,0 +1,215 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_log.h"
#include "argtable3/argtable3.h"
#include "ping/ping_sock.h"
#include "console_ping.h"
static const char *TAG = "console_ping";
SemaphoreHandle_t sync_semaphore;
/**
* Static registration of this plugin is achieved by defining the plugin description
* structure and placing it into .console_cmd_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = {
.name = "console_cmd_ping",
.plugin_regd_fn = &console_cmd_ping_register
};
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
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));
printf("%" PRIu32 " bytes from %s icmp_seq=%" PRIu16 " ttl=%" PRIu16 " time=%" PRIu32 " ms\n",
recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
uint32_t loss;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
if (transmitted > 0) {
loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
} else {
loss = 0;
}
if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
printf("%" 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
ESP_ERROR_CHECK(esp_ping_delete_session(hdl));
/* Give the semaphore as ping task is done */
xSemaphoreGive(sync_semaphore);
}
static struct {
struct arg_dbl *timeout;
struct arg_dbl *interval;
struct arg_int *data_size;
struct arg_int *count;
struct arg_int *tos;
struct arg_int *ttl;
struct arg_str *host;
struct arg_end *end;
} ping_args;
static int do_ping_cmd(int argc, char **argv)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
if (ping_args.timeout->count > 0) {
config.timeout_ms = (uint32_t)(ping_args.timeout->dval[0] * 1000);
}
if (ping_args.interval->count > 0) {
config.interval_ms = (uint32_t)(ping_args.interval->dval[0] * 1000);
}
if (ping_args.data_size->count > 0) {
config.data_size = (uint32_t)(ping_args.data_size->ival[0]);
}
if (ping_args.count->count > 0) {
config.count = (uint32_t)(ping_args.count->ival[0]);
}
if (ping_args.tos->count > 0) {
config.tos = (uint32_t)(ping_args.tos->ival[0]);
}
if (ping_args.ttl->count > 0) {
config.ttl = (uint32_t)(ping_args.ttl->ival[0]);
}
// parse IP address
struct sockaddr_in6 sock_addr6;
ip_addr_t target_addr = {0};
if (inet_pton(AF_INET6, ping_args.host->sval[0], &sock_addr6.sin6_addr) == 1) {
/* convert ip6 string to ip6 address */
ipaddr_aton(ping_args.host->sval[0], &target_addr);
} else {
struct addrinfo hint = {0};
struct addrinfo *res = NULL;
/* convert ip4 string or hostname to ip4 or ip6 address */
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
} else {
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
}
freeaddrinfo(res);
}
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.cb_args = NULL,
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end
};
esp_ping_handle_t ping;
ESP_ERROR_CHECK(esp_ping_new_session(&config, &cbs, &ping));
ESP_ERROR_CHECK(esp_ping_start(ping));
/* Wait till the ping task is done */
if (xSemaphoreTake(sync_semaphore, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Error in xSemaphoreTake\n");
}
return 0;
}
/**
* @brief Registers the ping command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_ping_register(void)
{
esp_err_t ret;
sync_semaphore = xSemaphoreCreateBinary();
ping_args.timeout = arg_dbl0("W", "timeout", "<t>", "Time to wait for a response, in seconds");
ping_args.interval = arg_dbl0("i", "interval", "<t>", "Wait interval seconds between sending each packet");
ping_args.data_size = arg_int0("s", "size", "<n>", "Specify the number of data bytes to be sent");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
ping_args.ttl = arg_int0("T", "ttl", "<n>", "Set Time to Live related bits in IP datagrams");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {
.command = "ping",
.help = "send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &do_ping_cmd,
.argtable = &ping_args
};
ret = esp_console_cmd_register(&ping_cmd);
if (ret) {
ESP_LOGE(TAG, "Unable to register ping");
}
return ret;
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "console_simple_init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers the ping command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_ping_register(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ping-basic)

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "ping-basic.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,6 @@
dependencies:
idf:
version: ">=5.0"
console_cmd_ping:
version: "*"
override_path: '../../../'

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "esp_netif.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "console_ping.h"
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
// Register ping command
ESP_ERROR_CHECK(console_cmd_ping_register());
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
}

View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.esp32
def test_examples_ping_command(dut):
dut.expect('esp>', timeout=30)
dut.write('ping 127.0.0.1')
dut.expect('5 packets transmitted, 5 received, 0% packet loss, time 0ms', timeout=30)
pass

View File

@ -0,0 +1,10 @@
version: 1.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ping
description: The component provides a console where the 'ping' command can be executed.
dependencies:
idf:
version: '>=5.0'
espressif/console_simple_init:
version: '>=1.1.0'
override_path: '../console_simple_init'
public: true

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(console): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py console_cmd_wifi
tag_format: console_cmd_wifi-v$version
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_wifi-v1.0.0)
### Features
- Console for runtime wifi configuration ([194d1179](https://github.com/espressif/esp-protocols/commit/194d1179))

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "console_wifi.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_netif console esp_wifi
WHOLE_ARCHIVE)

View File

@ -0,0 +1,44 @@
# Console command wifi
The component offers a console with a command that enables runtime wifi configuration for any example project.
## API
### Steps to enable console in an example code:
1. Add this component to your project using ```idf.py add-dependency``` command.
2. In the main file of the example, add the following line:
```c
#include "console_wifi.h"
```
3. Ensure esp-netif and NVS flash is initialized and default event loop is created in your app_main():
```c
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
```
4. In your app_main() function, add the following line as the last line:
```c
ESP_ERROR_CHECK(console_cmd_init()); // Initialize console
// Register all plugin command added to your project
ESP_ERROR_CHECK(console_cmd_all_register());
// To register only wifi command skip calling console_cmd_all_register()
ESP_ERROR_CHECK(console_cmd_wifi_register());
ESP_ERROR_CHECK(console_cmd_start()); // Start console
```
## Suported command:
### wifi:
* ```wifi help```: Prints the help text for all wifi commands
* ```wifi show network```: Scans and displays upto 10 available wifi networks.
* ```wifi show sta```: Shows the details of wifi station.
* ```wifi sta join <network ssid> <password>```: Station joins the given wifi network.
* ```wifi sta join <network ssid>```: Station joins the given unsecured wifi network.
* ```wifi sta leave```: Station leaves the wifi network.

View File

@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "lwip/ip6.h"
#include "lwip/opt.h"
#include "esp_wifi.h"
#include "console_wifi.h"
#define DEFAULT_SCAN_LIST_SIZE 10
/**
* Static registration of this plugin is achieved by defining the plugin description
* structure and placing it into .console_cmd_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = {
.name = "console_cmd_wifi",
.plugin_regd_fn = &console_cmd_wifi_register
};
typedef struct wifi_op_t {
char *name;
esp_err_t (*operation)(struct wifi_op_t *self, int argc, char *argv[]);
int arg_cnt;
int start_index;
char *help;
} wifi_op_t;
static esp_err_t wifi_help_op(wifi_op_t *self, int argc, char *argv[]);
static esp_err_t wifi_show_op(wifi_op_t *self, int argc, char *argv[]);
static esp_err_t wifi_sta_join_op(wifi_op_t *self, int argc, char *argv[]);
static esp_err_t wifi_sta_leave_op(wifi_op_t *self, int argc, char *argv[]);
static const char *TAG = "console_wifi";
#define JOIN_TIMEOUT_MS (10000)
static EventGroupHandle_t wifi_event_group;
static const int STA_STARTED_BIT = BIT0;
static const int CONNECTED_BIT = BIT1;
static wifi_op_t cmd_list[] = {
{.name = "help", .operation = wifi_help_op, .arg_cnt = 2, .start_index = 1, .help = "wifi help: Prints the help text for all wifi commands"},
{.name = "show", .operation = wifi_show_op, .arg_cnt = 3, .start_index = 1, .help = "wifi show network/sta: Scans and displays all available wifi APs./ Shows the details of wifi station."},
{.name = "join", .operation = wifi_sta_join_op, .arg_cnt = 4, .start_index = 2, .help = "wifi sta join <network ssid> <password>: Station joins the given wifi network."},
{.name = "join", .operation = wifi_sta_join_op, .arg_cnt = 5, .start_index = 2, .help = "wifi sta join <network ssid>: Station joins the given unsecured wifi network."},
{.name = "leave", .operation = wifi_sta_leave_op, .arg_cnt = 3, .start_index = 2, .help = "wifi sta leave: Station leaves the wifi network."},
};
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
xEventGroupSetBits(wifi_event_group, STA_STARTED_BIT);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
}
}
static esp_err_t wifi_help_op(wifi_op_t *self, int argc, char *argv[])
{
int cmd_count = sizeof(cmd_list) / sizeof(cmd_list[0]);
for (int i = 0; i < cmd_count; i++) {
if ((cmd_list[i].help != NULL) && (strlen(cmd_list[i].help) != 0)) {
printf(" %s\n", cmd_list[i].help);
}
}
return ESP_OK;
}
uint8_t wifi_connection_status = 0;
void wifi_init(void)
{
static bool init_flag = false;
if (init_flag) {
return;
}
wifi_event_group = xEventGroupCreate();
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_START, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
int bits = xEventGroupWaitBits(wifi_event_group, STA_STARTED_BIT,
pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS);
if ((bits & STA_STARTED_BIT) == 0) {
ESP_LOGE(TAG, "Error: Wifi Connection timed out");
return;
}
init_flag = true;
}
/* Initialize Wi-Fi as sta and set scan method */
static void wifi_scan(void)
{
uint16_t number = DEFAULT_SCAN_LIST_SIZE;
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
uint16_t ap_count = 0;
memset(ap_info, 0, sizeof(ap_info));
wifi_init();
esp_wifi_scan_start(NULL, true);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_LOGI(TAG, "Showing Wifi networks");
ESP_LOGI(TAG, "*********************");
for (int i = 0; i < number; i++) {
ESP_LOGI(TAG, "RSSI: %d\tChannel: %d\tSSID: %s", ap_info[i].rssi, ap_info[i].primary, ap_info[i].ssid);
}
ESP_LOGI(TAG, "Total APs scanned = %u, actual AP number ap_info holds = %u", ap_count, number);
}
static esp_err_t wifi_show_op(wifi_op_t *self, int argc, char *argv[])
{
if (!strcmp("network", argv[self->start_index + 1])) {
wifi_scan();
return ESP_OK;
}
if (!strcmp("sta", argv[self->start_index + 1])) {
{
wifi_config_t wifi_config = { 0 };
wifi_init();
ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &wifi_config));
ESP_LOGI(TAG, "Showing Joind AP details:");
ESP_LOGI(TAG, "*************************");
ESP_LOGI(TAG, "SSID: %s", wifi_config.sta.ssid);
ESP_LOGI(TAG, "Password: %s", wifi_config.sta.password);
ESP_LOGI(TAG, "Channel: %d", wifi_config.sta.channel);
ESP_LOGI(TAG, "bssid: %02x:%02x:%02x:%02x:%02x:%02x", wifi_config.sta.bssid[0],
wifi_config.sta.bssid[1], wifi_config.sta.bssid[2], wifi_config.sta.bssid[3],
wifi_config.sta.bssid[4], wifi_config.sta.bssid[5]);
}
return ESP_OK;
}
return ESP_OK;
}
static esp_err_t wifi_sta_join_op(wifi_op_t *self, int argc, char *argv[])
{
wifi_config_t wifi_config = { 0 };
if (strcmp("sta", argv[self->start_index - 1])) {
ESP_LOGE(TAG, "Error: Invalid command\n");
ESP_LOGE(TAG, "%s\n", self->help);
return ESP_FAIL;
}
strlcpy((char *) wifi_config.sta.ssid, argv[self->start_index + 1], sizeof(wifi_config.sta.ssid));
if (self->arg_cnt == 5) {
strlcpy((char *) wifi_config.sta.password, argv[self->start_index + 2], sizeof(wifi_config.sta.password));
}
wifi_init();
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
esp_wifi_connect();
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS);
if ((bits & CONNECTED_BIT) == 0) {
ESP_LOGE(TAG, "Error: Wifi Connection timed out");
return ESP_OK;
}
return ESP_OK;
}
static esp_err_t wifi_sta_leave_op(wifi_op_t *self, int argc, char *argv[])
{
wifi_config_t wifi_config = { 0 };
if (strcmp("sta", argv[self->start_index - 1])) {
ESP_LOGE(TAG, "Error: Invalid command\n");
ESP_LOGE(TAG, "%s\n", self->help);
return ESP_FAIL;
}
esp_wifi_disconnect();
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
return ESP_OK;
}
/* handle 'wifi' command */
static esp_err_t do_cmd_wifi(int argc, char **argv)
{
int cmd_count = sizeof(cmd_list) / sizeof(cmd_list[0]);
wifi_op_t cmd;
for (int i = 0; i < cmd_count; i++) {
cmd = cmd_list[i];
if (argc < cmd.start_index + 1) {
continue;
}
if (!strcmp(cmd.name, argv[cmd.start_index])) {
/* Get interface for eligible commands */
if (cmd.arg_cnt == argc) {
if (cmd.operation != NULL) {
if (cmd.operation(&cmd, argc, argv) != ESP_OK) {
ESP_LOGE(TAG, "Usage:\n%s", cmd.help);
return 0;
}
}
return ESP_OK;
}
}
}
ESP_LOGE(TAG, "Command not available");
return ESP_OK;
}
/**
* @brief Registers the wifi command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_wifi_register(void)
{
esp_err_t ret;
esp_console_cmd_t command = {
.command = "wifi",
.help = "Command for wifi configuration and monitoring\n For more info run 'wifi help'",
.func = &do_cmd_wifi
};
ret = esp_console_cmd_register(&command);
if (ret) {
ESP_LOGE(TAG, "Unable to register wifi");
}
return ret;
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "console_simple_init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers the wifi command.
*
* @return
* - esp_err_t
*/
esp_err_t console_cmd_wifi_register(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(wifi-basic)

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "wifi-basic.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,6 @@
dependencies:
idf:
version: ">=5.0"
console_cmd_wifi:
version: "*"
override_path: '../../../'

View File

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "esp_netif.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "console_wifi.h"
void app_main(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t ret = nvs_flash_init(); //Initialize NVS
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_cmd_wifi_register());
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
}

View File

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# -*- coding: utf-8 -*-
import pytest
@pytest.mark.esp32
def test_examples_ifconfig_command(dut):
dut.expect('esp>', timeout=30)
dut.write('wifi show network')
dut.expect(r'Showing Wifi networks', timeout=30)
dut.expect('esp>', timeout=30)

View File

@ -0,0 +1,10 @@
version: 1.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_wifi
description: The component offers a console that enables runtime wifi configuration and monitoring.
dependencies:
idf:
version: '>=5.0'
espressif/console_simple_init:
version: '>=1.1.0'
override_path: '../console_simple_init'
public: true

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.4
version: 1.0.5
version_files:
- idf_component.yml

View File

@ -1,5 +1,29 @@
# Changelog
## [1.0.5](https://github.com/espressif/esp-protocols/commits/modem-v1.0.5)
### Major changes
- Added support for implementing user defined modules in standard C-API ([Support for custom modules with C-API](https://github.com/espressif/esp-protocols/commit/0254d50))
### Bug Fixes
- Added test injecting unexpected replies ([b220d1e](https://github.com/espressif/esp-protocols/commit/b220d1e), [#426](https://github.com/espressif/esp-protocols/issues/426))
- Fixed inconsistent state on data after OK ([bf99f28](https://github.com/espressif/esp-protocols/commit/bf99f28), [#426](https://github.com/espressif/esp-protocols/issues/426))
- TLS example: Added restore session support in mbedtls-wrap ([79d38e5](https://github.com/espressif/esp-protocols/commit/79d38e5))
- Fixed examples to show netif on ppp-changed event ([a70b197](https://github.com/espressif/esp-protocols/commit/a70b197))
- remove unused GNU Make based buildsystem files ([d6b6f63](https://github.com/espressif/esp-protocols/commit/d6b6f63))
- specify override_path in example manifest files ([5b78da4](https://github.com/espressif/esp-protocols/commit/5b78da4))
- Added test-cases that exercise mode transitions ([aff571d](https://github.com/espressif/esp-protocols/commit/aff571d))
- Fixed mode transitions between any state and UNDEF ([93cb2ca](https://github.com/espressif/esp-protocols/commit/93cb2ca), [#320](https://github.com/espressif/esp-protocols/issues/320))
- Fixed API docs within doxygen comments ([020b407](https://github.com/espressif/esp-protocols/commit/020b407))
- Support for custom modules with C-API ([0254d50](https://github.com/espressif/esp-protocols/commit/0254d50))
- Fix CRLF issue with esp_modem_c_api.cpp ([2661b4d](https://github.com/espressif/esp-protocols/commit/2661b4d))
### Updated
- ci(common): Created reusable action for host and coverage tests ([9ad04de](https://github.com/espressif/esp-protocols/commit/9ad04de))
## [1.0.4](https://github.com/espressif/esp-protocols/commits/modem-v1.0.4)
### Bug Fixes

View File

@ -42,6 +42,9 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_EXTENSIONS ON
)
if(CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE)
idf_component_optional_requires(PUBLIC main)
endif()
if(${target} STREQUAL "linux")
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64

View File

@ -45,4 +45,22 @@ menu "esp-modem"
to make the protocol more robust on noisy environments or when underlying
transport gets corrupted often (for example by Rx buffer overflows)
config ESP_MODEM_ADD_CUSTOM_MODULE
bool "Add support for custom module in C-API"
default n
help
If enabled, we adapt the C-API to create a DCE from a user defined class
config ESP_MODEM_CUSTOM_MODULE_HEADER
string "Header file name which defines custom DCE creation"
depends on ESP_MODEM_ADD_CUSTOM_MODULE
default "custom_module.hpp"
help
Name of the header file in the main component which implements esp_modem_create_custom_dce()
called from C-API for creating esp_modem_dce object.
This header provides definition of the custom module with some additional and/or updated commands
and API. It also defines creation of DCE based on this custom module, typically calling:
dce_factory::Factory::create_unique_dce_from<CustomModule, DCE*>(dce_config, std::move(dte), netif)
Please refer to the pppos_client example for more details.
endmenu

View File

@ -235,7 +235,9 @@ extern "C" void app_main(void)
if (c->get_count_of(&SetModeArgs::mode)) {
auto mode = c->get_string_of(&SetModeArgs::mode);
modem_mode dev_mode;
if (mode == "CMUX1") {
if (mode == "UNDEF") {
dev_mode = esp_modem::modem_mode::UNDEF;
} else if (mode == "CMUX1") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_MODE;
} else if (mode == "CMUX2") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_EXIT;

View File

@ -6,6 +6,7 @@
#pragma once
#include <utility>
#include <memory>
#include <span>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
@ -22,6 +23,7 @@ public:
Tls();
virtual ~Tls();
bool init(is_server server, do_verify verify);
bool deinit();
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
@ -41,6 +43,11 @@ protected:
mbedtls_entropy_context entropy_{};
virtual void delay() {}
bool set_session();
bool get_session();
void reset_session();
bool is_session_loaded();
private:
static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
@ -48,5 +55,21 @@ private:
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
struct unique_session {
unique_session()
{
::mbedtls_ssl_session_init(&s);
}
~unique_session()
{
::mbedtls_ssl_session_free(&s);
}
mbedtls_ssl_session *ptr()
{
return &s;
}
mbedtls_ssl_session s;
};
std::unique_ptr<unique_session> session_;
};

View File

@ -35,6 +35,16 @@ bool Tls::init(is_server server, do_verify verify)
return true;
}
bool Tls::deinit()
{
::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_);
return true;
}
void Tls::print_error(const char *function, int error_code)
{
static char error_buf[100];
@ -132,3 +142,39 @@ Tls::~Tls()
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
}
bool Tls::get_session()
{
if (session_ == nullptr) {
session_ = std::make_unique<unique_session>();
}
int ret = ::mbedtls_ssl_get_session(&ssl_, session_->ptr());
if (ret != 0) {
print_error("mbedtls_ssl_get_session() failed", ret);
return false;
}
return true;
}
bool Tls::set_session()
{
if (session_ == nullptr) {
printf("session hasn't been initialized");
return false;
}
int ret = mbedtls_ssl_set_session(&ssl_, session_->ptr());
if (ret != 0) {
print_error("mbedtls_ssl_set_session() failed", ret);
return false;
}
return true;
}
void Tls::reset_session()
{
session_.reset(nullptr);
}
bool Tls::is_session_loaded()
{
return session_ != nullptr;
}

View File

@ -48,6 +48,13 @@ menu "Example Configuration"
bool "A7670"
help
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
config EXAMPLE_MODEM_DEVICE_CUSTOM
select ESP_MODEM_ADD_CUSTOM_MODULE
bool "Custom device"
help
This demonstrates use of a custom device in C-API.
endchoice
config EXAMPLE_MODEM_PPP_APN

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
/**
* @brief Definition of a custom module based on some already defined module
* Here we use GenericModule, but you can use any kind of device
* that is closer to your CustomModule.
*/
class SIM7600_WITH_TIME: public GenericModule {
/**
* @brief Need to reuse the constructors of our ancestor
*/
using GenericModule::GenericModule;
public:
/**
* @brief New command that is not defined in the GenericModule
*/
command_result get_time(std::string &time)
{
return esp_modem::dce_commands::generic_get_string(dte.get(), "AT+CCLK?\r", time);
}
/**
* @brief This command is already defined in the GenericModule
*
* Here we just modify get_signal_quality() to return zeroes
* for demonstration purpose only, since it's called within this example
*/
command_result get_signal_quality(int &rssi, int &ber) override
{
rssi = ber = 0;
return esp_modem::command_result::OK;
}
};
/**
* @brief esp_modem_create_custom_dce() needs to be defined, as it is called in C-API wrapper when creating esp_modem_dce
*
* This uses public factory function for creating a common DCE with our CustomModule. Creating raw DCE pointer is only needed
* for the C-API wrapper; C++API users would create DCE (any kind of smart pointer) directly with
* Factory::create_unique_dce_from<CustomModule>(dce_config, std::move(dte), netif);
*/
DCE *esp_modem_create_custom_dce(const esp_modem_dce_config_t *dce_config, std::shared_ptr<DTE> dte, esp_netif_t *netif)
{
return dce_factory::Factory::create_unique_dce_from<SIM7600_WITH_TIME, DCE *>(dce_config, std::move(dte), netif);
}
/**
* @brief This API is only needed for extending standard C-API, since we added get_time() method to our CustomModule
*
* @note This header is included from esp_modem_c_api.cpp, so it could use ESP_MODEM_C_API_STR_MAX macro
* indicating maximum C-API string size
*
* @note In order to access the newly added API get_time(), we have to static_cast<> the GenericModule from DCE
* to our CustomModule.
* Alternatively we could use the modem Factory to build our specific DCE_T<CustomModule>, but in that case
* we couldn't use our C-API wrappers which expect DCE type, DCE_T<GenericModule> with lib commands (this alternative
* is cleaner, but more suitable for C++ users)
*/
extern "C" esp_err_t esp_modem_get_time(esp_modem_dce_t *dce_wrap, char *p_time)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string time{ESP_MODEM_C_API_STR_MAX};
auto ret = command_response_to_esp_err(static_cast<SIM7600_WITH_TIME *>(dce_wrap->dce->get_module())->get_time(time));
if (ret == ESP_OK && !time.empty()) {
strlcpy(p_time, time.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}

View File

@ -37,6 +37,10 @@ static const int CONNECT_BIT = BIT0;
static const int GOT_DATA_BIT = BIT2;
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
esp_err_t esp_modem_get_time(esp_modem_dce_t *dce_wrap, char *p_time);
#endif
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
#include "esp_modem_usb_c_api.h"
#include "esp_modem_usb_config.h"
@ -105,8 +109,8 @@ static void on_ppp_changed(void *arg, esp_event_base_t event_base,
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;
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
esp_netif_t **p_netif = event_data;
ESP_LOGI(TAG, "User interrupted event from netif:%p", *p_netif);
}
}
@ -192,6 +196,9 @@ void app_main(void)
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_SIM7600, &dte_config, &dce_config, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM == 1
ESP_LOGI(TAG, "Initializing esp_modem with custom module...");
esp_modem_dce_t *dce = esp_modem_new_dev(ESP_MODEM_DCE_CUSTOM, &dte_config, &dce_config, esp_netif);
#else
ESP_LOGI(TAG, "Initializing esp_modem for a generic module...");
esp_modem_dce_t *dce = esp_modem_new(&dte_config, &dce_config, esp_netif);
@ -258,6 +265,18 @@ void app_main(void)
}
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
{
char time[64];
err = esp_modem_get_time(dce, time);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_get_time failed with %d %s", err, esp_err_to_name(err));
return;
}
ESP_LOGI(TAG, "esp_modem_get_time: %s", time);
}
#endif
#if CONFIG_EXAMPLE_SEND_MSG
if (esp_modem_sms_txt_mode(dce, true) != ESP_OK || esp_modem_sms_character_set(dce) != ESP_OK) {
ESP_LOGE(TAG, "Setting text mode or GSM character set failed");

View File

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

View File

@ -55,6 +55,7 @@ command_result power_down_sim70xx(CommandableIf *t);
command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mode, const int *bands, int size);
command_result power_down_sim8xx(CommandableIf *t);
command_result set_data_mode_sim8xx(CommandableIf *t);
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms);
/**
* @}

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
*/
@ -12,6 +12,7 @@
* @brief DCE modem factory
*/
#define esp_modem_create_dce_from dce_factory::Factory::create_unique_dce_from
namespace esp_modem::dce_factory {
@ -236,12 +237,12 @@ 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)
template <typename T_Module, typename Ptr = std::unique_ptr<DCE>>
static Ptr 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);
return build_generic_DCE<T_Module, DCE, Ptr>(config, std::move(dte), netif);
}
private:

View File

@ -170,6 +170,8 @@ public:
*/
class BG96: public GenericModule {
using GenericModule::GenericModule;
public:
command_result set_pdp_context(PdpContext &pdp) override;
};
/**

View File

@ -76,6 +76,7 @@ public:
* @param command Command to be sent
* @param got_line callback if a line received
* @param time_ms timeout in milliseconds
* @param separator Character treated as a line separator, typically '\n'
* @return OK, FAIL or TIMEOUT
*/
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;

View File

@ -53,6 +53,7 @@ typedef enum esp_modem_dce_device {
ESP_MODEM_DCE_SIM7000,
ESP_MODEM_DCE_BG96,
ESP_MODEM_DCE_SIM800,
ESP_MODEM_DCE_CUSTOM
} esp_modem_dce_device_t;
/**

View File

@ -25,8 +25,8 @@ ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result, 0) \
\
/**
* @brief Reads the operator name
* @param[out] operator name
* @param[out] access technology
* @param[out] name operator name
* @param[out] act access technology
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(get_operator_name, command_result, 2, STRING_OUT(p1, name), INT_OUT(p2, act)) \
@ -46,7 +46,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
\
/**
* @brief Execute the supplied AT command
* @param[in] at AT command
* @param[in] cmd AT command
* @param[out] out Command output string
* @param[in] timeout AT command timeout in milliseconds
* @return OK, FAIL or TIMEOUT
@ -252,6 +252,8 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_preferred_mode, command_result, 1, INT_IN(p1,
/**
* @brief Set network bands for CAT-M or NB-IoT
* @param[in] mode CAT-M or NB-IoT
* @param[in] bands bitmap in hex representing bands
* @param[in] size size of teh bands bitmap
* @return OK, FAIL or TIMEOUT
*/ \
ESP_MODEM_DECLARE_DCE_COMMAND(set_network_bands, command_result, 3, STRING_IN(p1, mode), INTEGER_LIST_IN(p2, bands), INT_IN(p3, size)) \

View File

@ -1,425 +1,436 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cassert>
#include "cxx_include/esp_modem_dte.hpp"
#include "uart_terminal.hpp"
#include "esp_log.h"
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_factory.hpp"
#include "esp_modem_c_api_types.h"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
#include "esp_private/c_api_wrapper.hpp"
#include "cstring"
#ifndef ESP_MODEM_C_API_STR_MAX
#define ESP_MODEM_C_API_STR_MAX 64
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dest, const char *src, size_t len);
#endif
//
// C API definitions
using namespace esp_modem;
extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
{
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
if (dce_wrap == nullptr) {
return nullptr;
}
auto dte = create_uart_dte(dte_config);
if (dte == nullptr) {
delete dce_wrap;
return nullptr;
}
dce_wrap->dte = dte;
dce_factory::Factory f(convert_modem_enum(module));
dce_wrap->dce = f.build(dce_config, std::move(dte), netif);
if (dce_wrap->dce == nullptr) {
delete dce_wrap;
return nullptr;
}
dce_wrap->modem_type = convert_modem_enum(module);
dce_wrap->dte_type = esp_modem_dce_wrap::modem_wrap_dte_type::UART;
return dce_wrap;
}
extern "C" esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
{
return esp_modem_new_dev(ESP_MODEM_DCE_GENETIC, dte_config, dce_config, netif);
}
extern "C" void esp_modem_destroy(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap) {
delete dce_wrap->dce;
delete dce_wrap;
}
}
extern "C" esp_err_t esp_modem_set_error_cb(esp_modem_dce_t *dce_wrap, esp_modem_terminal_error_cbt err_cb)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || dce_wrap->dte == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (err_cb) {
dce_wrap->dte->set_error_cb([err_cb](terminal_error err) {
err_cb(convert_terminal_error_enum(err));
});
} else {
dce_wrap->dte->set_error_cb(nullptr);
}
return ESP_OK;
}
extern "C" esp_err_t esp_modem_sync(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sync());
}
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce_mode_t mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
switch (mode) {
case ESP_MODEM_MODE_DATA:
return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_EXIT:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_SWAP:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_DATA:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL;
}
return ESP_ERR_NOT_SUPPORTED;
}
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce_wrap, bool *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
}
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce_wrap, bool txt)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_txt_mode(txt));
}
extern "C" esp_err_t esp_modem_send_sms(esp_modem_dce_t *dce_wrap, const char *number, const char *message)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string number_str(number);
std::string message_str(message);
return command_response_to_esp_err(dce_wrap->dce->send_sms(number_str, message_str));
}
extern "C" esp_err_t esp_modem_sms_character_set(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_character_set());
}
extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce_wrap, const char *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string pin_str(pin);
return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str));
}
extern "C" esp_err_t esp_modem_at(esp_modem_dce_t *dce_wrap, const char *at, char *p_out, int timeout)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string out;
std::string at_str(at);
auto ret = command_response_to_esp_err(dce_wrap->dce->at(at_str, out, timeout));
if ((p_out != NULL) && (!out.empty())) {
strlcpy(p_out, out.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t *dce_wrap, int *rssi, int *ber)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->get_signal_quality(*rssi, *ber));
}
extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce_wrap, char *p_imsi)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imsi;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imsi(imsi));
if (ret == ESP_OK && !imsi.empty()) {
strlcpy(p_imsi, imsi.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce_wrap, int dce_flow, int dte_flow)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_flow_control(dce_flow, dte_flow));
}
extern "C" esp_err_t esp_modem_store_profile(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->store_profile());
}
extern "C" esp_err_t esp_modem_get_imei(esp_modem_dce_t *dce_wrap, char *p_imei)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imei;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imei(imei));
if (ret == ESP_OK && !imei.empty()) {
strlcpy(p_imei, imei.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce_wrap, char *p_name, int *p_act)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_name == nullptr || p_act == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
int act;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_operator_name(name, act));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
*p_act = act;
}
return ret;
}
extern "C" esp_err_t esp_modem_get_module_name(esp_modem_dce_t *dce_wrap, char *p_name)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_module_name(name));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_battery_status(esp_modem_dce_t *dce_wrap, int *p_volt, int *p_bcs, int *p_bcl)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_bcs == nullptr || p_bcl == nullptr || p_volt == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int bcs, bcl, volt;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_battery_status(volt, bcs, bcl));
if (ret == ESP_OK) {
*p_volt = volt;
*p_bcs = bcs;
*p_bcl = bcl;
}
return ret;
}
extern "C" esp_err_t esp_modem_power_down(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->power_down());
}
extern "C" esp_err_t esp_modem_set_operator(esp_modem_dce_t *dce_wrap, int mode, int format, const char *oper)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string operator_str(oper);
return command_response_to_esp_err(dce_wrap->dce->set_operator(mode, format, operator_str));
}
extern "C" esp_err_t esp_modem_set_network_attachment_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_attachment_state(state));
}
extern "C" esp_err_t esp_modem_get_network_attachment_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_attachment_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_radio_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_radio_state(state));
}
extern "C" esp_err_t esp_modem_get_radio_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_radio_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_network_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_mode(mode));
}
extern "C" esp_err_t esp_modem_set_preferred_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_preferred_mode(mode));
}
extern "C" esp_err_t esp_modem_set_network_bands(esp_modem_dce_t *dce_wrap, const char *mode, const int *bands, int size)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string mode_str(mode);
return command_response_to_esp_err(dce_wrap->dce->set_network_bands(mode, bands, size));
}
extern "C" esp_err_t esp_modem_get_network_system_mode(esp_modem_dce_t *dce_wrap, int *p_mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int mode;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_system_mode(mode));
if (ret == ESP_OK) {
*p_mode = mode;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_gnss_power_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_gnss_power_mode(mode));
}
extern "C" esp_err_t esp_modem_get_gnss_power_mode(esp_modem_dce_t *dce_wrap, int *p_mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int mode;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_gnss_power_mode(mode));
if (ret == ESP_OK) {
*p_mode = mode;
}
return ret;
}
extern "C" esp_err_t esp_modem_reset(esp_modem_dce_t *dce_wrap)
{
return command_response_to_esp_err(dce_wrap->dce->reset());
}
extern "C" esp_err_t esp_modem_set_pdp_context(esp_modem_dce_t *dce_wrap, esp_modem_PdpContext_t *c_api_pdp)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
esp_modem::PdpContext pdp{c_api_pdp->apn};
pdp.context_id = c_api_pdp->context_id;
pdp.protocol_type = c_api_pdp->protocol_type;
return command_response_to_esp_err(dce_wrap->dce->set_pdp_context(pdp));
}
extern "C" esp_err_t esp_modem_command(esp_modem_dce_t *dce_wrap, const char *command, esp_err_t(*got_line_fn)(uint8_t *data, size_t len), uint32_t timeout_ms)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || command == nullptr || got_line_fn == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string cmd(command);
return command_response_to_esp_err(dce_wrap->dce->command(cmd, [got_line_fn](uint8_t *data, size_t len) {
switch (got_line_fn(data, len)) {
case ESP_OK:
return command_result::OK;
case ESP_FAIL:
return command_result::FAIL;
default:
return command_result::TIMEOUT;
}
}, timeout_ms));
}
extern "C" esp_err_t esp_modem_set_baud(esp_modem_dce_t *dce_wrap, int baud)
{
return command_response_to_esp_err(dce_wrap->dce->set_baud(baud));
}
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cstring>
#include <cassert>
#include "cxx_include/esp_modem_dte.hpp"
#include "uart_terminal.hpp"
#include "esp_log.h"
#include "cxx_include/esp_modem_api.hpp"
#include "cxx_include/esp_modem_dce_factory.hpp"
#include "esp_modem_c_api_types.h"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
#include "esp_private/c_api_wrapper.hpp"
#ifndef ESP_MODEM_C_API_STR_MAX
#define ESP_MODEM_C_API_STR_MAX 64
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dest, const char *src, size_t len);
#endif
#ifdef CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE
#include CONFIG_ESP_MODEM_CUSTOM_MODULE_HEADER
#endif
//
// C API definitions
using namespace esp_modem;
extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
{
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
if (dce_wrap == nullptr) {
return nullptr;
}
auto dte = create_uart_dte(dte_config);
if (dte == nullptr) {
delete dce_wrap;
return nullptr;
}
dce_wrap->dte = dte;
#ifdef CONFIG_ESP_MODEM_ADD_CUSTOM_MODULE
if (module == ESP_MODEM_DCE_CUSTOM) {
dce_wrap->dce = esp_modem_create_custom_dce(dce_config, dte, netif);
} else
#endif
{
dce_factory::Factory f(convert_modem_enum(module));
dce_wrap->dce = f.build(dce_config, std::move(dte), netif);
}
if (dce_wrap->dce == nullptr) {
delete dce_wrap;
return nullptr;
}
dce_wrap->modem_type = convert_modem_enum(module);
dce_wrap->dte_type = esp_modem_dce_wrap::modem_wrap_dte_type::UART;
return dce_wrap;
}
extern "C" esp_modem_dce_t *esp_modem_new(const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
{
return esp_modem_new_dev(ESP_MODEM_DCE_GENETIC, dte_config, dce_config, netif);
}
extern "C" void esp_modem_destroy(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap) {
delete dce_wrap->dce;
delete dce_wrap;
}
}
extern "C" esp_err_t esp_modem_set_error_cb(esp_modem_dce_t *dce_wrap, esp_modem_terminal_error_cbt err_cb)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || dce_wrap->dte == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (err_cb) {
dce_wrap->dte->set_error_cb([err_cb](terminal_error err) {
err_cb(convert_terminal_error_enum(err));
});
} else {
dce_wrap->dte->set_error_cb(nullptr);
}
return ESP_OK;
}
extern "C" esp_err_t esp_modem_sync(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sync());
}
extern "C" esp_err_t esp_modem_set_mode(esp_modem_dce_t *dce_wrap, esp_modem_dce_mode_t mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
switch (mode) {
case ESP_MODEM_MODE_DATA:
return dce_wrap->dce->set_mode(modem_mode::DATA_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::COMMAND_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_MODE) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_EXIT:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_EXIT) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_SWAP:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_SWAP) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_DATA:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_DATA) ? ESP_OK : ESP_FAIL;
case ESP_MODEM_MODE_CMUX_MANUAL_COMMAND:
return dce_wrap->dce->set_mode(modem_mode::CMUX_MANUAL_COMMAND) ? ESP_OK : ESP_FAIL;
}
return ESP_ERR_NOT_SUPPORTED;
}
extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce_wrap, bool *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
}
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce_wrap, bool txt)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_txt_mode(txt));
}
extern "C" esp_err_t esp_modem_send_sms(esp_modem_dce_t *dce_wrap, const char *number, const char *message)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string number_str(number);
std::string message_str(message);
return command_response_to_esp_err(dce_wrap->dce->send_sms(number_str, message_str));
}
extern "C" esp_err_t esp_modem_sms_character_set(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->sms_character_set());
}
extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce_wrap, const char *pin)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string pin_str(pin);
return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str));
}
extern "C" esp_err_t esp_modem_at(esp_modem_dce_t *dce_wrap, const char *at, char *p_out, int timeout)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string out;
std::string at_str(at);
auto ret = command_response_to_esp_err(dce_wrap->dce->at(at_str, out, timeout));
if ((p_out != NULL) && (!out.empty())) {
strlcpy(p_out, out.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_signal_quality(esp_modem_dce_t *dce_wrap, int *rssi, int *ber)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->get_signal_quality(*rssi, *ber));
}
extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce_wrap, char *p_imsi)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imsi;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imsi(imsi));
if (ret == ESP_OK && !imsi.empty()) {
strlcpy(p_imsi, imsi.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce_wrap, int dce_flow, int dte_flow)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_flow_control(dce_flow, dte_flow));
}
extern "C" esp_err_t esp_modem_store_profile(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->store_profile());
}
extern "C" esp_err_t esp_modem_get_imei(esp_modem_dce_t *dce_wrap, char *p_imei)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string imei;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_imei(imei));
if (ret == ESP_OK && !imei.empty()) {
strlcpy(p_imei, imei.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce_wrap, char *p_name, int *p_act)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_name == nullptr || p_act == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
int act;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_operator_name(name, act));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
*p_act = act;
}
return ret;
}
extern "C" esp_err_t esp_modem_get_module_name(esp_modem_dce_t *dce_wrap, char *p_name)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string name;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_module_name(name));
if (ret == ESP_OK && !name.empty()) {
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
}
return ret;
}
extern "C" esp_err_t esp_modem_get_battery_status(esp_modem_dce_t *dce_wrap, int *p_volt, int *p_bcs, int *p_bcl)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_bcs == nullptr || p_bcl == nullptr || p_volt == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int bcs, bcl, volt;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_battery_status(volt, bcs, bcl));
if (ret == ESP_OK) {
*p_volt = volt;
*p_bcs = bcs;
*p_bcl = bcl;
}
return ret;
}
extern "C" esp_err_t esp_modem_power_down(esp_modem_dce_t *dce_wrap)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->power_down());
}
extern "C" esp_err_t esp_modem_set_operator(esp_modem_dce_t *dce_wrap, int mode, int format, const char *oper)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string operator_str(oper);
return command_response_to_esp_err(dce_wrap->dce->set_operator(mode, format, operator_str));
}
extern "C" esp_err_t esp_modem_set_network_attachment_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_attachment_state(state));
}
extern "C" esp_err_t esp_modem_get_network_attachment_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_attachment_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_radio_state(esp_modem_dce_t *dce_wrap, int state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_radio_state(state));
}
extern "C" esp_err_t esp_modem_get_radio_state(esp_modem_dce_t *dce_wrap, int *p_state)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int state;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_radio_state(state));
if (ret == ESP_OK) {
*p_state = state;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_network_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_network_mode(mode));
}
extern "C" esp_err_t esp_modem_set_preferred_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_preferred_mode(mode));
}
extern "C" esp_err_t esp_modem_set_network_bands(esp_modem_dce_t *dce_wrap, const char *mode, const int *bands, int size)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string mode_str(mode);
return command_response_to_esp_err(dce_wrap->dce->set_network_bands(mode, bands, size));
}
extern "C" esp_err_t esp_modem_get_network_system_mode(esp_modem_dce_t *dce_wrap, int *p_mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int mode;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_network_system_mode(mode));
if (ret == ESP_OK) {
*p_mode = mode;
}
return ret;
}
extern "C" esp_err_t esp_modem_set_gnss_power_mode(esp_modem_dce_t *dce_wrap, int mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return command_response_to_esp_err(dce_wrap->dce->set_gnss_power_mode(mode));
}
extern "C" esp_err_t esp_modem_get_gnss_power_mode(esp_modem_dce_t *dce_wrap, int *p_mode)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
int mode;
auto ret = command_response_to_esp_err(dce_wrap->dce->get_gnss_power_mode(mode));
if (ret == ESP_OK) {
*p_mode = mode;
}
return ret;
}
extern "C" esp_err_t esp_modem_reset(esp_modem_dce_t *dce_wrap)
{
return command_response_to_esp_err(dce_wrap->dce->reset());
}
extern "C" esp_err_t esp_modem_set_pdp_context(esp_modem_dce_t *dce_wrap, esp_modem_PdpContext_t *c_api_pdp)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
return ESP_ERR_INVALID_ARG;
}
esp_modem::PdpContext pdp{c_api_pdp->apn};
pdp.context_id = c_api_pdp->context_id;
pdp.protocol_type = c_api_pdp->protocol_type;
return command_response_to_esp_err(dce_wrap->dce->set_pdp_context(pdp));
}
extern "C" esp_err_t esp_modem_command(esp_modem_dce_t *dce_wrap, const char *command, esp_err_t(*got_line_fn)(uint8_t *data, size_t len), uint32_t timeout_ms)
{
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || command == nullptr || got_line_fn == nullptr) {
return ESP_ERR_INVALID_ARG;
}
std::string cmd(command);
return command_response_to_esp_err(dce_wrap->dce->command(cmd, [got_line_fn](uint8_t *data, size_t len) {
switch (got_line_fn(data, len)) {
case ESP_OK:
return command_result::OK;
case ESP_FAIL:
return command_result::FAIL;
default:
return command_result::TIMEOUT;
}
}, timeout_ms));
}
extern "C" esp_err_t esp_modem_set_baud(esp_modem_dce_t *dce_wrap, int baud)
{
return command_response_to_esp_err(dce_wrap->dce->set_baud(baud));
}

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
*/
@ -261,12 +261,17 @@ command_result set_echo(CommandableIf *t, bool on)
return generic_command_common(t, "ATE0\r");
}
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__ );
std::string pdp_command = "AT+CGDCONT=" + std::to_string(pdp.context_id) +
",\"" + pdp.protocol_type + "\",\"" + pdp.apn + "\"\r";
return generic_command_common(t, pdp_command, 150000);
return generic_command_common(t, pdp_command, timeout_ms);
}
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
{
return set_pdp_context(t, pdp, 1000);
}
command_result set_data_mode(CommandableIf *t)

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
*/
@ -96,6 +96,11 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
{
switch (m) {
case modem_mode::UNDEF:
if (!dte->set_mode(m)) {
return false;
}
mode = m;
return true;
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
break;
case modem_mode::COMMAND_MODE:
@ -151,7 +156,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
mode = modem_mode::CMUX_MANUAL_MODE;
return true;
case modem_mode::CMUX_MANUAL_EXIT:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
if (!dte->set_mode(m)) {
@ -160,7 +165,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
mode = modem_mode::COMMAND_MODE;
return true;
case modem_mode::CMUX_MANUAL_SWAP:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
if (!dte->set_mode(m)) {
@ -168,12 +173,12 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
}
return true;
case modem_mode::CMUX_MANUAL_DATA:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
return transitions::enter_data(*dte, *device, netif);
case modem_mode::CMUX_MANUAL_COMMAND:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
return transitions::exit_data(*dte, *device, netif);

View File

@ -151,10 +151,14 @@ command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32
bool DTE::exit_cmux()
{
if (!cmux_term) {
return false;
}
if (!cmux_term->deinit()) {
return false;
}
exit_cmux_internal();
cmux_term.reset();
return true;
}
@ -174,6 +178,10 @@ void DTE::exit_cmux_internal()
bool DTE::setup_cmux()
{
if (cmux_term) {
ESP_LOGE("esp_modem_dte", "Cannot setup_cmux(), cmux_term already exists");
return false;
}
cmux_term = std::make_shared<CMux>(primary_term, std::move(buffer));
if (cmux_term == nullptr) {
return false;
@ -198,6 +206,11 @@ bool DTE::setup_cmux()
bool DTE::set_mode(modem_mode m)
{
// transitions (any) -> UNDEF
if (m == modem_mode::UNDEF) {
mode = m;
return true;
}
// transitions (COMMAND|UNDEF) -> CMUX
if (m == modem_mode::CMUX_MODE) {
if (mode == modem_mode::UNDEF || mode == modem_mode::COMMAND_MODE) {
@ -246,7 +259,7 @@ bool DTE::set_mode(modem_mode m)
return false;
}
// manual CMUX transitions: Exit CMUX
if (m == modem_mode::CMUX_MANUAL_EXIT && mode == modem_mode::CMUX_MANUAL_MODE) {
if (m == modem_mode::CMUX_MANUAL_EXIT && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
if (exit_cmux()) {
mode = modem_mode::COMMAND_MODE;
return true;
@ -255,7 +268,7 @@ bool DTE::set_mode(modem_mode m)
return false;
}
// manual CMUX transitions: Swap terminals
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
if (m == modem_mode::CMUX_MANUAL_SWAP && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
secondary_term.swap(primary_term);
set_command_callbacks();
return true;
@ -334,6 +347,9 @@ 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 (result != command_result::TIMEOUT) {
return false; // this line has been processed already (got OK or FAIL previously)
}
if (memchr(data + consumed, separator, len)) {
result = got_line(data, consumed + len);
if (result == command_result::OK || result == command_result::FAIL) {

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
*/
@ -82,4 +82,9 @@ command_result SIM800::set_data_mode()
return dce_commands::set_data_mode_sim8xx(dte.get());
}
command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp)
{
return dce_commands::set_pdp_context(dte.get(), pdp, 300);
}
}

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
*/
@ -33,6 +33,20 @@ TEST_CASE("DTE command races", "[esp_modem]")
// this command should either timeout or finish successfully
CHECK((ret == command_result::TIMEOUT || ret == command_result::OK));
}
// Now we test the same, but with some garbage after the expected data and inject the reply in chunks by 3 bytes
uint8_t resp2[] = {'O', 'K', '\n', '1', '2', '\n'};
for (int i = 0; i < 1000; ++i) {
loopback->inject(&resp2[0], sizeof(resp2), 3, /* 1ms before injecting reply */0, 0);
auto ret = dce->command("check\n", [&](uint8_t *data, size_t len) {
if (len > 0 && data[0] == 'O') { // expected reply only when it starts with '0'
return command_result::OK;
}
return esp_modem::command_result::TIMEOUT;
}, 1);
// this command should either timeout or finish successfully
CHECK((ret == command_result::TIMEOUT || ret == command_result::OK));
}
}
TEST_CASE("Test polymorphic delete for custom device/dte", "[esp_modem]")
@ -247,3 +261,88 @@ TEST_CASE("Test CMUX protocol by injecting payloads", "[esp_modem]")
CHECK(ret == command_result::OK);
}
}
TEST_CASE("Command and Data mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto loopback = term.get();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// UNDEF -> CMD (OK)
uint8_t resp[] = "DISCONNECTED\n";
loopback->inject(&resp[0], sizeof(resp), sizeof(resp), /* 10ms before injecting reply */100, 0);
loopback->write(nullptr, 0); /* this triggers sending the injected response */
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
loopback->inject(nullptr, 0, 0, 0, 0); /* reset injection, use synchronous replies now */
// CMD -> CMD (Fail)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == false);
// Forcing transition to CMD (via UNDEF)
// CMD -> UNDEF (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
// UNDEF -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
// CMD -> DATA (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == true);
// DATA -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("CMUX mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// UNDEF -> CMUX (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MODE) == true);
// CMUX -> DATA (Fail)
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == false);
// CMUX back -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("CMUX manual mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// Happy flow transitions of Manual CMUX transitions
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true);
// Cannot test CMUX_MANUAL_DATA -> CMUX_MANUAL_COMMAND with our mocked terminal for now
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
// Check some out of order manual transitions, most of them are allowed,
// but some fail as modem layers report issues with specific steps
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == false); // cannot go directly to SWAP
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true); // can go via UNDEF
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == false); // EXIT is allowed, but CMUX terms don't exist
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true); // Enter CMUX (via UNDEF)
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true); // Go directly to DATA mode
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true); // Exit CMUX
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true); // Succeeds from any state
}

View File

@ -64,8 +64,8 @@ static void on_ppp_changed(void *arg, esp_event_base_t event_base,
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);
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
auto p_netif = static_cast<esp_netif_t **>(event_data);
ESP_LOGI(TAG, "User interrupted event from netif:%p", *p_netif);
xEventGroupSetBits(event_group, 2);
}
}

View File

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

View File

@ -1,5 +1,28 @@
# Changelog
## [1.2.1](https://github.com/espressif/esp-protocols/commits/websocket-v1.2.1)
### Bug Fixes
- consider failure if return value of `esp_websocket_client_send_with_exact_opcode` less than 0 ([f523b4d](https://github.com/espressif/esp-protocols/commit/f523b4d))
- fix of return value for `esp_websocket_client_send_with_opcode` API ([ba33588](https://github.com/espressif/esp-protocols/commit/ba33588))
## [1.2.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.2.0)
### Features
- Added new API `esp_websocket_client_append_header` ([39e9725](https://github.com/espressif/esp-protocols/commit/39e9725))
- Added new APIs to support fragmented messages transmission ([fae80e2](https://github.com/espressif/esp-protocols/commit/fae80e2))
### Bug Fixes
- Reference common component from IDF ([74fc228](https://github.com/espressif/esp-protocols/commit/74fc228))
- Revert referencing protocol_examples_common from IDF ([b176d3a](https://github.com/espressif/esp-protocols/commit/b176d3a))
- reference protocol_examples_common from IDF ([025ede1](https://github.com/espressif/esp-protocols/commit/025ede1))
- specify override_path in example manifests ([d5e7898](https://github.com/espressif/esp-protocols/commit/d5e7898))
- Return status code correctly on esp_websocket_client_send_with_opcode ([ac8f1de](https://github.com/espressif/esp-protocols/commit/ac8f1de))
- Fix pytest exclusion, gitignore, and changelog checks ([2696221](https://github.com/espressif/esp-protocols/commit/2696221))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.1.0)
### Features

View File

@ -545,15 +545,20 @@ static esp_err_t esp_websocket_client_create_transport(esp_websocket_client_hand
return ESP_OK;
}
static bool esp_websocket_client_send_with_exact_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, TickType_t timeout)
{
int ret = -1;
int need_write = len;
int wlen = 0, widx = 0;
bool contained_fin = opcode & WS_TRANSPORT_OPCODES_FIN;
while (widx < len || opcode) { // allow for sending "current_opcode" only message with len==0
if (need_write > client->buffer_size) {
need_write = client->buffer_size;
opcode = opcode & ~WS_TRANSPORT_OPCODES_FIN;
} else if (contained_fin) {
opcode = opcode | WS_TRANSPORT_OPCODES_FIN;
}
memcpy(client->tx_buffer, data + widx, need_write);
// send with ws specific way and specific opcode
@ -571,14 +576,14 @@ static bool esp_websocket_client_send_with_exact_opcode(esp_websocket_client_han
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
}
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
return false;
return ret;
}
opcode = 0;
widx += wlen;
need_write = len - widx;
}
esp_websocket_free_buf(client, true);
return true;
return widx;
}
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config)
@ -1227,9 +1232,9 @@ int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client,
ret = ESP_FAIL;
goto unlock_and_return;
}
if (esp_websocket_client_send_with_exact_opcode(client, opcode | WS_TRANSPORT_OPCODES_FIN, data, len, timeout) != true) {
ret = esp_websocket_client_send_with_exact_opcode(client, opcode | WS_TRANSPORT_OPCODES_FIN, data, len, timeout);
if (ret < 0) {
ESP_LOGE(TAG, "Failed to send the buffer");
ret = ESP_FAIL;
goto unlock_and_return;
}
unlock_and_return:

View File

@ -1,4 +1,4 @@
version: "1.1.0"
version: "1.2.1"
description: WebSocket protocol client for ESP-IDF
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client
dependencies:

View File

@ -26,8 +26,7 @@
#endif
#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 0)
// IDF <= v5.1 does not support enabling/disabling esp-wifi
#define MDNS_ESP_WIFI_ENABLED 1
#define MDNS_ESP_WIFI_ENABLED CONFIG_SOC_WIFI_SUPPORTED
#else
#define MDNS_ESP_WIFI_ENABLED CONFIG_ESP_WIFI_ENABLED
#endif

View File

@ -67,22 +67,72 @@ supports SIM800, BG96, SIM7600.
Use cases
---------
Users interact with the esp-modem using the DCEs interface, to
basically
Users interact with the esp-modem using the DCE's interface, to basically
- Switch between command and data mode to connect to the internet via cellular network.
- Send various commands to the device (e.g. send SMS)
- Send various commands to the device (e.g. send SMS)
The applications typically register handlers for network events to
receive notification on the network availability and IP address changes.
Common use cases of the esp-modem are also listed as the examples:
- ``examples/pppos_client`` simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
- ``examples/modem_console`` is an example to exercise all possible module commands in a console application.
- ``examples/ap_to_pppos`` this example focuses on the network
connectivity of the esp-modem and provides a WiFi AP that forwards
packets (and uses NAT) to and from the PPPoS connection.
- ``examples/pppos_client`` simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
- ``examples/modem_console`` is an example to exercise all possible module commands in a console application.
- ``examples/ap_to_pppos`` this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
Working modes
~~~~~~~~~~~~~
Modem devices could work in multiple different modes, esp-modem library
uses these states to describe them:
- Standard modes:
- Command mode -- This mode is used for sending AT commands
- Data or PPP mode -- This mode is used for data communication (to create PPPoS tunnel between the device and the library)
- Multiplexing modes:
- CMUX mode -- This mode creates two virtual channels and uses one for sending AT commands and the other one for data communication.
- DUAL mode -- This mode uses two physical channels the same way as CMUX. This mode is supported only by certain devices, usually with USB interface.
- Manual CMUX modes -- These modes are designed for applications to have better control over CMUX mode transitions. It allows setting up the virtual channels,
switching between channels, transitioning between data and command modes for each channel separately, and exiting the CMUX.
Switching between common modes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The diagram below depicts allowed transitions between the most common modes
::
+---------+ +---------+
| COMMAND |<-->| DATA |
+---------+ +---------+
^
|
v
+-------+
| CMUX |
+-------+
Note that it is possible to switch from any mode to the "UNDEF" mode and vice-versa.
Switching between manual modes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The diagram below depicts allowed transitions between manual CMUX modes
::
+------------------------------------
| |
+----------+ +-------------+ +------------+ +----------+
| |<-->| MANUAL_DATA |<-->| MANUAL_CMD |<-->| COMMAND |
| CMUX | +-------------+ +------------+ | (CMUX |
| MANUAL | | | MANUAL |
| | +-------------+ | EXIT) |
| |<-->| MANUAL_SWAP |<-------------------->| |
+----------+ +-------------+ +----------+
| |
+-----------------------------------------------------+
Note that transitioning between "MANUAL_DATA" and "MANUAL_CMD" switches the secondary terminal (dedicated to PPP session) and could be used for recovering data communication if PPP session gets dropped.
Extensibility
-------------
@ -90,13 +140,12 @@ Extensibility
CMUX
~~~~
Implementation of virtual terminals is an experimental feature, which
allows users to also issue commands in the data mode, after creating
multiple virtual terminals, designating some of them solely to data
mode, others solely to command mode.
Implements virtual terminals which allow users to also issue commands in the data mode;
after creating two virtual terminals, designating one of them solely to data mode, and
another one solely to command mode.
DTEs
~~~~~
DTE
~~~
Currently, we support only UART (and USB as a preview feature), but
modern modules support other communication interfaces, such as USB, SPI.
@ -110,6 +159,10 @@ commands might have a different implementation. Adding a new device
means to provide a new implementation as a class derived from
``GenericModule``, where we could add new commands or modify the
existing ones.
If you have to support a custom device with C-API, please refer to
the example ``examples/pppos_client`` and enable ``ESP_MODEM_ADD_CUSTOM_MODULE``.
For advanced use-case, mainly with C++ API and/or usage of esp_modem's
Factory class, please read <advanced_api>.
Configuration
-------------