mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-06-25 09:21:32 +02:00
Compare commits
46 Commits
websocket-
...
console_cm
Author | SHA1 | Date | |
---|---|---|---|
bf79e2f734 | |||
a22c9e8da9 | |||
8548dabbd6 | |||
d2e4370d30 | |||
e4c353760c | |||
4be5efc84e | |||
7451ec2a75 | |||
19fb36000c | |||
7d0eb5c48c | |||
a0a58e60b5 | |||
8285e9730f | |||
cf845cc23e | |||
a532e76ec1 | |||
194d1179bf | |||
aadda22961 | |||
a6845f4cdd | |||
d88cd6123b | |||
10bb738911 | |||
edc3e7251f | |||
774d1c75e6 | |||
f2223dd719 | |||
1d80cbc179 | |||
82c2cf8936 | |||
997cff5338 | |||
ed8bbf2666 | |||
b220d1ee5e | |||
671190bc6d | |||
1bf6843b3e | |||
b78569860d | |||
27f955abfa | |||
bf99f287bc | |||
909e8d9494 | |||
0c6bb4bedc | |||
afbca5343e | |||
3f49583761 | |||
c6448c3bd1 | |||
f523b4dc84 | |||
6fda4c134e | |||
ba33588008 | |||
9fe44a4504 | |||
76a4a9f97a | |||
8bab1420d3 | |||
79d38e54f2 | |||
5396de42d0 | |||
aff571df2c | |||
93cb2caadb |
32
.github/workflows/console_cmd_ifconfig__build.yml
vendored
Normal file
32
.github/workflows/console_cmd_ifconfig__build.yml
vendored
Normal 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
|
32
.github/workflows/console_cmd_wifi__build.yml
vendored
Normal file
32
.github/workflows/console_cmd_wifi__build.yml
vendored
Normal 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
|
40
.github/workflows/modem__build-host-tests.yml
vendored
40
.github/workflows/modem__build-host-tests.yml
vendored
@ -8,25 +8,19 @@ on:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_esp_modem:
|
||||
build_esp_modem_examples:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Build
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v4.2", "release-v4.3", "release-v4.4", "release-v5.0"]
|
||||
idf_ver: ["latest", "release-v4.3", "release-v4.4", "release-v5.0"]
|
||||
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
|
||||
exclude:
|
||||
- idf_ver: "release-v4.2"
|
||||
example: simple_cmux_client
|
||||
- idf_ver: "release-v4.2"
|
||||
example: modem_tcp_client
|
||||
- idf_ver: "release-v4.3"
|
||||
example: modem_tcp_client
|
||||
- idf_ver: "release-v4.4"
|
||||
example: modem_tcp_client
|
||||
include:
|
||||
- idf_ver: "release-v4.2"
|
||||
skip_config: usb
|
||||
- idf_ver: "release-v4.3"
|
||||
skip_config: usb
|
||||
- idf_ver: "release-v5.0"
|
||||
@ -50,7 +44,33 @@ jobs:
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
cd $GITHUB_WORKSPACE/protocols
|
||||
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/examples/.build-test-rules.yml
|
||||
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/.build-test-rules.yml
|
||||
|
||||
build_esp_modem_tests:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Build tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
|
||||
test: ["target", "target_ota"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: protocols
|
||||
- name: Build ${{ matrix.test }} with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
cd $GITHUB_WORKSPACE/protocols
|
||||
python ./ci/build_apps.py components/esp_modem/test/${{ matrix.test }} -m components/esp_modem/.build-test-rules.yml
|
||||
|
||||
|
||||
host_test_esp_modem:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
|
2
.github/workflows/publish-docs-component.yml
vendored
2
.github/workflows/publish-docs-component.yml
vendored
@ -95,5 +95,7 @@ jobs:
|
||||
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 }}
|
||||
|
@ -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:
|
||||
|
@ -41,3 +41,11 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
### console_cmd_ping
|
||||
|
||||
* Brief introduction [README](components/console_cmd_ping/README.md)
|
||||
|
||||
### console_cmd_ifconfig
|
||||
|
||||
* Brief introduction [README](components/console_cmd_ifconfig/README.md)
|
||||
|
||||
### console_cmd_wifi
|
||||
|
||||
* Brief introduction [README](components/console_cmd_wifi/README.md)
|
||||
|
@ -2,9 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../.. $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_chat)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -2,9 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(async_http_request)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -2,9 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_sock4)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -2,9 +2,6 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
set(EXCLUDE_COMPONENTS openssl)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -2,9 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_tcp_echo_server)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -2,9 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_udp_echo_server)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/asio:
|
||||
version: "^1.14.1"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
8
components/console_cmd_ifconfig/.cz.yaml
Normal file
8
components/console_cmd_ifconfig/.cz.yaml
Normal 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.1
|
||||
version_files:
|
||||
- idf_component.yml
|
13
components/console_cmd_ifconfig/CHANGELOG.md
Normal file
13
components/console_cmd_ifconfig/CHANGELOG.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/console_cmd_ifconfig-v1.0.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed ifconfig command for PPP interface and IPv4 only ([8548dabb](https://github.com/espressif/esp-protocols/commit/8548dabb))
|
||||
|
||||
## [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))
|
4
components/console_cmd_ifconfig/CMakeLists.txt
Normal file
4
components/console_cmd_ifconfig/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "console_ifconfig.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_netif console esp_eth
|
||||
WHOLE_ARCHIVE)
|
201
components/console_cmd_ifconfig/LICENSE
Normal file
201
components/console_cmd_ifconfig/LICENSE
Normal 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.
|
113
components/console_cmd_ifconfig/README.md
Normal file
113
components/console_cmd_ifconfig/README.md
Normal file
@ -0,0 +1,113 @@
|
||||
# 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.
|
||||
```
|
||||
|
||||
## Usage:
|
||||
|
||||
### Creating an ethernet interface
|
||||
```
|
||||
esp> ifconfig eth init
|
||||
Internal(IP101): pins: 23,18, Id: 0
|
||||
esp> ifconfig netif create 0
|
||||
```
|
||||
|
||||
### Removing an interface and deinitializing ethernet
|
||||
```
|
||||
esp> ifconfig netif destroy en1
|
||||
I (8351266) ethernet_init: Ethernet(IP101[23,18]) Link Down
|
||||
I (8351266) ethernet_init: Ethernet(IP101[23,18]) Stopped
|
||||
esp> ifconfig eth deinit
|
||||
```
|
||||
|
||||
### Set default interface
|
||||
```
|
||||
esp> ifconfig en1 default
|
||||
```
|
||||
|
||||
### Enable NAPT on an interface
|
||||
```
|
||||
esp> ifconfig en1 napt enable
|
||||
I (8467116) console_ifconfig: Setting napt enable on en1
|
||||
```
|
||||
|
||||
### Enable DHCP client on an interface
|
||||
```
|
||||
esp> ifconfig en1 dhcp client enable
|
||||
```
|
||||
|
||||
### Enable static IP on an interface
|
||||
```
|
||||
esp> ifconfig en1 dhcp client disable
|
||||
```
|
||||
|
||||
### Set static IP address
|
||||
```
|
||||
esp> ifconfig en1 ip 192.168.5.2
|
||||
I (111466) console_ifconfig: Setting ip: 192.168.5.2
|
||||
esp> ifconfig en1 mask 255.255.255.0
|
||||
I (130946) console_ifconfig: Setting mask: 255.255.255.0
|
||||
esp> ifconfig en1 gw 192.168.5.1
|
||||
I (143576) console_ifconfig: Setting gw: 192.168.5.1
|
||||
I (143576) esp_netif_handlers: eth ip: 192.168.5.2, mask: 255.255.255.0, gw: 192.168.5.1
|
||||
```
|
682
components/console_cmd_ifconfig/console_ifconfig.c
Normal file
682
components/console_cmd_ifconfig/console_ifconfig.c
Normal file
@ -0,0 +1,682 @@
|
||||
/*
|
||||
* 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"
|
||||
#if CONFIG_LWIP_IPV6
|
||||
#include "lwip/ip6.h"
|
||||
#endif
|
||||
#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"},
|
||||
#if CONFIG_LWIP_IPV6
|
||||
{.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"},
|
||||
#endif
|
||||
{.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_unsafe(esp_netif)) != NULL) {
|
||||
#else
|
||||
while ((esp_netif = esp_netif_next(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;
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
/* 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;
|
||||
}
|
||||
#endif
|
||||
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_unsafe(esp_netif);
|
||||
#else
|
||||
esp_netif = esp_netif_next(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)) {
|
||||
return esp_netif_napt_enable(esp_netif);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
#if CONFIG_LWIP_IPV6
|
||||
int ip6_addrs_count = 0;
|
||||
esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES];
|
||||
#endif
|
||||
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
|
||||
if (lwip_netif != NULL) {
|
||||
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*/
|
||||
if (lwip_netif != NULL) {
|
||||
ESP_LOGI(TAG, "NAPT: %s", lwip_netif->napt ? "enabled" : "disabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
/* 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]));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Print Interface and Link Status*/
|
||||
ESP_LOGI(TAG, "Interface Status: %s", esp_netif_is_netif_up(esp_netif) ? "UP" : "DOWN");
|
||||
if (lwip_netif != NULL) {
|
||||
ESP_LOGI(TAG, "Link Status: %s", netif_is_link_up(lwip_netif) ? "UP" : "DOWN");
|
||||
}
|
||||
ESP_LOGI(TAG, "");
|
||||
}
|
||||
|
||||
|
||||
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_unsafe(esp_netif);
|
||||
#else
|
||||
esp_netif = esp_netif_next(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, ð_cfg));
|
||||
for (int i = 0; i < MAX_ETH_NETIF_COUNT; i++) {
|
||||
if (iface_list[i].state == ETH_INITIALIZED) {
|
||||
esp_netif = esp_netif_new(ð_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(ð_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(ð_handle_g, ð_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;
|
||||
}
|
25
components/console_cmd_ifconfig/console_ifconfig.h
Normal file
25
components/console_cmd_ifconfig/console_ifconfig.h
Normal 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
|
@ -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)
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "ifconfig-basic.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
console_cmd_ifconfig:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
@ -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());
|
||||
|
||||
}
|
@ -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
|
12
components/console_cmd_ifconfig/idf_component.yml
Normal file
12
components/console_cmd_ifconfig/idf_component.yml
Normal file
@ -0,0 +1,12 @@
|
||||
version: 1.0.1
|
||||
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'
|
@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(console): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py console_cmd_ping
|
||||
tag_format: console_cmd_ping-v$version
|
||||
version: 0.0.9
|
||||
version: 1.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
7
components/console_cmd_ping/CHANGELOG.md
Normal file
7
components/console_cmd_ping/CHANGELOG.md
Normal 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))
|
@ -1,4 +1,4 @@
|
||||
version: 0.0.9
|
||||
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:
|
||||
|
8
components/console_cmd_wifi/.cz.yaml
Normal file
8
components/console_cmd_wifi/.cz.yaml
Normal 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.1
|
||||
version_files:
|
||||
- idf_component.yml
|
17
components/console_cmd_wifi/CHANGELOG.md
Normal file
17
components/console_cmd_wifi/CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/console_cmd_wifi-v1.0.1)
|
||||
|
||||
### Features
|
||||
|
||||
- Console for runtime wifi configuration ([194d1179](https://github.com/espressif/esp-protocols/commit/194d1179))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed license file for console_cmd_wifi ([8285e973](https://github.com/espressif/esp-protocols/commit/8285e973))
|
||||
|
||||
## [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))
|
4
components/console_cmd_wifi/CMakeLists.txt
Normal file
4
components/console_cmd_wifi/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "console_wifi.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_netif console esp_wifi
|
||||
WHOLE_ARCHIVE)
|
201
components/console_cmd_wifi/LICENSE
Normal file
201
components/console_cmd_wifi/LICENSE
Normal 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.
|
44
components/console_cmd_wifi/README.md
Normal file
44
components/console_cmd_wifi/README.md
Normal 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.
|
276
components/console_cmd_wifi/console_wifi.c
Normal file
276
components/console_cmd_wifi/console_wifi.c
Normal 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;
|
||||
}
|
25
components/console_cmd_wifi/console_wifi.h
Normal file
25
components/console_cmd_wifi/console_wifi.h
Normal 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
|
@ -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)
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "wifi-basic.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
console_cmd_wifi:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
@ -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());
|
||||
|
||||
}
|
@ -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)
|
10
components/console_cmd_wifi/idf_component.yml
Normal file
10
components/console_cmd_wifi/idf_component.yml
Normal file
@ -0,0 +1,10 @@
|
||||
version: 1.0.1
|
||||
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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
@ -22,11 +23,13 @@ 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);
|
||||
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
|
||||
[[nodiscard]] bool set_ca_cert(const_buf crt);
|
||||
bool set_hostname(const char *name);
|
||||
virtual int send(const unsigned char *buf, size_t len) = 0;
|
||||
virtual int recv(unsigned char *buf, size_t len) = 0;
|
||||
size_t get_available_bytes();
|
||||
@ -41,6 +44,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 +56,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_;
|
||||
|
||||
};
|
||||
|
@ -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];
|
||||
@ -106,6 +116,16 @@ bool Tls::set_ca_cert(const_buf crt)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tls::set_hostname(const char *name)
|
||||
{
|
||||
int ret = mbedtls_ssl_set_hostname(&ssl_, name);
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_ssl_set_hostname", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Tls::Tls()
|
||||
{
|
||||
mbedtls_x509_crt_init(&public_cert_);
|
||||
@ -132,3 +152,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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
@ -170,6 +170,8 @@ public:
|
||||
*/
|
||||
class BG96: public GenericModule {
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
command_result set_pdp_context(PdpContext &pdp) override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@
|
||||
*/
|
||||
struct esp_modem_vfs_uart_creator {
|
||||
const char *dev_name; /*!< VFS device name, e.g. /dev/uart/n */
|
||||
const struct esp_modem_uart_term_config uart; /*!< UART driver init struct */
|
||||
struct esp_modem_uart_term_config uart; /*!< UART driver init struct */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
8
components/esp_modem/test/target_ota/CMakeLists.txt
Normal file
8
components/esp_modem/test/target_ota/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../.." "../../examples/modem_tcp_client/components")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ota_test)
|
27
components/esp_modem/test/target_ota/README.md
Normal file
27
components/esp_modem/test/target_ota/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Target test running OTA update
|
||||
|
||||
## Overview
|
||||
|
||||
The aim of this test is to exercise the most commonly failing scenario, running OTA over PPPoS with https.
|
||||
|
||||
This project opens a data session, runs basic mqtt operations and initiates OTA update.
|
||||
It supports the following test configurations:
|
||||
* Using a real modem device (default config)
|
||||
* Using VFS device (only to exercise VFS DTE)
|
||||
* Using network-only DCE (connecting directly to PPP server) -- needs some configuration
|
||||
|
||||
### Configuring the PPP server
|
||||
|
||||
You need to run these applications on your host machine:
|
||||
* PPP server
|
||||
```bash
|
||||
sudo pppd /dev/ttyUSB1 115200 192.168.11.1:192.168.11.2 ms-dns 8.8.8.8 modem local noauth debug nocrtscts nodetach +ipv6
|
||||
```
|
||||
* MQTT broker: Running mosquitto in the default config is enough, configuring the broker's URL to the local PPP address: `config.broker.address.uri = "mqtt://192.168.11.1";`
|
||||
* HTTP server: Need to support HTTP/1.1 (to support ranges). You can use the script `http_server.py` and configure the OTA endpoint as `"https://192.168.11.1:1234/esp32.bin"`
|
||||
|
||||
## Potential issues
|
||||
|
||||
When running this test it is expected to experience some buffer overflows or connection interruption.
|
||||
The modem library should recover from these failure modes and continue and complete OTA update.
|
||||
These issues are expected, since UART ISR is deliberately not placed into IRAM in the test configuration to exhibit some minor communication glitches.
|
BIN
components/esp_modem/test/target_ota/bin/blink.bin
Normal file
BIN
components/esp_modem/test/target_ota/bin/blink.bin
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS manual_ota.cpp transport_batch_tls.cpp
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES extra_tcp_transports esp_http_client app_update)
|
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "manual_ota.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_app_format.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_transport_tcp.h"
|
||||
#include "transport_batch_tls.hpp"
|
||||
|
||||
static const char *TAG = "manual_ota";
|
||||
|
||||
bool manual_ota::begin()
|
||||
{
|
||||
if (status != state::UNDEF) {
|
||||
ESP_LOGE(TAG, "Invalid state for manual_ota::perform");
|
||||
return false;
|
||||
}
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
|
||||
if (configured != running) {
|
||||
ESP_LOGE(TAG, "Configured OTA boot partition at offset 0x%08" PRIx32 ", but running from offset 0x%08" PRIx32, configured->address, running->address);
|
||||
return false;
|
||||
}
|
||||
|
||||
status = state::INIT;
|
||||
max_buffer_size_ = size_ * 1024;
|
||||
if (mode_ == mode::BATCH) {
|
||||
#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT
|
||||
esp_transport_handle_t tcp = esp_transport_tcp_init();
|
||||
ssl_ = esp_transport_batch_tls_init(tcp, max_buffer_size_);
|
||||
http_.config_.transport = ssl_;
|
||||
if (!esp_transport_batch_set_ca_cert(ssl_, http_.config_.cert_pem, 0)) {
|
||||
return fail();
|
||||
}
|
||||
if (!esp_transport_batch_set_cn(ssl_, common_name_)) {
|
||||
return fail();
|
||||
}
|
||||
#else
|
||||
ESP_LOGE(TAG, "mode::BATCH Cannot be used without CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!http_.init()) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
image_length_ = http_.get_image_len();
|
||||
if (image_length_ <= 0) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
if (image_length_ > size_) {
|
||||
if (!http_.set_range(0, max_buffer_size_ - 1)) {
|
||||
return fail();
|
||||
}
|
||||
}
|
||||
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
|
||||
|
||||
partition_ = esp_ota_get_next_update_partition(nullptr);
|
||||
if (partition_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Invalid update partition");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%" PRIx32, partition_->subtype, partition_->address);
|
||||
|
||||
file_length_ = 0;
|
||||
reconnect_attempts_ = 0;
|
||||
buffer_.resize(max_buffer_size_);
|
||||
status = state::IMAGE_CHECK;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manual_ota::perform()
|
||||
{
|
||||
if (status != state::IMAGE_CHECK && status != state::START) {
|
||||
ESP_LOGE(TAG, "Invalid state for manual_ota::perform");
|
||||
return false;
|
||||
}
|
||||
esp_err_t err = esp_http_client_open(http_.handle_, 0);
|
||||
if (err != ESP_OK) {
|
||||
if (image_length_ == file_length_) {
|
||||
status = state::END;
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_http_client_close(http_.handle_);
|
||||
ESP_LOGI(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
|
||||
if (reconnect_attempts_++ < max_reconnect_attempts_) {
|
||||
return true; // will retry in the next iteration
|
||||
}
|
||||
return fail();
|
||||
}
|
||||
esp_http_client_fetch_headers(http_.handle_);
|
||||
|
||||
int batch_len = max_buffer_size_;
|
||||
if (mode_ == mode::BATCH) {
|
||||
batch_len = esp_transport_batch_tls_pre_read(ssl_, max_buffer_size_, timeout_ * 1000);
|
||||
if (batch_len < 0) {
|
||||
ESP_LOGE(TAG, "Error: Failed to pre-read plain text data!");
|
||||
fail();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int data_read = esp_http_client_read(http_.handle_, buffer_.data(), batch_len);
|
||||
|
||||
if (data_read < 0) {
|
||||
ESP_LOGE(TAG, "Error: SSL data read error");
|
||||
return fail();
|
||||
} else if (data_read > 0) {
|
||||
esp_http_client_close(http_.handle_);
|
||||
|
||||
if (status == state::IMAGE_CHECK) {
|
||||
esp_app_desc_t new_app_info;
|
||||
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
|
||||
// check current version with downloading
|
||||
memcpy(&new_app_info, &buffer_[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
|
||||
|
||||
esp_app_desc_t running_app_info;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
|
||||
}
|
||||
|
||||
const esp_partition_t *last_invalid_app = esp_ota_get_last_invalid_partition();
|
||||
esp_app_desc_t invalid_app_info;
|
||||
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
|
||||
}
|
||||
|
||||
// check current version with last invalid partition
|
||||
if (last_invalid_app != NULL) {
|
||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
ESP_LOGW(TAG, "New version is the same as invalid version.");
|
||||
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
|
||||
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
|
||||
return fail();
|
||||
}
|
||||
}
|
||||
|
||||
status = state::START;
|
||||
err = esp_ota_begin(partition_, OTA_WITH_SEQUENTIAL_WRITES, &update_handle_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
|
||||
return fail();
|
||||
}
|
||||
ota_begin = true;
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Received chunk doesn't contain app descriptor");
|
||||
return fail();
|
||||
}
|
||||
}
|
||||
err = esp_ota_write(update_handle_, (const void *)buffer_.data(), data_read);
|
||||
if (err != ESP_OK) {
|
||||
return fail();
|
||||
}
|
||||
file_length_ += data_read;
|
||||
ESP_LOGI(TAG, "Written image length %d", file_length_);
|
||||
|
||||
if (image_length_ == file_length_) {
|
||||
status = state::END;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!prepare_reconnect()) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
} else if (data_read == 0) {
|
||||
if (file_length_ == 0) {
|
||||
// Try to handle possible HTTP redirections
|
||||
if (!http_.handle_redirects()) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
err = esp_http_client_open(http_.handle_, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
|
||||
return fail();
|
||||
}
|
||||
esp_http_client_fetch_headers(http_.handle_);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manual_ota::prepare_reconnect()
|
||||
{
|
||||
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
|
||||
return http_.set_range(file_length_,
|
||||
(image_length_ - file_length_) > max_buffer_size_ ? (file_length_ + max_buffer_size_ - 1) : 0);
|
||||
}
|
||||
|
||||
bool manual_ota::fail()
|
||||
{
|
||||
status = state::FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool manual_ota::end()
|
||||
{
|
||||
if (status == state::END) {
|
||||
if (!http_.is_data_complete()) {
|
||||
ESP_LOGE(TAG, "Error in receiving complete file");
|
||||
return fail();
|
||||
}
|
||||
esp_err_t err = esp_ota_end(update_handle_);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
|
||||
}
|
||||
return fail();
|
||||
}
|
||||
ota_begin = false;
|
||||
err = esp_ota_set_boot_partition(partition_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
||||
return fail();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
manual_ota::~manual_ota()
|
||||
{
|
||||
if (ota_begin) {
|
||||
esp_ota_abort(update_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool manual_ota::http_client::handle_redirects()
|
||||
{
|
||||
int status_code = esp_http_client_get_status_code(handle_);
|
||||
ESP_LOGW(TAG, "Status code: %d", status_code);
|
||||
esp_err_t err = esp_http_client_set_redirection(handle_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "URL redirection Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = esp_http_client_open(handle_, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
esp_http_client_fetch_headers(handle_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manual_ota::http_client::set_range(size_t from, size_t to)
|
||||
{
|
||||
char *header_val = nullptr;
|
||||
if (to != 0) {
|
||||
asprintf(&header_val, "bytes=%d-%d", from, to);
|
||||
} else {
|
||||
asprintf(&header_val, "bytes=%d-", from);
|
||||
}
|
||||
if (header_val == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
|
||||
return false;
|
||||
}
|
||||
esp_http_client_set_header(handle_, "Range", header_val);
|
||||
free(header_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manual_ota::http_client::is_data_complete()
|
||||
{
|
||||
return esp_http_client_is_complete_data_received(handle_);
|
||||
}
|
||||
|
||||
manual_ota::http_client::~http_client()
|
||||
{
|
||||
if (handle_) {
|
||||
esp_http_client_close(handle_);
|
||||
esp_http_client_cleanup(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool manual_ota::http_client::init()
|
||||
{
|
||||
handle_ = esp_http_client_init(&config_);
|
||||
return handle_ != nullptr;
|
||||
}
|
||||
|
||||
int64_t manual_ota::http_client::get_image_len()
|
||||
{
|
||||
esp_http_client_set_method(handle_, HTTP_METHOD_HEAD);
|
||||
esp_err_t err = esp_http_client_perform(handle_);
|
||||
if (err == ESP_OK) {
|
||||
int http_status = esp_http_client_get_status_code(handle_);
|
||||
if (http_status != HttpStatus_Ok) {
|
||||
ESP_LOGE(TAG, "Received incorrect http status %d", http_status);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
|
||||
return -1;
|
||||
}
|
||||
int64_t image_length = esp_http_client_get_content_length(handle_);
|
||||
ESP_LOGI(TAG, "image_length = %lld", image_length);
|
||||
esp_http_client_close(handle_);
|
||||
return image_length;
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_transport_tcp.h"
|
||||
#include "esp_ota_ops.h"
|
||||
|
||||
class manual_ota {
|
||||
public:
|
||||
/**
|
||||
* @brief Set the preferred mode
|
||||
*/
|
||||
enum class mode {
|
||||
BATCH, /**< Read data chunk from TCP and pass it to SSL, restore session on reconnection */
|
||||
NORMAL /**< Use standard partial download, continuously passing data from TCP to mbedTLS */
|
||||
} mode_ {mode::BATCH};
|
||||
|
||||
/**
|
||||
* @brief Set the OTA batch size in kB
|
||||
*
|
||||
* This would allocate two big buffers:
|
||||
* - one for reading from TCP socket and
|
||||
* - one for passing to mbedTLS for description
|
||||
*/
|
||||
size_t size_{32};
|
||||
|
||||
/**
|
||||
* @brief Set timeout in seconds
|
||||
*
|
||||
* This is the network timeout, so if less data than the batch size received
|
||||
* the timeout (and no EOF) we should proceed with passing the data to mbedtls
|
||||
*/
|
||||
int timeout_{2};
|
||||
|
||||
/**
|
||||
* @brief Set common name of the server to verify
|
||||
*/
|
||||
const char *common_name_;
|
||||
/**
|
||||
* @brief Wrapper around the http client -- Please set the http config
|
||||
*/
|
||||
class http_client {
|
||||
friend class manual_ota;
|
||||
~http_client();
|
||||
bool init();
|
||||
esp_http_client_handle_t handle_{nullptr};
|
||||
bool handle_redirects();
|
||||
bool set_range(size_t from, size_t to);
|
||||
bool is_data_complete();
|
||||
int64_t get_image_len();
|
||||
public:
|
||||
esp_http_client_config_t config_{}; /**< Configure the http connection parameters */
|
||||
} http_;
|
||||
|
||||
/**
|
||||
* @brief Construct a new manual ota object
|
||||
*/
|
||||
explicit manual_ota() {}
|
||||
|
||||
~manual_ota();
|
||||
|
||||
/**
|
||||
* @brief Start the manual OTA process
|
||||
*
|
||||
* @return true if started successfully
|
||||
*/
|
||||
bool begin();
|
||||
|
||||
/**
|
||||
* @brief Performs one read-write OTA iteration
|
||||
*
|
||||
* @return true if the process is in progress
|
||||
* @return false if the process finished, call end() to get OTA result
|
||||
*/
|
||||
bool perform();
|
||||
|
||||
/**
|
||||
* @brief Finishes an OTA update
|
||||
*
|
||||
* @return true if the OTA update completed successfully
|
||||
*/
|
||||
bool end();
|
||||
|
||||
private:
|
||||
enum class state {
|
||||
UNDEF,
|
||||
INIT,
|
||||
IMAGE_CHECK,
|
||||
START,
|
||||
END,
|
||||
FAIL,
|
||||
};
|
||||
int64_t image_length_;
|
||||
size_t file_length_;
|
||||
size_t max_buffer_size_{size_ * 1024};
|
||||
const esp_partition_t *partition_{nullptr};
|
||||
state status{state::UNDEF};
|
||||
std::vector<char> buffer_{};
|
||||
int reconnect_attempts_;
|
||||
const int max_reconnect_attempts_{3};
|
||||
esp_transport_handle_t ssl_;
|
||||
esp_ota_handle_t update_handle_{0};
|
||||
bool ota_begin;
|
||||
|
||||
bool prepare_reconnect();
|
||||
bool fail();
|
||||
};
|
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <vector>
|
||||
#include "esp_log.h"
|
||||
#include "mbedtls_wrap.hpp"
|
||||
#include "esp_transport_tcp.h"
|
||||
|
||||
#define TAG "batch-tls"
|
||||
|
||||
class TlsTransport: public Tls {
|
||||
public:
|
||||
explicit TlsTransport(esp_transport_handle_t parent):
|
||||
Tls(), transport_(parent), last_timeout(0), read_len(0), offset(0) {}
|
||||
int send(const unsigned char *buf, size_t len) override;
|
||||
int recv(unsigned char *buf, size_t len) override;
|
||||
static bool set_func(esp_transport_handle_t tls_transport);
|
||||
int preread(size_t len, int timeout_ms);
|
||||
bool prepare_buffer(size_t max_size);
|
||||
private:
|
||||
esp_transport_handle_t transport_{};
|
||||
int connect(const char *host, int port, int timeout_ms);
|
||||
void delay() override;
|
||||
|
||||
struct transport {
|
||||
static int connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms);
|
||||
static int read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms);
|
||||
static int write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms);
|
||||
static int close(esp_transport_handle_t t);
|
||||
static int poll_read(esp_transport_handle_t t, int timeout_ms);
|
||||
static int poll_write(esp_transport_handle_t t, int timeout_ms);
|
||||
static int destroy(esp_transport_handle_t t);
|
||||
};
|
||||
int last_timeout;
|
||||
std::vector<char> buf;
|
||||
size_t read_len;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent)
|
||||
{
|
||||
esp_transport_handle_t transport_handle = esp_transport_init();
|
||||
auto *tls_context = new TlsTransport(parent);
|
||||
esp_transport_set_context_data(transport_handle, tls_context);
|
||||
TlsTransport::set_func(transport_handle);
|
||||
return transport_handle;
|
||||
}
|
||||
|
||||
int TlsTransport::send(const unsigned char *buf, size_t len)
|
||||
{
|
||||
int ret = esp_transport_write(transport_, reinterpret_cast<const char *>(buf), len, 0);
|
||||
ESP_LOGD(TAG, "writing(len=%d) ret=%d", len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TlsTransport::recv(unsigned char *buffer, size_t len)
|
||||
{
|
||||
ESP_LOGD(TAG, "recv(len=%d)", len);
|
||||
if (read_len != 0) {
|
||||
|
||||
if (read_len > len) {
|
||||
memcpy((char *)buffer, buf.data() + offset, len);
|
||||
read_len -= len;
|
||||
offset += len;
|
||||
ESP_LOGD(TAG, "read %d from batch read_len = %d", len, read_len);
|
||||
return len;
|
||||
} else {
|
||||
int remaining = len = read_len;
|
||||
if (remaining > 0) {
|
||||
memcpy((char *)buffer, buf.data() + offset, remaining);
|
||||
read_len = 0;
|
||||
offset = 0;
|
||||
return remaining;
|
||||
|
||||
}
|
||||
read_len = 0;
|
||||
offset = 0;
|
||||
return ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN;
|
||||
}
|
||||
}
|
||||
int ret = esp_transport_read(transport_, reinterpret_cast<char *>(buffer), len, last_timeout);
|
||||
|
||||
if (ret == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT) {
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
return ret == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN ? 0 : ret;
|
||||
}
|
||||
|
||||
bool TlsTransport::set_func(esp_transport_handle_t tls_transport)
|
||||
{
|
||||
return esp_transport_set_func(tls_transport, TlsTransport::transport::connect, TlsTransport::transport::read, TlsTransport::transport::write, TlsTransport::transport::close, TlsTransport::transport::poll_read, TlsTransport::transport::poll_write, TlsTransport::transport::destroy) == ESP_OK;
|
||||
}
|
||||
|
||||
int TlsTransport::connect(const char *host, int port, int timeout_ms)
|
||||
{
|
||||
return esp_transport_connect(transport_, host, port, timeout_ms);
|
||||
}
|
||||
|
||||
void TlsTransport::delay()
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
int TlsTransport::transport::connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
tls->init(is_server{false}, do_verify{true});
|
||||
|
||||
ESP_LOGD(TAG, "TLS-connect");
|
||||
auto ret = tls->connect(host, port, timeout_ms);
|
||||
if (ret < 0) {
|
||||
ESP_LOGI(TAG, "Failed to connect to transport");
|
||||
return ret;
|
||||
}
|
||||
if (tls->is_session_loaded()) {
|
||||
tls->set_session();
|
||||
}
|
||||
ESP_LOGI(TAG, "Before handshake");
|
||||
ret = tls->handshake();
|
||||
if (ret < 0) {
|
||||
ESP_LOGI(TAG, "Failed to handshake");
|
||||
return ret;
|
||||
}
|
||||
if (!tls->get_session()) {
|
||||
// we're not able to save session, report an error and continue (next connection will be slower)
|
||||
ESP_LOGW(TAG, "Failed to save session");
|
||||
}
|
||||
ESP_LOGI(TAG, "After handshake");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TlsTransport::transport::read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
ESP_LOGD(TAG, "available=%d tls->read_len=%d", tls->get_available_bytes(), tls->read_len);
|
||||
if (tls->get_available_bytes() <= 0 && tls->read_len == 0) {
|
||||
ESP_LOGD(TAG, "red(len=%d, timeout=%d) tls->read_len=%d", len, timeout_ms, tls->read_len);
|
||||
tls->last_timeout = timeout_ms;
|
||||
int poll = esp_transport_poll_read(t, timeout_ms);
|
||||
if (poll == -1) {
|
||||
return ERR_TCP_TRANSPORT_CONNECTION_FAILED;
|
||||
}
|
||||
if (poll == 0) {
|
||||
return ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = tls->read(reinterpret_cast<unsigned char *>(buffer), len);
|
||||
if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
ret = ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
|
||||
}
|
||||
ESP_LOGD(TAG, "red(len=%d, timeout=%d) ret=%d", len, timeout_ms, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TlsTransport::transport::write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
int poll;
|
||||
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
|
||||
ESP_LOGW(TAG, "Poll timeout or error timeout_ms=%d", timeout_ms);
|
||||
return poll;
|
||||
}
|
||||
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
int ret = tls->write(reinterpret_cast<const unsigned char *>(buffer), len);
|
||||
ESP_LOGD(TAG, "write ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TlsTransport::transport::close(esp_transport_handle_t t)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
int ret = esp_transport_close(tls->transport_);
|
||||
tls->deinit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TlsTransport::transport::poll_read(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_poll_read(tls->transport_, timeout_ms);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::poll_write(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_poll_write(tls->transport_, timeout_ms);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::destroy(esp_transport_handle_t t)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_destroy(tls->transport_);
|
||||
}
|
||||
|
||||
|
||||
esp_transport_handle_t esp_transport_batch_tls_init(esp_transport_handle_t parent, const size_t max_buffer_size)
|
||||
{
|
||||
esp_transport_handle_t ssl = esp_transport_init();
|
||||
auto *tls = new TlsTransport(parent);
|
||||
esp_transport_set_context_data(ssl, tls);
|
||||
TlsTransport::set_func(ssl);
|
||||
tls->prepare_buffer(max_buffer_size);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
bool esp_transport_batch_set_ca_cert(esp_transport_handle_t t, const char *ca_cert, size_t cert_len = 0)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
const_buf cert((const unsigned char *)ca_cert, cert_len ? cert_len : strlen(ca_cert) + 1);
|
||||
return tls->set_ca_cert(cert);
|
||||
}
|
||||
|
||||
bool esp_transport_batch_set_cn(esp_transport_handle_t t, const char *name)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return tls->set_hostname(name);
|
||||
}
|
||||
|
||||
int TlsTransport::preread(size_t len, int timeout_ms)
|
||||
{
|
||||
while (len != read_len) {
|
||||
int l = esp_transport_read(transport_, buf.data() + read_len, len - read_len, timeout_ms);
|
||||
ESP_LOGD(TAG, "need %d read %d already %d", len, l, read_len);
|
||||
if ((l == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN || l == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT ) && read_len > 0) {
|
||||
return read_len;
|
||||
}
|
||||
if (l <= 0) {
|
||||
read_len = 0;
|
||||
return read_len;
|
||||
}
|
||||
read_len += l;
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
||||
bool TlsTransport::prepare_buffer(size_t max_size)
|
||||
{
|
||||
buf.resize(max_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
int esp_transport_batch_tls_pre_read(esp_transport_handle_t t, size_t len, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return tls->preread(len, timeout_ms);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Creates batch transport
|
||||
*
|
||||
* @param parent tcp-transport handle to the parent transport
|
||||
* @param max_buffer_size maximum size of one batch
|
||||
* @return created transport handle
|
||||
*/
|
||||
esp_transport_handle_t esp_transport_batch_tls_init(esp_transport_handle_t parent, const size_t max_buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Performs batch read operation from the underlying transport
|
||||
*
|
||||
* @param t Transport handle
|
||||
* @param len Batch size
|
||||
* @param timeout_ms Timeout in ms
|
||||
* @return true If read from the parent transport completed successfully
|
||||
*/
|
||||
bool esp_transport_batch_tls_pre_read(esp_transport_handle_t t, size_t len, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Set the CA Certificate to verify the server
|
||||
*
|
||||
* @param ca_cert Pointer to the CA Cert data
|
||||
* @param cert_len CA Cert data len (set to 0 if null terminated string, i.e. PEM format)
|
||||
* @return true on success
|
||||
*/
|
||||
bool esp_transport_batch_set_ca_cert(esp_transport_handle_t t, const char *ca_cert, size_t cert_len);
|
||||
|
||||
/**
|
||||
* @brief Set comman name
|
||||
* @param t
|
||||
* @param ca_cert
|
||||
* @param cert_len
|
||||
* @return
|
||||
*/
|
||||
bool esp_transport_batch_set_cn(esp_transport_handle_t t, const char *name);
|
15
components/esp_modem/test/target_ota/http_server.py
Normal file
15
components/esp_modem/test/target_ota/http_server.py
Normal file
@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import ssl
|
||||
from http.server import HTTPServer
|
||||
|
||||
from RangeHTTPServer import RangeRequestHandler
|
||||
|
||||
server_address = ('0.0.0.0', 1234)
|
||||
httpd = HTTPServer(server_address, RangeRequestHandler)
|
||||
httpd.socket = ssl.wrap_socket(httpd.socket,
|
||||
server_side=True,
|
||||
certfile='srv.crt',
|
||||
keyfile='srv.key',
|
||||
ssl_version=ssl.PROTOCOL_TLS)
|
||||
httpd.serve_forever()
|
2
components/esp_modem/test/target_ota/main/CMakeLists.txt
Normal file
2
components/esp_modem/test/target_ota/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS ota_test.cpp
|
||||
INCLUDE_DIRS ".")
|
60
components/esp_modem/test/target_ota/main/Kconfig.projbuild
Normal file
60
components/esp_modem/test/target_ota/main/Kconfig.projbuild
Normal file
@ -0,0 +1,60 @@
|
||||
menu "Test Configuration"
|
||||
|
||||
choice TEST_DEVICE
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default TEST_DEVICE_MODEM_GENERIC
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
|
||||
config TEST_DEVICE_MODEM_GENERIC
|
||||
bool "Common modem device"
|
||||
help
|
||||
Generic device that could be used with most common modems (BG96, SIM76xx, A76xx).
|
||||
|
||||
config TEST_DEVICE_PPPD_SERVER
|
||||
bool "PPPD Server"
|
||||
help
|
||||
Test device is a pppd service in server mode, running on linux.
|
||||
endchoice
|
||||
|
||||
config TEST_MODEM_APN
|
||||
string "Modem APN"
|
||||
depends on TEST_DEVICE_MODEM_GENERIC
|
||||
default "lpwa.vodafone.com"
|
||||
help
|
||||
Set APN (Access Point Name), a logical name to choose data network
|
||||
|
||||
config TEST_USE_VFS_TERM
|
||||
bool "Use VFS terminal"
|
||||
default n
|
||||
help
|
||||
Demonstrate use of VFS as a communication terminal of the DTE.
|
||||
VFS driver implements non-block reads, writes and selects to communicate with esp-modem,
|
||||
but this implementation uses UART resource only.
|
||||
|
||||
config TEST_OTA_URI
|
||||
string "URI of the binary"
|
||||
default "https://192.168.11.1/esp32.bin"
|
||||
help
|
||||
HTTPS address of the update binary.
|
||||
|
||||
config TEST_OTA_CA_CERT
|
||||
string "Server certificate"
|
||||
default "---paste the server side certificate here---"
|
||||
help
|
||||
Insert the CA cert of the server side here. copy the base64 text between -----BEGIN CERTIFICATE-----
|
||||
and -----END CERTIFICATE-----.
|
||||
|
||||
config TEST_OTA_CN
|
||||
string "Server common name"
|
||||
default "192.168.11.1"
|
||||
help
|
||||
Insert the server's common name to be checked within verification of the Server side certificat
|
||||
|
||||
config BROKER_URI
|
||||
string "Broker URL"
|
||||
default "mqtt://test.mosquitto.org"
|
||||
help
|
||||
URL of an mqtt broker which this example connects to.
|
||||
|
||||
endmenu
|
55
components/esp_modem/test/target_ota/main/network_dce.hpp
Normal file
55
components/esp_modem/test/target_ota/main/network_dce.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
class NetModule;
|
||||
|
||||
/**
|
||||
* @brief Custom factory which can build and create a DCE using a custom module
|
||||
*/
|
||||
class NetDCE_Factory: public esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
template <typename T, typename ...Args>
|
||||
static auto create(const esp_modem::dce_factory::config *cfg, Args &&... args) -> std::shared_ptr<esp_modem::DCE_T<T>>
|
||||
{
|
||||
return build_generic_DCE<T, esp_modem::DCE_T<T>, std::shared_ptr<esp_modem::DCE_T<T>>>(cfg, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a null-module, doesn't define any AT commands, just passes everything to pppd
|
||||
*/
|
||||
class NetModule: public esp_modem::ModuleIf {
|
||||
public:
|
||||
explicit NetModule(std::shared_ptr<esp_modem::DTE> dte, const esp_modem_dce_config *cfg):
|
||||
dte(std::move(dte)) {}
|
||||
|
||||
bool setup_data_mode() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_mode(esp_modem::modem_mode mode) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<esp_modem::DTE> dte;
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<esp_modem::DCE_T<NetModule>> create(std::shared_ptr<esp_modem::DTE> dte, esp_netif_t *netif)
|
||||
{
|
||||
const esp_modem::dce_config config = {};
|
||||
return NetDCE_Factory::create<NetModule>(&config, dte, netif);
|
||||
}
|
307
components/esp_modem/test/target_ota/main/ota_test.cpp
Normal file
307
components/esp_modem/test/target_ota/main/ota_test.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "esp_vfs_dev.h" // For optional VFS support
|
||||
#include "vfs_resource/vfs_create.hpp"
|
||||
#include "network_dce.hpp"
|
||||
#include "manual_ota.hpp"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
static const char *TAG = "ota_test";
|
||||
|
||||
|
||||
// Wrap event handlers to destruct correctly on returning from main
|
||||
class StatusHandler {
|
||||
public:
|
||||
static constexpr auto IP_Event = SignalGroup::bit0;
|
||||
static constexpr auto MQTT_Connect = SignalGroup::bit1;
|
||||
static constexpr auto MQTT_Data = SignalGroup::bit2;
|
||||
|
||||
StatusHandler()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_event, this));
|
||||
}
|
||||
|
||||
~StatusHandler()
|
||||
{
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_event);
|
||||
}
|
||||
|
||||
void handle_mqtt(esp_mqtt_client_handle_t client)
|
||||
{
|
||||
mqtt = client;
|
||||
esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, on_event, this);
|
||||
}
|
||||
|
||||
void remove_mqtt()
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
esp_mqtt_client_unregister_event(mqtt, MQTT_EVENT_ANY, on_event);
|
||||
#endif
|
||||
mqtt = nullptr;
|
||||
}
|
||||
|
||||
esp_err_t wait_for(decltype(IP_Event) event, int milliseconds)
|
||||
{
|
||||
return signal.wait_any(event, milliseconds);
|
||||
}
|
||||
|
||||
ip_event_t get_ip_event_type()
|
||||
{
|
||||
return ip_event_type;
|
||||
}
|
||||
|
||||
private:
|
||||
static void on_event(void *arg, esp_event_base_t base, int32_t event, void *data)
|
||||
{
|
||||
auto *handler = static_cast<StatusHandler *>(arg);
|
||||
if (base == IP_EVENT) {
|
||||
handler->ip_event(event, data);
|
||||
} else {
|
||||
handler->mqtt_event(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
void ip_event(int32_t id, void *data)
|
||||
{
|
||||
if (id == IP_EVENT_PPP_GOT_IP) {
|
||||
auto *event = (ip_event_got_ip_t *)data;
|
||||
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
|
||||
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
|
||||
signal.set(IP_Event);
|
||||
} else if (id == IP_EVENT_PPP_LOST_IP) {
|
||||
signal.set(IP_Event);
|
||||
}
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
}
|
||||
|
||||
void mqtt_event(int32_t event, void *data)
|
||||
{
|
||||
if (mqtt && event == MQTT_EVENT_CONNECTED) {
|
||||
signal.set(MQTT_Connect);
|
||||
} else if (mqtt && event == MQTT_EVENT_DATA) {
|
||||
auto event_data = static_cast<esp_mqtt_event_handle_t>(data);
|
||||
ESP_LOGI(TAG, " TOPIC: %.*s", event_data->topic_len, event_data->topic);
|
||||
ESP_LOGI(TAG, " DATA: %.*s", event_data->data_len, event_data->data);
|
||||
signal.set(MQTT_Data);
|
||||
}
|
||||
}
|
||||
|
||||
esp_modem::SignalGroup signal{};
|
||||
esp_mqtt_client_handle_t mqtt{};
|
||||
ip_event_t ip_event_type{};
|
||||
};
|
||||
|
||||
// Wrap MQTT operations to destroy everything on returning from main
|
||||
struct PublishOnce {
|
||||
esp_mqtt_client_handle_t mqtt_;
|
||||
StatusHandler *events_;
|
||||
|
||||
PublishOnce(StatusHandler *events)
|
||||
{
|
||||
esp_mqtt_client_config_t config = { };
|
||||
config.broker.address.uri = CONFIG_BROKER_URI;
|
||||
mqtt_ = esp_mqtt_client_init(&config);
|
||||
events_ = events;
|
||||
events->handle_mqtt(mqtt_);
|
||||
}
|
||||
|
||||
bool Connect()
|
||||
{
|
||||
return esp_mqtt_client_start(mqtt_) == ESP_OK;
|
||||
}
|
||||
|
||||
bool SubscribePublish()
|
||||
{
|
||||
return esp_mqtt_client_subscribe(mqtt_, "/topic/esp-modem", 0) >= 0 &&
|
||||
esp_mqtt_client_publish(mqtt_, "/topic/esp-modem", "Hello modem", 0, 0, 0) >= 0;
|
||||
}
|
||||
|
||||
~PublishOnce()
|
||||
{
|
||||
events_->remove_mqtt();
|
||||
esp_mqtt_client_destroy(mqtt_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// OTA related
|
||||
static constexpr auto OTA_OK = SignalGroup::bit0;
|
||||
static constexpr auto OTA_FAILED = SignalGroup::bit1;
|
||||
|
||||
void ota_task(void *ctx)
|
||||
{
|
||||
static const char *ca_cert_pem = "-----BEGIN CERTIFICATE-----\n" CONFIG_TEST_OTA_CA_CERT "\n-----END CERTIFICATE-----";
|
||||
auto ota_done = static_cast<esp_modem::SignalGroup *>(ctx);
|
||||
manual_ota ota;
|
||||
ota.http_.config_.url = CONFIG_TEST_OTA_URI;
|
||||
ota.http_.config_.cert_pem = ca_cert_pem;
|
||||
ota.size_ = 32;
|
||||
ota.common_name_ = CONFIG_TEST_OTA_CN;
|
||||
#ifndef CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT
|
||||
// will have to use NORMAL mode, before custom transport is supported in IDF
|
||||
ota.mode_ = manual_ota::mode::NORMAL;
|
||||
#endif
|
||||
|
||||
ota.begin();
|
||||
while (true) {
|
||||
if (!ota.perform()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto ret = ota.end();
|
||||
ota_done->set(ret ? OTA_OK : OTA_FAILED);
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
|
||||
// App related
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
esp_log_level_set("ota_test", ESP_LOG_DEBUG);
|
||||
|
||||
// Initialize system functions
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
// Initialize DTE
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
#ifdef CONFIG_TEST_USE_VFS_TERM
|
||||
// To code-cover the vfs layers
|
||||
struct esp_modem_vfs_uart_creator uart_config = ESP_MODEM_VFS_DEFAULT_UART_CONFIG("/dev/uart/1");
|
||||
assert(vfs_create_uart(&uart_config, &dte_config.vfs_config) == true);
|
||||
|
||||
auto dte = create_vfs_dte(&dte_config);
|
||||
esp_vfs_dev_uart_use_driver(uart_config.uart.port_num);
|
||||
#else
|
||||
auto dte = create_uart_dte(&dte_config);
|
||||
#endif // CONFIG_TEST_USE_VFS_TERM
|
||||
assert(dte);
|
||||
dte->set_error_cb([](terminal_error err) {
|
||||
ESP_LOGE(TAG, "DTE reported terminal error: %d", static_cast<int>(err));
|
||||
});
|
||||
|
||||
// Initialize PPP netif
|
||||
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
|
||||
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
|
||||
assert(esp_netif);
|
||||
|
||||
// Initialize DCE
|
||||
#ifdef CONFIG_TEST_DEVICE_PPPD_SERVER
|
||||
auto dce = create(dte, esp_netif);
|
||||
#else
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_TEST_MODEM_APN);
|
||||
auto dce = create_generic_dce(&dce_config, dte, esp_netif);
|
||||
assert(dce);
|
||||
#endif
|
||||
|
||||
StatusHandler handler;
|
||||
|
||||
#ifndef CONFIG_TEST_DEVICE_PPPD_SERVER
|
||||
if (dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) &&
|
||||
dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) &&
|
||||
dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA)) {
|
||||
#else
|
||||
if (dce->set_mode(esp_modem::modem_mode::DATA_MODE)) {
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Modem has correctly entered the desired mode (CMUX/DATA/Manual CMUX)");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to configure multiplexed command mode... exiting");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler.wait_for(StatusHandler::IP_Event, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
|
||||
return;
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_GOT_IP) {
|
||||
ESP_LOGI(TAG, "Got IP address");
|
||||
|
||||
/* When connected to network, subscribe and publish some MQTT data */
|
||||
PublishOnce publish(&handler);
|
||||
if (!publish.Connect()) {
|
||||
ESP_LOGE(TAG, "Failed to connect to mqtt server");
|
||||
return;
|
||||
}
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Connect, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot connect to %s within specified timeout... exiting", CONFIG_BROKER_URI);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
|
||||
if (!publish.SubscribePublish()) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe and publish to mqtt server");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Data, 60000)) {
|
||||
ESP_LOGE(TAG, "Didn't receive published data within specified timeout... exiting");
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Received MQTT data");
|
||||
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGE(TAG, "PPP client has lost connection... exiting");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
esp_modem::SignalGroup ota_done{};
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
|
||||
// now stop the LCP keepalive before performing OTA
|
||||
esp_netif_ppp_config_t cfg;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_get_params(esp_netif, &cfg));
|
||||
cfg.ppp_lcp_echo_disabled = true;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_set_params(esp_netif, &cfg));
|
||||
#endif
|
||||
|
||||
// Run the OTA in a separate task to keep sending commands to the modem at the same time
|
||||
xTaskCreate(ota_task, "ota_task", 8192, &ota_done, 5, nullptr);
|
||||
|
||||
#ifndef CONFIG_TEST_DEVICE_PPPD_SERVER
|
||||
while (true) {
|
||||
std::string str;
|
||||
if (dce->get_imsi(str) == esp_modem::command_result::OK) {
|
||||
ESP_LOGI(TAG, "Modem IMSI number: %s", str.c_str());
|
||||
}
|
||||
if (ota_done.wait_any(OTA_OK | OTA_FAILED, 100)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ota_done.wait_any(OTA_OK | OTA_FAILED, portMAX_DELAY);
|
||||
#endif // CONFIG_TEST_DEVICE_PPPD_SERVER
|
||||
|
||||
#ifndef CONFIG_TEST_DEVICE_PPPD_SERVER
|
||||
if (dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT)) {
|
||||
#else
|
||||
if (dce->set_mode(esp_modem::modem_mode::COMMAND_MODE)) {
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Modem CMUX/DATA mode exit");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to configure desired mode... exiting");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ota_done.is_any(OTA_OK)) {
|
||||
ESP_LOGI(TAG, "Prepare to restart system!");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
}
|
2
components/esp_modem/test/target_ota/sdkconfig.ci.1
Normal file
2
components/esp_modem/test/target_ota/sdkconfig.ci.1
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_TEST_DEVICE_PPPD_SERVER=y
|
||||
CONFIG_ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD=n
|
4
components/esp_modem/test/target_ota/sdkconfig.ci.2
Normal file
4
components/esp_modem/test/target_ota/sdkconfig.ci.2
Normal file
@ -0,0 +1,4 @@
|
||||
CONFIG_TEST_DEVICE_MODEM_GENERIC=y
|
||||
CONFIG_TEST_OTA_URI="https://raw.githubusercontent.com/espressif/esp-protocols/master/components/esp_modem/test/target_ota/bin/blink.bin"
|
||||
CONFIG_TEST_OTA_CA_CERT="MIIEvjCCA6agAwIBAgIQBtjZBNVYQ0b2ii+nVCJ+xDANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaME8xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBSU0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6aqXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddng9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuWraKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGBAfr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21reacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBgjCCAX4wEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUt2ui6qiqhIx56rTaD5iyxZV2ufQwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMD0GA1UdIAQ2MDQwCwYJYIZIAYb9bAIBMAcGBWeBDAEBMAgGBmeBDAECATAIBgZngQwBAgIwCAYGZ4EMAQIDMA0GCSqGSIb3DQEBCwUAA4IBAQCAMs5eC91uWg0Kr+HWhMvAjvqFcO3aXbMM9yt1QP6FCvrzMXi3cEsaiVi6gL3zax3pfs8LulicWdSQ0/1s/dCYbbdxglvPbQtaCdB73sRD2Cqk3p5BJl+7j5nL3a7hqG+fh/50tx8bIKuxT8b1Z11dmzzp/2n3YWzW2fP9NsarA4h20ksudYbj/NhVfSbCEXffPgK2fPOre3qGNm+499iTcc+G33Mw+nur7SpZyEKEOxEXGlLzyQ4UfaJbcme6ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697EA7sKPPcw7+uvTPyLNhBzPvOk"
|
||||
CONFIG_TEST_OTA_CN="github.com"
|
4
components/esp_modem/test/target_ota/sdkconfig.ci.3
Normal file
4
components/esp_modem/test/target_ota/sdkconfig.ci.3
Normal file
@ -0,0 +1,4 @@
|
||||
CONFIG_TEST_DEVICE_PPPD_SERVER=y
|
||||
CONFIG_ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD=y
|
||||
CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED=y
|
||||
CONFIG_TEST_USE_VFS_TERM=y
|
12
components/esp_modem/test/target_ota/sdkconfig.defaults
Normal file
12
components/esp_modem/test/target_ota/sdkconfig.defaults
Normal file
@ -0,0 +1,12 @@
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_TWO_OTA=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
# This is not supported in IDF yet
|
||||
# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT=y
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_ENABLE_IPV6=n
|
||||
CONFIG_LWIP_ENABLE_LCP_ECHO=y
|
||||
CONFIG_LWIP_LCP_ECHOINTERVAL=1
|
||||
CONFIG_LWIP_LCP_MAXECHOFAILS=2
|
@ -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.2.0
|
||||
version: 1.2.1
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,12 @@
|
||||
# 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
|
||||
|
@ -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:
|
||||
|
@ -8,7 +8,6 @@ set(EXTRA_COMPONENT_DIRS
|
||||
"${common_component_dir}/linux_compat"
|
||||
"${common_component_dir}/linux_compat/freertos")
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
@ -2,8 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(websocket_example)
|
||||
|
@ -4,3 +4,5 @@ dependencies:
|
||||
espressif/esp_websocket_client:
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: "1.2.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:
|
||||
|
@ -5882,6 +5882,7 @@ static mdns_ip_addr_t *_copy_delegated_host_address_list(char *hostname)
|
||||
if (strcasecmp(host->hostname, hostname) == 0) {
|
||||
return copy_address_list(host->address_list);
|
||||
}
|
||||
host = host->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -26,10 +26,9 @@ CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversi
|
||||
-I$(COMPONENTS_DIR)/heap/include \
|
||||
-I$(COMPONENTS_DIR)/log/include \
|
||||
-I$(COMPONENTS_DIR)/lwip/lwip/src/include \
|
||||
-I$(COMPONENTS_DIR)/linux/include \
|
||||
-I$(COMPONENTS_DIR)/lwip/port/esp32/include \
|
||||
-I$(COMPONENTS_DIR)/lwip/lwip/src/include/lwip/apps \
|
||||
-I$(COMPONENTS_DIR)/newlib/platform_include \
|
||||
-I$(COMPONENTS_DIR)/soc/include \
|
||||
-I$(COMPONENTS_DIR)/soc/include \
|
||||
-I$(COMPONENTS_DIR)/soc/esp32/include \
|
||||
-I$(COMPONENTS_DIR)/soc/src/esp32/include \
|
||||
|
@ -24,5 +24,17 @@ menu "Example Configuration"
|
||||
help
|
||||
If enabled, a portion of MAC address is added to the hostname, this is used
|
||||
for evaluation of tests in CI
|
||||
config MDNS_ADD_MAC_TO_HOSTNAME
|
||||
bool "Add mac suffix to hostname"
|
||||
default n
|
||||
help
|
||||
If enabled, a portion of MAC address is added to the hostname, this is used
|
||||
for evaluation of tests in CI
|
||||
config MDNS_PUBLISH_DELEGATE_HOST
|
||||
bool "Publish a delegated host"
|
||||
help
|
||||
Enable publishing a delegated host other than ESP32.
|
||||
The example will also add a mock service for this host.
|
||||
|
||||
|
||||
endmenu
|
||||
|
@ -1,5 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
@ -12,4 +11,3 @@ CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_MDNS_BUTTON_GPIO=32
|
||||
|
@ -81,16 +81,68 @@ Common use cases of the esp-modem are also listed as the examples:
|
||||
- ``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
|
||||
-------------
|
||||
|
||||
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.
|
||||
|
||||
DTE
|
||||
~~~
|
||||
|
Reference in New Issue
Block a user