mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-02 21:30:57 +02:00
Compare commits
48 Commits
console_cm
...
console_cm
Author | SHA1 | Date | |
---|---|---|---|
de8ec67a88 | |||
8dac30781c | |||
ba3f06f942 | |||
a10f0008fb | |||
93d140875f | |||
585e4b30b2 | |||
47736a2556 | |||
1393764dc5 | |||
0998f3dd4f | |||
577de67c0e | |||
ae38110d84 | |||
5ab699d6f4 | |||
976e98d6ff | |||
18b2ae103a | |||
52a34c21d0 | |||
9df86641f3 | |||
75af01670e | |||
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 | |||
bf99f287bc | |||
76a4a9f97a | |||
8bab1420d3 |
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: ifconfig-basic, 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 ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
32
.github/workflows/console_cmd_iperf__build.yml
vendored
Normal file
32
.github/workflows/console_cmd_iperf__build.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: "console_cmd_iperf: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_console_cmd_iperf:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: iperf-basic, path: "components/console_cmd_iperf/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 ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
@ -15,7 +15,7 @@ jobs:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "components/console_cmd_ping/examples" }]
|
||||
test: [ { app: ping-basic, path: "components/console_cmd_ping/examples" }]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -29,4 +29,4 @@ jobs:
|
||||
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
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --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: wifi-basic, 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 ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
@ -15,7 +15,7 @@ jobs:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "components/console_simple_init/examples" }]
|
||||
test: [ { app: console_basic, path: "components/console_simple_init/examples" }]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -29,4 +29,4 @@ jobs:
|
||||
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
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --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", "target_iperf"]
|
||||
|
||||
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'
|
||||
|
4
.github/workflows/mqtt_cxx__build.yml
vendored
4
.github/workflows/mqtt_cxx__build.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "components/esp_mqtt_cxx/examples" }]
|
||||
test: [ { app: mqtt-basic, path: "components/esp_mqtt_cxx/examples" }]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -29,4 +29,4 @@ jobs:
|
||||
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
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
||||
|
5
.github/workflows/publish-docs-component.yml
vendored
5
.github/workflows/publish-docs-component.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip
|
||||
python -m pip install breathe recommonmark esp-docs==1.4.1
|
||||
python -m pip install -r docs/requirements.txt
|
||||
for comp in `ls components`; do
|
||||
if [[ -d $GITHUB_WORKSPACE/docs/${comp} ]]; then
|
||||
cd $GITHUB_WORKSPACE/docs/${comp}
|
||||
@ -95,5 +95,8 @@ jobs:
|
||||
components/mdns;
|
||||
components/console_simple_init;
|
||||
components/console_cmd_ping;
|
||||
components/console_cmd_ifconfig;
|
||||
components/console_cmd_wifi;
|
||||
components/console_cmd_iperf;
|
||||
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)
|
||||
|
@ -10,6 +10,8 @@ import sys
|
||||
|
||||
from idf_build_apps import build_apps, find_apps, setup_logging
|
||||
from idf_build_apps.constants import SUPPORTED_TARGETS
|
||||
from packaging import version
|
||||
from pkg_resources import get_distribution
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
@ -17,6 +19,12 @@ if __name__ == '__main__':
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
parser.add_argument('paths', nargs='+', help='Paths to the apps to build.')
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--verbose',
|
||||
action='count',
|
||||
help='Increase the LOGGER level of the script. Can be specified multiple times.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-t',
|
||||
'--target',
|
||||
@ -28,6 +36,8 @@ if __name__ == '__main__':
|
||||
parser.add_argument('-d', '--delete', action='store_true', help='Delete build artifacts')
|
||||
parser.add_argument('-c', '--recursive', action='store_true', help='Build recursively')
|
||||
parser.add_argument('-l', '--linux', action='store_true', help='Include linux build (dont check warnings)')
|
||||
parser.add_argument('--preserve-all', action='store_true', help='Preserve the binaries for all apps when specified.')
|
||||
parser.add_argument('--pytest-apps', action='store_true', help='Only build apps required by pytest scripts.')
|
||||
args = parser.parse_args()
|
||||
|
||||
IDF_PATH = os.environ['IDF_PATH']
|
||||
@ -41,20 +51,36 @@ if __name__ == '__main__':
|
||||
SUPPORTED_TARGETS.append('linux')
|
||||
ignore_warning = 'warning: ' # Ignore all common warnings on linux builds
|
||||
setup_logging(2)
|
||||
apps = find_apps(
|
||||
args.paths,
|
||||
recursive=args.recursive,
|
||||
target=args.target,
|
||||
build_dir='build_@t_@w',
|
||||
config_rules_str=args.rules,
|
||||
build_log_path='build_log.txt',
|
||||
size_json_path='size.json' if not args.linux else None,
|
||||
check_warnings=True,
|
||||
preserve=not args.delete,
|
||||
manifest_files=args.manifests,
|
||||
default_build_targets=SUPPORTED_TARGETS,
|
||||
manifest_rootpath='.',
|
||||
)
|
||||
if version.parse(get_distribution('idf_build_apps').version) >= version.parse('2.0.0'):
|
||||
apps = find_apps(
|
||||
args.paths,
|
||||
recursive=args.recursive,
|
||||
target=args.target,
|
||||
build_dir='build_@t_@w',
|
||||
config_rules_str=args.rules,
|
||||
build_log_filename='build_log.txt',
|
||||
size_json_filename='size.json' if not args.linux else None,
|
||||
check_warnings=True,
|
||||
preserve=not args.delete,
|
||||
manifest_files=args.manifests,
|
||||
default_build_targets=SUPPORTED_TARGETS,
|
||||
manifest_rootpath='.',
|
||||
)
|
||||
else:
|
||||
apps = find_apps(
|
||||
args.paths,
|
||||
recursive=args.recursive,
|
||||
target=args.target,
|
||||
build_dir='build_@t_@w',
|
||||
config_rules_str=args.rules,
|
||||
build_log_path='build_log.txt',
|
||||
size_json_path='size.json' if not args.linux else None,
|
||||
check_warnings=True,
|
||||
preserve=not args.delete,
|
||||
manifest_files=args.manifests,
|
||||
default_build_targets=SUPPORTED_TARGETS,
|
||||
manifest_rootpath='.',
|
||||
)
|
||||
|
||||
for app in apps:
|
||||
print(app)
|
||||
|
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'
|
8
components/console_cmd_iperf/.cz.yaml
Normal file
8
components/console_cmd_iperf/.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_iperf
|
||||
tag_format: console_cmd_iperf-v$version
|
||||
version: 1.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
7
components/console_cmd_iperf/CHANGELOG.md
Normal file
7
components/console_cmd_iperf/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_iperf-v1.0.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Added component with iperf command ([93d14087](https://github.com/espressif/esp-protocols/commit/93d14087))
|
4
components/console_cmd_iperf/CMakeLists.txt
Normal file
4
components/console_cmd_iperf/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "console_iperf.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_netif console
|
||||
WHOLE_ARCHIVE)
|
201
components/console_cmd_iperf/LICENSE
Normal file
201
components/console_cmd_iperf/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.
|
52
components/console_cmd_iperf/README.md
Normal file
52
components/console_cmd_iperf/README.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Console command iperf
|
||||
The component provides a console where the 'iperf' command can be executed 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_iperf.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());
|
||||
```
|
||||
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 iperf command skip calling console_cmd_all_register()
|
||||
ESP_ERROR_CHECK(console_cmd_iperf_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:
|
||||
|
||||
### iperf:
|
||||
```
|
||||
iperf [-suVa] [-c <ip>] [-p <port>] [-l <length>] [-i <interval>] [-t <time>] [-b <bandwidth>]
|
||||
Command to measure network performance, through TCP or UDP connections.
|
||||
-c, --client=<ip> run in client mode, connecting to <host>
|
||||
-s, --server run in server mode
|
||||
-u, --udp use UDP rather than TCP
|
||||
-V, --ipv6_domain use IPV6 address rather than IPV4
|
||||
-p, --port=<port> server port to listen on/connect to
|
||||
-l, --len=<length> set read/write buffer size
|
||||
-i, --interval=<interval> seconds between periodic bandwidth reports
|
||||
-t, --time=<time> time in seconds to transmit for (default 10 secs)
|
||||
-b, --bandwidth=<bandwidth> bandwidth to send at in Mbits/sec
|
||||
-a, --abort abort running iperf
|
||||
```
|
201
components/console_cmd_iperf/console_iperf.c
Normal file
201
components/console_cmd_iperf/console_iperf.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "sys/socket.h" // for INADDR_ANY
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_console.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "iperf.h"
|
||||
#include "console_iperf.h"
|
||||
|
||||
/* "iperf" command */
|
||||
|
||||
/**
|
||||
* 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_iperf",
|
||||
.plugin_regd_fn = &console_cmd_iperf_register
|
||||
};
|
||||
|
||||
static const char *TAG = "console_iperf";
|
||||
|
||||
static struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_lit *server;
|
||||
struct arg_lit *udp;
|
||||
struct arg_lit *version;
|
||||
struct arg_int *port;
|
||||
struct arg_int *length;
|
||||
struct arg_int *interval;
|
||||
struct arg_int *time;
|
||||
struct arg_int *bw_limit;
|
||||
struct arg_lit *abort;
|
||||
struct arg_end *end;
|
||||
} iperf_args;
|
||||
|
||||
static int do_cmd_iperf(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
|
||||
/* ethernet iperf only support IPV4 address */
|
||||
iperf_cfg_t cfg = {.type = IPERF_IP_TYPE_IPV4};
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, iperf_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf -a */
|
||||
if (iperf_args.abort->count != 0) {
|
||||
iperf_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
|
||||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
|
||||
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf -s */
|
||||
if (iperf_args.ip->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_SERVER;
|
||||
}
|
||||
/* iperf -c SERVER_ADDRESS */
|
||||
else {
|
||||
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
|
||||
cfg.flag |= IPERF_FLAG_CLIENT;
|
||||
}
|
||||
|
||||
if (iperf_args.length->count == 0) {
|
||||
cfg.len_send_buf = 0;
|
||||
} else {
|
||||
cfg.len_send_buf = iperf_args.length->ival[0];
|
||||
}
|
||||
|
||||
cfg.source_ip4 = INADDR_ANY;
|
||||
|
||||
/* iperf -u */
|
||||
if (iperf_args.udp->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_TCP;
|
||||
} else {
|
||||
cfg.flag |= IPERF_FLAG_UDP;
|
||||
}
|
||||
|
||||
/* iperf -p */
|
||||
if (iperf_args.port->count == 0) {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
if (cfg.flag & IPERF_FLAG_SERVER) {
|
||||
cfg.sport = iperf_args.port->ival[0];
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = iperf_args.port->ival[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -i */
|
||||
if (iperf_args.interval->count == 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
} else {
|
||||
cfg.interval = iperf_args.interval->ival[0];
|
||||
if (cfg.interval <= 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -t */
|
||||
if (iperf_args.time->count == 0) {
|
||||
cfg.time = IPERF_DEFAULT_TIME;
|
||||
} else {
|
||||
cfg.time = iperf_args.time->ival[0];
|
||||
if (cfg.time <= cfg.interval) {
|
||||
cfg.time = cfg.interval;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -b */
|
||||
if (iperf_args.bw_limit->count == 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
} else {
|
||||
cfg.bw_lim = iperf_args.bw_limit->ival[0];
|
||||
if (cfg.bw_lim <= 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n",
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
(uint16_t) cfg.source_ip4 & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 24) & 0xFF,
|
||||
cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
cfg.interval, cfg.time);
|
||||
|
||||
iperf_start(&cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers the iperf command.
|
||||
*
|
||||
* @return
|
||||
* - esp_err_t
|
||||
*/
|
||||
esp_err_t console_cmd_iperf_register(void)
|
||||
{
|
||||
iperf_args.ip = arg_str0("c", "client", "<ip>", "run in client mode, connecting to <host>");
|
||||
iperf_args.server = arg_lit0("s", "server", "run in server mode");
|
||||
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
|
||||
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
|
||||
iperf_args.port = arg_int0("p", "port", "<port>", "server port to listen on/connect to");
|
||||
iperf_args.length = arg_int0("l", "len", "<length>", "set read/write buffer size");
|
||||
iperf_args.interval = arg_int0("i", "interval", "<interval>", "seconds between periodic bandwidth reports");
|
||||
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
|
||||
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
|
||||
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
|
||||
iperf_args.end = arg_end(1);
|
||||
|
||||
esp_err_t ret;
|
||||
const esp_console_cmd_t command = {
|
||||
.command = "iperf",
|
||||
.help = "Command to measure network performance, through TCP or UDP connections.",
|
||||
.hint = NULL,
|
||||
.func = &do_cmd_iperf,
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
|
||||
ret = esp_console_cmd_register(&command);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Unable to register iperf");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
25
components/console_cmd_iperf/console_iperf.h
Normal file
25
components/console_cmd_iperf/console_iperf.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 iperf command.
|
||||
*
|
||||
* @return
|
||||
* - esp_err_t
|
||||
*/
|
||||
esp_err_t console_cmd_iperf_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(iperf-basic)
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "iperf-basic.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
console_cmd_iperf:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "esp_netif.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "console_iperf.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
// Initialize console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
|
||||
ESP_ERROR_CHECK(console_cmd_all_register());
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
def test_examples_iperf_command(dut):
|
||||
dut.expect('esp>', timeout=30)
|
||||
dut.write('help iperf')
|
||||
dut.expect('esp>', timeout=5)
|
||||
pass
|
13
components/console_cmd_iperf/idf_component.yml
Normal file
13
components/console_cmd_iperf/idf_component.yml
Normal file
@ -0,0 +1,13 @@
|
||||
version: 1.0.0
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_iperf
|
||||
description: The component provides a console where the 'iperf' command can be executed.
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.0'
|
||||
espressif/console_simple_init:
|
||||
version: '>=1.1.0'
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
||||
iperf:
|
||||
version: "*"
|
||||
path: '${IDF_PATH}/examples/common_components/iperf'
|
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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -17,26 +17,20 @@
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
|
||||
/**
|
||||
* @brief Definition of a custom modem which inherits from the GenericModule, uses all its methods
|
||||
* and could override any of them. Here, for demonstration purposes only, we redefine just `get_module_name()`
|
||||
* @brief Definition of a custom DCE uses GenericModule and all its methods
|
||||
* but could override command processing. Here, for demonstration purposes only,
|
||||
* we "inject" URC handler to the actual command processing.
|
||||
* This is possible since we inherit from `CommandableIf` and redefine `command()` method.
|
||||
* Then we're able to use declare all common methods from the command library
|
||||
* to be processed using "our" `command()` method (with custom URC handler).
|
||||
*/
|
||||
class MyShinyModem: public esp_modem::GenericModule {
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
esp_modem::command_result get_module_name(std::string &name) override
|
||||
{
|
||||
name = "Custom Shiny Module";
|
||||
return esp_modem::command_result::OK;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Shiny {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
class DCE : public esp_modem::DCE_T<MyShinyModem>, public CommandableIf {
|
||||
class DCE : public esp_modem::DCE_T<GenericModule>, public CommandableIf {
|
||||
public:
|
||||
using DCE_T<MyShinyModem>::DCE_T;
|
||||
using DCE_T<GenericModule>::DCE_T;
|
||||
|
||||
command_result
|
||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
|
||||
@ -97,7 +91,7 @@ public:
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<MyShinyModem, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
return build_generic_DCE<GenericModule, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
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();
|
||||
|
@ -116,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_);
|
||||
|
@ -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
|
||||
|
@ -54,7 +54,8 @@ command_result power_down_sim76xx(CommandableIf *t);
|
||||
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_data_mode_alt(CommandableIf *t);
|
||||
command_result set_pdp_context(CommandableIf *t, PdpContext &pdp, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
@ -144,6 +144,8 @@ class SIM7070: public GenericModule {
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
command_result power_down() override;
|
||||
command_result set_data_mode() override;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -162,7 +164,6 @@ class SIM800: public GenericModule {
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
command_result power_down() override;
|
||||
command_result set_data_mode() override;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -170,6 +171,8 @@ public:
|
||||
*/
|
||||
class BG96: public GenericModule {
|
||||
using GenericModule::GenericModule;
|
||||
public:
|
||||
command_result set_pdp_context(PdpContext &pdp) override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -44,6 +44,18 @@ ESP_MODEM_DECLARE_DCE_COMMAND(store_profile, command_result, 0) \
|
||||
*/\
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
|
||||
\
|
||||
/**
|
||||
* @brief Execute the supplied AT command in raw mode (doesn't append '\r' to command, returns everything)
|
||||
* @param[in] cmd String command that's send to DTE
|
||||
* @param[out] out Raw output from DTE
|
||||
* @param[in] pass Pattern in response for the API to return OK
|
||||
* @param[in] fail Pattern in response for the API to return FAIL
|
||||
* @param[in] cmd String command that's send to DTE
|
||||
* @param[in] timeout AT command timeout in milliseconds
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/\
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(at_raw, command_result, 5, STRING_IN(p1, cmd), STRING_OUT(p2, out), STRING_IN(p3, pass), STRING_IN(p4, fail), INT_IN(p5, timeout)) \
|
||||
\
|
||||
/**
|
||||
* @brief Execute the supplied AT command
|
||||
* @param[in] cmd AT command
|
||||
|
@ -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-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -17,7 +17,7 @@
|
||||
#include "esp_private/c_api_wrapper.hpp"
|
||||
|
||||
#ifndef ESP_MODEM_C_API_STR_MAX
|
||||
#define ESP_MODEM_C_API_STR_MAX 64
|
||||
#define ESP_MODEM_C_API_STR_MAX 128
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
@ -206,6 +206,20 @@ extern "C" esp_err_t esp_modem_get_imsi(esp_modem_dce_t *dce_wrap, char *p_imsi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_at_raw(esp_modem_dce_t *dce_wrap, const char *cmd, char *p_out, const char *pass, const char *fail, int timeout)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
std::string out;
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->at_raw(cmd, out, pass, fail, timeout));
|
||||
if ((p_out != NULL) && (!out.empty())) {
|
||||
strlcpy(p_out, out.c_str(), ESP_MODEM_C_API_STR_MAX);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_flow_control(esp_modem_dce_t *dce_wrap, int dce_flow, int dte_flow)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -261,24 +261,29 @@ 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)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
|
||||
return generic_command(t, "ATD*99#\r", "CONNECT", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result set_data_mode_sim8xx(CommandableIf *t)
|
||||
command_result set_data_mode_alt(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return generic_command(t, "ATD*99#\r", "CONNECT", "ERROR", 5000);
|
||||
return generic_command(t, "ATD*99##\r", "CONNECT", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result resume_data_mode(CommandableIf *t)
|
||||
@ -389,6 +394,22 @@ command_result at(CommandableIf *t, const std::string &cmd, std::string &out, in
|
||||
return generic_get_string(t, at_command, out, timeout);
|
||||
}
|
||||
|
||||
command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out, const std::string &pass, const std::string &fail, int timeout = 500)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return t->command(cmd, [&](uint8_t *data, size_t len) {
|
||||
out.assign(reinterpret_cast<char *>(data), len);
|
||||
|
||||
if (out.find(pass) != std::string::npos) {
|
||||
return command_result::OK;
|
||||
} else if (out.find(fail) != std::string::npos) {
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
return command_result::TIMEOUT;
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
|
@ -347,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-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -67,6 +67,11 @@ command_result SIM7070::power_down()
|
||||
return dce_commands::power_down_sim70xx(dte.get());
|
||||
}
|
||||
|
||||
command_result SIM7070::set_data_mode()
|
||||
{
|
||||
return dce_commands::set_data_mode_alt(dte.get());
|
||||
}
|
||||
|
||||
command_result SIM7000::power_down()
|
||||
{
|
||||
return dce_commands::power_down_sim70xx(dte.get());
|
||||
@ -77,9 +82,9 @@ command_result SIM800::power_down()
|
||||
return dce_commands::power_down_sim8xx(dte.get());
|
||||
}
|
||||
|
||||
command_result SIM800::set_data_mode()
|
||||
command_result BG96::set_pdp_context(esp_modem::PdpContext &pdp)
|
||||
{
|
||||
return dce_commands::set_data_mode_sim8xx(dte.get());
|
||||
return dce_commands::set_pdp_context(dte.get(), pdp, 300);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -36,7 +36,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
|
||||
} else if (command == "ATO\r") {
|
||||
response = "ERROR\r\n";
|
||||
} else if (command.find("ATD") != std::string::npos) {
|
||||
response = "CONNECT\r\n";
|
||||
response = "CONNECT\n";
|
||||
} else if (command.find("AT+CSQ\r") != std::string::npos) {
|
||||
response = "+CSQ: 123,456\n\r\nOK\r\n";
|
||||
} else if (command.find("AT+CGMM\r") != std::string::npos) {
|
||||
@ -74,6 +74,9 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
|
||||
}
|
||||
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
|
||||
// turn the request into a reply -> implements CMUX loopback
|
||||
// Note: This simple CMUX responder only updates CMUX headers and replaces payload.
|
||||
// It means that all responses (that we test) must be shorter or equal to the requests
|
||||
// For example ATD (dial command): sizeof("ATD*99#") >= sizeof("CONNECT");
|
||||
if (data[2] == 0x3f || data[2] == 0x53) { // SABM command
|
||||
data[2] = 0x73;
|
||||
} else if (data[2] == 0xef) { // Generic request
|
||||
|
@ -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]")
|
||||
|
10
components/esp_modem/test/target_iperf/CMakeLists.txt
Normal file
10
components/esp_modem/test/target_iperf/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# 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.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components
|
||||
$ENV{IDF_PATH}/examples/common_components/iperf
|
||||
"../..")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pppd_test)
|
48
components/esp_modem/test/target_iperf/README.md
Normal file
48
components/esp_modem/test/target_iperf/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Target test for measuring TCP/UDP network performance
|
||||
|
||||
## Overview
|
||||
|
||||
The aim of this test is to run `iperf` application to measure network throughput of the esp_modem library.
|
||||
|
||||
### Configure PPP server
|
||||
|
||||
This test uses a network DCE device, which only needs a PPP server. To run the PPP server, use this command:
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Running using pytest
|
||||
|
||||
For checking the performance, you only need to execute the pytest, which will measure the network throughput automatically and report the resultant values. For running the pytest, you need to:
|
||||
* install IDF pytest packages by running: `./install.sh --enable-pytest`
|
||||
* add IDF internal packages to python path: `export PYTHONPATH=${DIF_PATH}/tools/ci/python_packages/`
|
||||
* run the pytest as **superuser**
|
||||
|
||||
It's useful to note that when running the test multiple times, you can use `pytest --skip-autoflash y` so the pytest wouldn't have to always reprogram the DUT.
|
||||
|
||||
### Performance summary
|
||||
|
||||
Here's an example of the resultant summary logged by the pytest
|
||||
```
|
||||
2023-11-29 18:28:25 INFO [Performance][tcp_tx_throughput]: 0.75 Mbps
|
||||
2023-11-29 18:28:25 INFO [Performance][tcp_rx_throughput]: 0.70 Mbps
|
||||
2023-11-29 18:28:25 INFO [Performance][udp_tx_throughput]: 0.73 Mbps
|
||||
2023-11-29 18:28:25 INFO [Performance][udp_rx_throughput]: 0.70 Mbps
|
||||
```
|
||||
|
||||
### Running the iperf manually
|
||||
|
||||
Execute `idf.py flash monitor` in one terminal and after connecting to the PPP server (after getting an IP address), you can use standard `iperf` commands.
|
||||
In another terminal, you need to execute the iperf counterpart.
|
||||
For example running for checking UDP performance, and running server on ESP32, please run:
|
||||
* iperf -u -s -i 3 (in ESP32 terminal)
|
||||
* iperf -u -c SERVER_IP -t 60 -i 3 (on the host side)
|
||||
|
||||
Note that command `pppd info` will print actual IP addresses:
|
||||
```
|
||||
iperf> pppd info
|
||||
ppp:
|
||||
IP: 192.168.11.2
|
||||
MASK: 255.255.255.255
|
||||
GW: 192.168.11.3
|
||||
```
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "NetworkDCE.cpp"
|
||||
"cmd_pppclient.c"
|
||||
"pppd_iperf_main.c")
|
96
components/esp_modem/test/target_iperf/main/NetworkDCE.cpp
Normal file
96
components/esp_modem/test/target_iperf/main/NetworkDCE.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
using namespace esp_modem;
|
||||
using namespace esp_modem::dce_factory;
|
||||
|
||||
class NetModule;
|
||||
typedef DCE_T<NetModule> NetDCE;
|
||||
|
||||
/**
|
||||
* @brief Custom factory which can build and create a DCE using a custom module
|
||||
*/
|
||||
class NetDCE_Factory: public Factory {
|
||||
public:
|
||||
template <typename T, typename ...Args>
|
||||
static DCE_T<T> *create(const config *cfg, Args &&... args)
|
||||
{
|
||||
return build_generic_DCE<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 ModuleIf {
|
||||
public:
|
||||
explicit NetModule(std::shared_ptr<DTE> dte, const esp_modem_dce_config *cfg):
|
||||
dte(std::move(dte)) {}
|
||||
|
||||
bool setup_data_mode() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_mode(modem_mode mode) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t init(esp_netif_t *netif)
|
||||
{
|
||||
// configure
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
dte_config.uart_config.baud_rate = 921600; // check also 460800
|
||||
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("");
|
||||
|
||||
// create DTE and minimal network DCE
|
||||
auto uart_dte = create_uart_dte(&dte_config);
|
||||
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif);
|
||||
return dce == nullptr ? ESP_FAIL : ESP_OK;
|
||||
}
|
||||
|
||||
static void deinit()
|
||||
{
|
||||
delete dce;
|
||||
}
|
||||
static void start()
|
||||
{
|
||||
dce->set_data();
|
||||
}
|
||||
static void stop()
|
||||
{
|
||||
dce->exit_data();
|
||||
}
|
||||
|
||||
private:
|
||||
static NetDCE *dce;
|
||||
std::shared_ptr<DTE> dte;
|
||||
};
|
||||
|
||||
NetDCE *NetModule::dce = nullptr;
|
||||
|
||||
extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
|
||||
{
|
||||
return NetModule::init(netif);
|
||||
}
|
||||
|
||||
extern "C" esp_err_t modem_start_network()
|
||||
{
|
||||
NetModule::start();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
extern "C" void modem_stop_network()
|
||||
{
|
||||
NetModule::stop();
|
||||
}
|
289
components/esp_modem/test/target_iperf/main/cmd_pppclient.c
Normal file
289
components/esp_modem/test/target_iperf/main/cmd_pppclient.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "sys/socket.h" // for INADDR_ANY
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_console.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "iperf.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "network_dce.h"
|
||||
|
||||
static const char *TAG = "pppd_test";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
static esp_netif_t *s_ppp_netif;
|
||||
static const int GOTIP_BIT = BIT0;
|
||||
|
||||
static void on_modem_event(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_base == IP_EVENT) {
|
||||
ESP_LOGD(TAG, "IP event! %" PRIu32, event_id);
|
||||
if (event_id == IP_EVENT_PPP_GOT_IP) {
|
||||
esp_netif_dns_info_t dns_info;
|
||||
|
||||
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
esp_netif_t *netif = event->esp_netif;
|
||||
|
||||
ESP_LOGI(TAG, "Modem Connect to PPP Server");
|
||||
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
|
||||
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));
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
|
||||
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info);
|
||||
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
|
||||
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
|
||||
|
||||
ESP_LOGI(TAG, "GOT ip event!!!");
|
||||
xEventGroupSetBits(event_group, GOTIP_BIT);
|
||||
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
|
||||
} else if (event_id == IP_EVENT_GOT_IP6) {
|
||||
ESP_LOGI(TAG, "GOT IPv6 event!");
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
|
||||
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "PPP state changed event %" PRId32, event_id);
|
||||
if (event_id == NETIF_PPP_ERRORUSER) {
|
||||
/* User interrupted event from esp-netif */
|
||||
esp_netif_t *netif = (esp_netif_t *)event_data;
|
||||
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
|
||||
xEventGroupSetBits(event_group, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* "pppd" command */
|
||||
static struct {
|
||||
struct arg_str *control;
|
||||
struct arg_end *end;
|
||||
} pppd_control_args;
|
||||
|
||||
static int pppd_cmd_control(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&pppd_control_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, pppd_control_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strncmp(pppd_control_args.control->sval[0], "info", 4)) {
|
||||
esp_netif_ip_info_t ip;
|
||||
printf("%s:\n", esp_netif_get_desc(s_ppp_netif));
|
||||
esp_netif_get_ip_info(s_ppp_netif, &ip);
|
||||
printf(" IP: " IPSTR "\r\n", IP2STR(&ip.ip));
|
||||
printf(" MASK: " IPSTR "\r\n", IP2STR(&ip.netmask));
|
||||
printf(" GW: " IPSTR "\r\n", IP2STR(&ip.gw));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* "iperf" command */
|
||||
|
||||
static struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_lit *server;
|
||||
struct arg_lit *udp;
|
||||
struct arg_lit *version;
|
||||
struct arg_int *port;
|
||||
struct arg_int *length;
|
||||
struct arg_int *interval;
|
||||
struct arg_int *time;
|
||||
struct arg_int *bw_limit;
|
||||
struct arg_lit *abort;
|
||||
struct arg_end *end;
|
||||
} iperf_args;
|
||||
|
||||
static int ppp_cmd_iperf(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
|
||||
iperf_cfg_t cfg;
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, iperf_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
|
||||
// ethernet iperf only support IPV4 address
|
||||
cfg.type = IPERF_IP_TYPE_IPV4;
|
||||
|
||||
/* iperf -a */
|
||||
if (iperf_args.abort->count != 0) {
|
||||
iperf_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
|
||||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
|
||||
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf -s */
|
||||
if (iperf_args.ip->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_SERVER;
|
||||
}
|
||||
/* iperf -c SERVER_ADDRESS */
|
||||
else {
|
||||
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
|
||||
cfg.flag |= IPERF_FLAG_CLIENT;
|
||||
}
|
||||
|
||||
if (iperf_args.length->count == 0) {
|
||||
cfg.len_send_buf = 0;
|
||||
} else {
|
||||
cfg.len_send_buf = iperf_args.length->ival[0];
|
||||
}
|
||||
|
||||
/* wait for ip, could blocked here */
|
||||
xEventGroupWaitBits(event_group, GOTIP_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
|
||||
cfg.source_ip4 = INADDR_ANY;
|
||||
|
||||
/* iperf -u */
|
||||
if (iperf_args.udp->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_TCP;
|
||||
} else {
|
||||
cfg.flag |= IPERF_FLAG_UDP;
|
||||
}
|
||||
|
||||
/* iperf -p */
|
||||
if (iperf_args.port->count == 0) {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
if (cfg.flag & IPERF_FLAG_SERVER) {
|
||||
cfg.sport = iperf_args.port->ival[0];
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = iperf_args.port->ival[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -i */
|
||||
if (iperf_args.interval->count == 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
} else {
|
||||
cfg.interval = iperf_args.interval->ival[0];
|
||||
if (cfg.interval <= 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -t */
|
||||
if (iperf_args.time->count == 0) {
|
||||
cfg.time = IPERF_DEFAULT_TIME;
|
||||
} else {
|
||||
cfg.time = iperf_args.time->ival[0];
|
||||
if (cfg.time <= cfg.interval) {
|
||||
cfg.time = cfg.interval;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -b */
|
||||
if (iperf_args.bw_limit->count == 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
} else {
|
||||
cfg.bw_lim = iperf_args.bw_limit->ival[0];
|
||||
if (cfg.bw_lim <= 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n",
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
(uint16_t) cfg.source_ip4 & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
|
||||
cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
cfg.interval, cfg.time);
|
||||
|
||||
iperf_start(&cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_pppd(void)
|
||||
{
|
||||
event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
|
||||
s_ppp_netif = esp_netif_new(&ppp_netif_config);
|
||||
assert(s_ppp_netif);
|
||||
esp_netif_ppp_config_t ppp_config = { true, true };
|
||||
esp_netif_ppp_set_params(s_ppp_netif, &ppp_config);
|
||||
|
||||
ESP_ERROR_CHECK(modem_init_network(s_ppp_netif));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_modem_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_changed, NULL));
|
||||
|
||||
modem_start_network();
|
||||
|
||||
pppd_control_args.control = arg_str1(NULL, NULL, "<info>", "Get info of pppd");
|
||||
pppd_control_args.end = arg_end(1);
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "pppd",
|
||||
.help = "PPP interface IO control",
|
||||
.hint = NULL,
|
||||
.func = pppd_cmd_control,
|
||||
.argtable = &pppd_control_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
|
||||
iperf_args.ip = arg_str0("c", "client", "<ip>",
|
||||
"run in client mode, connecting to <host>");
|
||||
iperf_args.server = arg_lit0("s", "server", "run in server mode");
|
||||
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
|
||||
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
|
||||
iperf_args.port = arg_int0("p", "port", "<port>",
|
||||
"server port to listen on/connect to");
|
||||
iperf_args.length = arg_int0("l", "len", "<length>", "set read/write buffer size");
|
||||
iperf_args.interval = arg_int0("i", "interval", "<interval>",
|
||||
"seconds between periodic bandwidth reports");
|
||||
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
|
||||
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
|
||||
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
|
||||
iperf_args.end = arg_end(1);
|
||||
const esp_console_cmd_t iperf_cmd = {
|
||||
.command = "iperf",
|
||||
.help = "iperf command",
|
||||
.hint = NULL,
|
||||
.func = &ppp_cmd_iperf,
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
|
||||
}
|
26
components/esp_modem/test/target_iperf/main/network_dce.h
Normal file
26
components/esp_modem/test/target_iperf/main/network_dce.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "esp_err.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
/**
|
||||
* @brief Initialize PPP network (with a generic PPP server)
|
||||
* @param netif PPP netif
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t modem_init_network(esp_netif_t *netif);
|
||||
|
||||
/**
|
||||
* @brief Start PPP network
|
||||
*/
|
||||
void modem_start_network();
|
||||
|
||||
/**
|
||||
* @brief Stop PPP network
|
||||
*/
|
||||
void modem_stop_network();
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "cmd_system.h"
|
||||
|
||||
void register_pppd(void);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
repl_config.prompt = "iperf>";
|
||||
// init console REPL environment
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
|
||||
|
||||
/* Register commands */
|
||||
register_system_common();
|
||||
register_pppd();
|
||||
|
||||
printf("\n =======================================================\n");
|
||||
printf(" | Steps to Test PPP Client Bandwidth |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | 1. Enter 'help', check all supported commands |\n");
|
||||
printf(" | 2. Start PPP server on host system |\n");
|
||||
printf(" | - pppd /dev/ttyUSB1 115200 192.168.11.1:192.168.11.2 modem local noauth debug nocrtscts nodetach +ipv6\n");
|
||||
printf(" | 3. Wait ESP32 to get IP from PPP server |\n");
|
||||
printf(" | 4. Enter 'pppd info' (optional) |\n");
|
||||
printf(" | 5. Server: 'iperf -u -s -i 3' |\n");
|
||||
printf(" | 6. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
|
||||
printf(" | |\n");
|
||||
printf(" =======================================================\n\n");
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
112
components/esp_modem/test/target_iperf/pytest_ppp_iperf.py
Normal file
112
components/esp_modem/test/target_iperf/pytest_ppp_iperf.py
Normal file
@ -0,0 +1,112 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
"""
|
||||
Test case for iperf example.
|
||||
|
||||
This test case might have problem running on Windows:
|
||||
|
||||
- use `sudo killall iperf` to force kill iperf, didn't implement windows version
|
||||
|
||||
"""
|
||||
from __future__ import division, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
from common_test_methods import get_host_ip4_by_dest_ip
|
||||
from idf_iperf_test_util import IperfUtility
|
||||
from pytest_embedded import Dut
|
||||
|
||||
try:
|
||||
from typing import Any, Callable, Tuple
|
||||
except ImportError:
|
||||
# Only used for type annotations
|
||||
pass
|
||||
|
||||
NO_BANDWIDTH_LIMIT = -1 # iperf send bandwidth is not limited
|
||||
|
||||
|
||||
class IperfTestUtilityEth(IperfUtility.IperfTestUtility):
|
||||
""" iperf test implementation """
|
||||
def __init__(self, dut: str, config_name: str, pc_nic_ip: str, pc_iperf_log_file: str, test_result:Any=None) -> None:
|
||||
IperfUtility.IperfTestUtility.__init__(self, dut, config_name, 'None', 'None', pc_nic_ip, pc_iperf_log_file, test_result)
|
||||
|
||||
def setup(self) -> Tuple[str,int]:
|
||||
"""
|
||||
setup iperf test:
|
||||
|
||||
1. kill current iperf process
|
||||
2. reboot DUT (currently iperf is not very robust, need to reboot DUT)
|
||||
"""
|
||||
try:
|
||||
subprocess.check_output('sudo killall iperf 2>&1 > /dev/null', shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
self.dut.write('restart')
|
||||
dut_ip = self.dut.expect(r'pppd_test: IP : (\d+\.\d+\.\d+\.\d+)').group(1)
|
||||
self.dut.expect("Type 'help' to get the list of commands.")
|
||||
self.dut.expect('iperf>')
|
||||
rssi = 0
|
||||
return dut_ip, rssi
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def log_performance(record_property: Callable[[str, object], None]) -> Callable[[str, str], None]:
|
||||
"""
|
||||
log performance item with pre-defined format to the console
|
||||
and record it under the ``properties`` tag in the junit report if available.
|
||||
"""
|
||||
|
||||
def real_func(item: str, value: str) -> None:
|
||||
"""
|
||||
:param item: performance item name
|
||||
:param value: performance value
|
||||
"""
|
||||
logging.info('[Performance][%s]: %s', item, value)
|
||||
|
||||
return real_func
|
||||
|
||||
|
||||
def test_esp_pppd_iperf(
|
||||
dut: Dut,
|
||||
log_performance: Callable[[str, object], None],
|
||||
# check_performance: Callable[[str, float, str], None],
|
||||
) -> None:
|
||||
"""
|
||||
steps: |
|
||||
1. test TCP tx rx and UDP tx rx throughput
|
||||
2. compare with the pre-defined pass standard
|
||||
"""
|
||||
|
||||
# 1. preparing
|
||||
pc_iperf_log_file = os.path.join(dut.logdir, 'pc_iperf_log.md')
|
||||
# 2. wait for DUT
|
||||
dut_ip = dut.expect(r'pppd_test: IP : (\d+\.\d+\.\d+\.\d+)').group(1)
|
||||
|
||||
pc_nic_ip = get_host_ip4_by_dest_ip(dut_ip)
|
||||
test_result = {
|
||||
'tcp_tx': IperfUtility.TestResult('tcp', 'tx', 'ppp_client'),
|
||||
'tcp_rx': IperfUtility.TestResult('tcp', 'rx', 'ppp_client'),
|
||||
'udp_tx': IperfUtility.TestResult('udp', 'tx', 'ppp_client'),
|
||||
'udp_rx': IperfUtility.TestResult('udp', 'rx', 'ppp_client'),
|
||||
}
|
||||
test_utility = IperfTestUtilityEth(dut, 'ppp', pc_nic_ip, pc_iperf_log_file, test_result)
|
||||
|
||||
# 3. run test for TCP Tx, Rx and UDP Tx, Rx
|
||||
test_utility.run_test('tcp', 'tx', 0, 5)
|
||||
test_utility.run_test('tcp', 'rx', 0, 5)
|
||||
test_utility.run_test('udp', 'tx', 0, 5)
|
||||
test_utility.run_test('udp', 'rx', 0, 5)
|
||||
|
||||
# 4. print out performance details
|
||||
for throughput_type in test_result:
|
||||
print('{}_throughput'.format(throughput_type))
|
||||
print(test_result[throughput_type])
|
||||
print('---------------------------')
|
||||
|
||||
# 5. log performance summary
|
||||
for throughput_type in test_result:
|
||||
log_performance('{}_throughput'.format(throughput_type),
|
||||
'{:.02f} Mbps'.format(test_result[throughput_type].get_best_throughput()))
|
@ -0,0 +1,3 @@
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096
|
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
|
@ -551,9 +551,14 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand
|
||||
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
|
||||
|
@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(mdns): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py mdns
|
||||
tag_format: mdns-v$version
|
||||
version: 1.2.2
|
||||
version: 1.2.3
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## [1.2.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.3)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fixed CI issues for host and afl tests ([4be5efc84e](https://github.com/espressif/esp-protocols/commit/4be5efc84e))
|
||||
- fix copy delegated host addr ([19fb36000c](https://github.com/espressif/esp-protocols/commit/19fb36000c))
|
||||
- enable CONFIG_ESP_WIFI_ENABLED if CONFIG_SOC_WIFI_SUPPORTED is also enabled (for ESP-IDF <= 5.1) ([d20a718320](https://github.com/espressif/esp-protocols/commit/d20a718320))
|
||||
- remove protocol_examples_common ([1ee9dae6bf](https://github.com/espressif/esp-protocols/commit/1ee9dae6bf))
|
||||
- move the example into a subdirectory ([d28232b9f8](https://github.com/espressif/esp-protocols/commit/d28232b9f8))
|
||||
- reference protocol_examples_common from IDF ([c83b76ea8f](https://github.com/espressif/esp-protocols/commit/c83b76ea8f))
|
||||
|
||||
## [1.2.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.2)
|
||||
|
||||
### Bug Fixes
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: "1.2.2"
|
||||
version: "1.2.3"
|
||||
description: mDNS
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
|
||||
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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user