mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-03 21:56:40 +02:00
Compare commits
128 Commits
mdns-v1.0.
...
websocket-
Author | SHA1 | Date | |
---|---|---|---|
b992ef6914 | |||
9cc594afaa | |||
4cf9e505e1 | |||
c8c05075fb | |||
cb6e03ac62 | |||
2e42b9bb49 | |||
86f7a8bbe3 | |||
7b5a41deea | |||
38d50eede0 | |||
27303d28b2 | |||
d273e10819 | |||
60c87ddf26 | |||
a95891e211 | |||
411dced3e2 | |||
42fe60828f | |||
36899f1135 | |||
0714e100ee | |||
cbfee945a0 | |||
71bb461ed8 | |||
5143f5ac01 | |||
7e83741615 | |||
b880fc0367 | |||
f6d5186e5b | |||
8eb3a0feea | |||
28cd898eca | |||
a22391ae2c | |||
f5a0d5fb40 | |||
ecc465daa3 | |||
181b6e4013 | |||
40e3875f76 | |||
ddc3eb62d0 | |||
3625889049 | |||
2bd6163ec8 | |||
f1eb46580e | |||
1c20328dcf | |||
6de22f3893 | |||
e280b3b541 | |||
00d7c40848 | |||
4f1769ec71 | |||
20dd910b3a | |||
3635e2fabc | |||
84035d8f3b | |||
e620eb5fb1 | |||
7d4755f119 | |||
588465d9db | |||
c443326a34 | |||
79a0e57ca1 | |||
68392f0ba9 | |||
a67cbbcab9 | |||
b5177cb23a | |||
cab0e1d10e | |||
fbc79a846b | |||
a50f91f422 | |||
4910e89249 | |||
c562461711 | |||
8b9c957fe0 | |||
f0df12dad3 | |||
9637517192 | |||
e085826dbb | |||
c974c14220 | |||
247baeed22 | |||
613d67d1cf | |||
441f79022e | |||
4de52981cb | |||
ef1c5eb28a | |||
9a3aa1d23f | |||
64b0e4ef1a | |||
fcff00740a | |||
1102133458 | |||
4f54c49912 | |||
910f6ffadc | |||
9836a8620f | |||
af0ed62ecf | |||
217a96a2e4 | |||
6a94e61a7e | |||
c8fe4092c4 | |||
31187b7d17 | |||
ab94566995 | |||
f67511c2c1 | |||
05348534a6 | |||
18ea910f02 | |||
62be981c92 | |||
6daf6c6ba6 | |||
b0d3b41aae | |||
f48d9b2fcf | |||
6c299c068b | |||
ea5d3cee29 | |||
7330597586 | |||
1a0a41fa2d | |||
2f7cbd16db | |||
87dcd7dc7d | |||
8e5a27f4b7 | |||
6528f446bc | |||
4fa3023ea2 | |||
10f8200564 | |||
a547ec8147 | |||
db0e20f446 | |||
ca3fce003e | |||
f6ff132eb1 | |||
8bb207e9bb | |||
76047a4cc6 | |||
ee9b04f598 | |||
d238e9311b | |||
a838af46a6 | |||
5ed3e9a6a1 | |||
5b3346f5c5 | |||
a06fb7714b | |||
e69a9ebb3d | |||
2e607e8ffb | |||
0a9765626b | |||
537d170f1d | |||
0a682e7f12 | |||
afb69308df | |||
284cdeb2ed | |||
97d52495d4 | |||
423e965c88 | |||
e762ada551 | |||
31d4323f53 | |||
97831c62b1 | |||
ceedcfca23 | |||
7547267336 | |||
bf114d3624 | |||
574738b8d3 | |||
ee09ff45e5 | |||
895a72b27d | |||
bf32e452d6 | |||
4c7720a2c1 | |||
610372f5a7 |
2
.flake8
2
.flake8
@ -137,4 +137,4 @@ show_source = True
|
||||
statistics = True
|
||||
|
||||
exclude =
|
||||
components/asio/docs/conf_common.py
|
||||
docs/asio/conf_common.py
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -8,7 +8,7 @@ body:
|
||||
label: Answers checklist.
|
||||
description: Before submitting a new issue, please follow the checklist and try to find the answer.
|
||||
options:
|
||||
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
|
||||
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
|
||||
required: true
|
||||
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
|
||||
required: true
|
||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,7 +1,7 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: esp-protocols documentation
|
||||
url: https://espressif.github.io/esp-protocols/
|
||||
- name: esp-protocols documentation links
|
||||
url: https://github.com/espressif/esp-protocols#readme
|
||||
about: Documenation for esp-protocols components.
|
||||
- name: ESP-IDF Programming Guide
|
||||
url: https://docs.espressif.com/projects/esp-idf/en/latest/
|
||||
|
2
.github/ISSUE_TEMPLATE/other-issue.yml
vendored
2
.github/ISSUE_TEMPLATE/other-issue.yml
vendored
@ -7,7 +7,7 @@ body:
|
||||
label: Answers checklist.
|
||||
description: Before submitting a new issue, please follow the checklist and try to find the answer.
|
||||
options:
|
||||
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
|
||||
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
|
||||
required: true
|
||||
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
|
||||
required: true
|
||||
|
@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_asio:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'asio')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'asio') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
@ -58,7 +58,9 @@ jobs:
|
||||
|
||||
target_tests_asio:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'asio') && (github.repository == 'espressif/esp-protocols') }}
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'asio') || github.event_name == 'push' )
|
||||
name: Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
|
48
.github/workflows/examples_build-host-test.yml
vendored
Normal file
48
.github/workflows/examples_build-host-test.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: "examples: build/host-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_all_examples:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1"]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
# Build default configs for all targets
|
||||
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
|
||||
|
||||
build_and_run_on_host:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
|
||||
name: Build and run examples on linux
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
python ./ci/build_apps.py examples/mqtt -l -t linux
|
||||
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
|
||||
grep 'MQTT_EVENT_DATA' test.log
|
125
.github/workflows/mdns__build-target-test.yml
vendored
125
.github/workflows/mdns__build-target-test.yml
vendored
@ -9,133 +9,48 @@ on:
|
||||
|
||||
jobs:
|
||||
build_mdns:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32c3"]
|
||||
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
|
||||
idf_ver: ["latest", "release-v5.0"]
|
||||
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
working-directory: ${{ matrix.test.path }}
|
||||
run: |
|
||||
${IDF_PATH}/install.sh --enable-pytest
|
||||
. ${IDF_PATH}/export.sh
|
||||
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
||||
for dir in `ls -d build_*`; do
|
||||
python -m pip install idf-build-apps
|
||||
# Build default configs for all targets
|
||||
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -m components/mdns/.build-test-rules.yml -d
|
||||
# Build specific configs for test targets
|
||||
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }}
|
||||
cd components/mdns/${{ matrix.test.path }}
|
||||
for dir in `ls -d build_esp32_*`; do
|
||||
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
|
||||
zip -qur artifacts.zip $dir
|
||||
done
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.idf_target }} == "esp32"
|
||||
with:
|
||||
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ matrix.test.path }}/artifacts.zip
|
||||
name: mdns_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: components/mdns/${{ matrix.test.path }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build_mdns_app:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns')
|
||||
name: Build Test Apps
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32c3"]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Build Environment
|
||||
working-directory: components/mdns/tests/test_apps
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: components/mdns/tests/test_apps
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --pytest-apps
|
||||
- name: Merge binaries with IDF-${{ matrix.idf_ver }}
|
||||
working-directory: components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: |
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/bootloader/bootloader.bin
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/partition_table/partition-table.bin
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.bin
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.elf
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/flasher_args.json
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.h
|
||||
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.json
|
||||
if-no-files-found: error
|
||||
|
||||
target_test_apps_mdns:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'mdns') && (github.repository == 'espressif/esp-protocols') }}
|
||||
name: Target Test Apps
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
needs: build_mdns_app
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: components/mdns/tests/test_apps/build
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Download Test apps to target
|
||||
run: |
|
||||
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/mdns/tests/test_apps/build/flash_image.bin
|
||||
- name: Run Example Test on target
|
||||
working-directory: components/mdns/tests/test_apps
|
||||
run: |
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{matrix.idf_target}}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: test_apps_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: components/mdns/tests/test_apps/*.xml
|
||||
|
||||
target_tests_mdns:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'mdns') && (github.repository == 'espressif/esp-protocols') }}
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push' )
|
||||
name: Target Example and Unit tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
|
||||
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
|
||||
needs: build_mdns
|
||||
runs-on:
|
||||
- self-hosted
|
||||
@ -147,14 +62,14 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ matrix.test.path }}/ci/
|
||||
path: components/mdns/${{ matrix.test.path }}/ci/
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
|
||||
working-directory: ${{ matrix.test.path }}
|
||||
working-directory: components/mdns/${{ matrix.test.path }}
|
||||
run: |
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
@ -166,4 +81,4 @@ jobs:
|
||||
if: always()
|
||||
with:
|
||||
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
|
||||
path: ${{ matrix.test.path }}/*.xml
|
||||
path: components/mdns/${{ matrix.test.path }}/*.xml
|
||||
|
6
.github/workflows/mdns__host-tests.yml
vendored
6
.github/workflows/mdns__host-tests.yml
vendored
@ -9,10 +9,10 @@ on:
|
||||
|
||||
jobs:
|
||||
host_test_mdns:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Host test
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:latest
|
||||
container: espressif/idf:release-v5.1
|
||||
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
@ -33,7 +33,7 @@ jobs:
|
||||
cat ip.txt
|
||||
|
||||
build_afl_host_test_mdns:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Build AFL host test
|
||||
strategy:
|
||||
matrix:
|
||||
|
51
.github/workflows/modem__build-host-tests.yml
vendored
51
.github/workflows/modem__build-host-tests.yml
vendored
@ -9,15 +9,19 @@ on:
|
||||
|
||||
jobs:
|
||||
build_esp_modem:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v4.2", "release-v4.3", "release-v4.4", "release-v5.0"]
|
||||
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
|
||||
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
|
||||
exclude:
|
||||
- idf_ver: "release-v4.2"
|
||||
example: simple_cmux_client
|
||||
- idf_ver: "release-v4.2"
|
||||
example: modem_tcp_client
|
||||
- idf_ver: "release-v4.3"
|
||||
example: modem_tcp_client
|
||||
include:
|
||||
- idf_ver: "release-v4.2"
|
||||
skip_config: usb
|
||||
@ -44,10 +48,10 @@ jobs:
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
cd $GITHUB_WORKSPACE/protocols
|
||||
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }}
|
||||
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/examples/.build-test-rules.yml
|
||||
|
||||
host_test_esp_modem:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Host Tests
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:release-v4.3
|
||||
@ -84,9 +88,11 @@ jobs:
|
||||
files: esp-protocols/components/esp_modem/test/host_test/junit.xml
|
||||
|
||||
host_test_gcov_esp_modem:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Run gcovr on esp modem host test
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
container: espressif/idf:release-v4.3
|
||||
env:
|
||||
lwip: lwip-2.1.2
|
||||
@ -98,11 +104,11 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: esp-protocols
|
||||
repository: ''
|
||||
persist-credentials: false
|
||||
- name: Build and Test
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip
|
||||
apt-get install -y rsync
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
|
||||
@ -122,10 +128,9 @@ jobs:
|
||||
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
|
||||
gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/*
|
||||
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml
|
||||
mkdir docs_gcovr
|
||||
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr
|
||||
touch docs_gcovr/.nojekyll
|
||||
|
||||
mkdir modem_coverage_report
|
||||
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report
|
||||
cp -rf modem_coverage_report $GITHUB_WORKSPACE
|
||||
- name: Code Coverage Summary Report
|
||||
uses: irongut/CodeCoverageSummary@v1.3.0
|
||||
with:
|
||||
@ -138,7 +143,6 @@ jobs:
|
||||
indicators: true
|
||||
output: both
|
||||
thresholds: '60 80'
|
||||
|
||||
- name: Write to Job Summary
|
||||
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@ -146,28 +150,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: docs_gcovr
|
||||
name: modem_coverage_report
|
||||
path: |
|
||||
${{ env.COMP_DIR }}/docs_gcovr
|
||||
${{ env.COMP_DIR }}/modem_coverage_report
|
||||
if-no-files-found: error
|
||||
|
||||
# show_report_data:
|
||||
# name: Publish-Results
|
||||
# if: github.ref == 'refs/heads/master' || github.repository != 'espressif/esp-protocols'
|
||||
# runs-on: ubuntu-22.04
|
||||
# needs: gcovr_analyzer_esp_modem
|
||||
# steps:
|
||||
# - name: Checkout 🛎️
|
||||
# uses: actions/checkout@v3
|
||||
# with:
|
||||
# persist-credentials: false
|
||||
# - name: Download Artifacts
|
||||
# uses: actions/download-artifact@v1
|
||||
# with:
|
||||
# name: docs_gcovr
|
||||
#
|
||||
# - name: Deploy generated docs
|
||||
# uses: JamesIves/github-pages-deploy-action@v4
|
||||
# with:
|
||||
# branch: gh-pages
|
||||
# folder: 'docs_gcovr'
|
||||
|
8
.github/workflows/modem__target-test.yml
vendored
8
.github/workflows/modem__target-test.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_esp_modem_tests:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Build Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
@ -46,7 +46,9 @@ jobs:
|
||||
|
||||
target_tests_esp_modem:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'modem') && (github.repository == 'espressif/esp-protocols') }}
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push' )
|
||||
name: Run Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
@ -56,7 +58,7 @@ jobs:
|
||||
needs: build_esp_modem_tests
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- BrnoRPI-GH006
|
||||
- modem
|
||||
env:
|
||||
TEST_DIR: components/esp_modem/${{ matrix.test.path }}
|
||||
steps:
|
||||
|
2
.github/workflows/mqtt_cxx__build.yml
vendored
2
.github/workflows/mqtt_cxx__build.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_esp_mqtt_cxx:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mqtt')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mqtt') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
|
18
.github/workflows/pre_commit_check.yml
vendored
18
.github/workflows/pre_commit_check.yml
vendored
@ -12,12 +12,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
fetch-depth: 20
|
||||
- name: Fetch head and base refs
|
||||
# This is necessary for pre-commit to check the changes in the PR branch
|
||||
# This is necessary for pre-commit to check the changes in the PR branch (and to set origin/HEAD and HEAD refs)
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }}:base_ref
|
||||
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_ref
|
||||
git fetch origin ${{ github.event.pull_request.head.sha }}:pr_ref
|
||||
git checkout pr_ref
|
||||
git remote set-head origin --auto
|
||||
git merge-base origin/HEAD HEAD || ( echo "Your PR is far behind origin/HEAD, please rebase" && exit 1 )
|
||||
- name: Set up Python environment
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
@ -26,13 +31,12 @@ jobs:
|
||||
run: |
|
||||
pip install pre-commit
|
||||
pre-commit install-hooks
|
||||
pre-commit install --hook-type commit-msg --hook-type pre-push
|
||||
- name: Run pre-commit and check for any changes
|
||||
run: |
|
||||
echo "Commits being checked:"
|
||||
git log --oneline --no-decorate base_ref..pr_ref
|
||||
git log --oneline --no-decorate origin/HEAD..HEAD
|
||||
echo ""
|
||||
if ! pre-commit run --from-ref base_ref --to-ref pr_ref --show-diff-on-failure ; then
|
||||
if ! pre-commit run --from-ref origin/HEAD --to-ref HEAD --hook-stage manual --show-diff-on-failure ; then
|
||||
echo ""
|
||||
echo "::notice::It looks like the commits in this PR have been made without having pre-commit hooks installed."
|
||||
echo "::notice::Please see https://github.com/espressif/esp-protocols/CONTRIBUTING.md for instructions."
|
||||
|
45
.github/workflows/publish-coverage-report.yml
vendored
Normal file
45
.github/workflows/publish-coverage-report.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Publish coverage report to Github Pages
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"]
|
||||
types:
|
||||
- completed
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
publish_github_pages:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Download Websocket Artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: websocket__build-host-tests.yml
|
||||
workflow_conclusion: success
|
||||
name: websocket_coverage_report
|
||||
path: websocket_coverage_report_artifact
|
||||
- name: Download Modem Artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: modem__build-host-tests.yml
|
||||
workflow_conclusion: success
|
||||
name: modem_coverage_report
|
||||
path: modem_coverage_report_artifact
|
||||
- name: Merge HTML files
|
||||
run: |
|
||||
echo "<html><body>" > index.html
|
||||
cat modem_coverage_report_artifact/index.html >> index.html
|
||||
cat websocket_coverage_report_artifact/index.html >> index.html
|
||||
echo "</body></html>" >> index.html
|
||||
mkdir coverage_report
|
||||
mv index.html coverage_report
|
||||
|
||||
- name: Deploy generated docs
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.5
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: coverage_report
|
123
.github/workflows/publish-docs-component.yml
vendored
123
.github/workflows/publish-docs-component.yml
vendored
@ -5,66 +5,89 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_DEPLOY_URL_BASE }}
|
||||
DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_DEPLOY_SERVER }}
|
||||
DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_DEPLOY_SERVER_USER }}
|
||||
DOCS_DEPLOY_KEY: ${{ secrets.DOCS_DEPLOY_PRIVATEKEY }}
|
||||
DOCS_DEPLOY_PATH_ORIG : ${{ secrets.DOCS_DEPLOY_PATH }}
|
||||
|
||||
jobs:
|
||||
docs_build:
|
||||
name: Docs-Build-And-Upload
|
||||
publish:
|
||||
name: Publish Tag, Release, Docs, Component
|
||||
runs-on: ubuntu-latest
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Generate docs
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip
|
||||
python -m pip install breathe recommonmark esp-docs
|
||||
cd $GITHUB_WORKSPACE/components/esp_modem/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/mdns/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
|
||||
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
|
||||
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/asio/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/asio
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/asio
|
||||
|
||||
cd $GITHUB_WORKSPACE/docs
|
||||
touch .nojekyll
|
||||
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
|
||||
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
|
||||
echo '<a href="asio/index.html">ASIO</a><br>' >> index.html
|
||||
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
|
||||
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
|
||||
|
||||
|
||||
- name: Upload components to component service
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Check for version update
|
||||
shell: bash
|
||||
run: ./ci/detect_component_bump
|
||||
- name: Tag merge commit
|
||||
if: env.BUMP_VERSION != ''
|
||||
uses: anothrNick/github-tag-action@1.61.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_TAG: ${{ env.BUMP_TAG }}
|
||||
- name: Create Release
|
||||
if: env.BUMP_VERSION != ''
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio"
|
||||
body_path: "release_notes.md"
|
||||
tag_name: ${{ env.BUMP_TAG }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate docs
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip
|
||||
python -m pip install breathe recommonmark esp-docs==1.4.1
|
||||
for comp in `ls components`; do
|
||||
cd $GITHUB_WORKSPACE/docs/${comp}
|
||||
if [[ "${{ env.BUMP_COMPONENT }}" == "${comp}" ]]; then
|
||||
echo "Building specific version of ${comp} (${{ env.BUMP_VERSION }})"
|
||||
./generate_docs ${{ env.BUMP_VERSION }}
|
||||
else
|
||||
echo "Building latest version of ${comp}"
|
||||
./generate_docs
|
||||
fi
|
||||
done
|
||||
- name: Deploying generated docs
|
||||
shell: bash
|
||||
run: |
|
||||
source $GITHUB_WORKSPACE/docs/utils.sh
|
||||
add_doc_server_ssh_keys $DOCS_DEPLOY_KEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
|
||||
export GIT_VER=$(git describe --always)
|
||||
export GITHUB_REF_NAME=latest
|
||||
for comp in `ls components`; do
|
||||
echo "Deploying latest of ${comp}"
|
||||
export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs/${comp}
|
||||
export DOCS_DEPLOY_PATH=$DOCS_DEPLOY_PATH_ORIG/${comp}
|
||||
cd $GITHUB_WORKSPACE/docs/${comp}
|
||||
deploy-docs
|
||||
done;
|
||||
# Deploy docs with version path
|
||||
if [[ "${{ env.BUMP_VERSION }}" != "" ]]; then
|
||||
echo "Deploying specific version of ${comp} (${{ env.BUMP_VERSION }})"
|
||||
cd $GITHUB_WORKSPACE/docs/${{ env.BUMP_COMPONENT }}
|
||||
export GITHUB_REF_NAME=${{ env.BUMP_VERSION }}
|
||||
deploy-docs
|
||||
fi
|
||||
- name: Upload components to component service
|
||||
uses: espressif/upload-components-ci-action@v1
|
||||
with:
|
||||
directories: >
|
||||
components/asio;
|
||||
components/esp_modem;
|
||||
components/esp_mqtt_cxx;
|
||||
components/esp_websocket_client;
|
||||
components/mdns;
|
||||
namespace: "espressif"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
||||
- name: Deploy generated docs
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.5
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: docs
|
||||
|
86
.github/workflows/run-host-tests.yml
vendored
Normal file
86
.github/workflows/run-host-tests.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
name: Run on host
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
idf_version:
|
||||
required: true
|
||||
type: string
|
||||
app_name:
|
||||
type: string
|
||||
required: true
|
||||
app_path:
|
||||
type: string
|
||||
required: true
|
||||
component_path:
|
||||
type: string
|
||||
required: true
|
||||
upload_artifacts:
|
||||
type: boolean
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build App
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: write
|
||||
container: espressif/idf:${{inputs.idf_version}}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
|
||||
rm -rf sdkconfig sdkconfig.defaults build
|
||||
cp sdkconfig.ci.linux sdkconfig.defaults
|
||||
idf.py build
|
||||
./build/${{inputs.app_name}}.elf
|
||||
- name: Build with Coverage Enabled
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
|
||||
rm -rf build sdkconfig sdkconfig.defaults
|
||||
cp sdkconfig.ci.coverage sdkconfig.defaults
|
||||
idf.py fullclean
|
||||
idf.py build
|
||||
./build/${{inputs.app_name}}.elf
|
||||
- name: Run Coverage
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update && apt-get install -y python3-pip rsync
|
||||
python -m pip install gcovr
|
||||
cd $GITHUB_WORKSPACE/${{inputs.component_path}}
|
||||
gcov `find . -name "*gcda"`
|
||||
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x ${{inputs.app_name}}_coverage.xml
|
||||
mkdir ${{inputs.app_name}}_coverage_report
|
||||
touch ${{inputs.app_name}}_coverage_report/.nojekyll
|
||||
cp index.html ${{inputs.app_name}}_coverage_report
|
||||
cp -rf ${{inputs.app_name}}_coverage_report ${{inputs.app_name}}_coverage.xml $GITHUB_WORKSPACE
|
||||
- name: Code Coverage Summary Report
|
||||
uses: irongut/CodeCoverageSummary@v1.3.0
|
||||
with:
|
||||
filename: esp-protocols/**/${{inputs.app_name}}_coverage.xml
|
||||
badge: true
|
||||
fail_below_min: false
|
||||
format: markdown
|
||||
hide_branch_rate: false
|
||||
hide_complexity: false
|
||||
indicators: true
|
||||
output: both
|
||||
thresholds: '60 80'
|
||||
- name: Write to Job Summary
|
||||
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
|
||||
- name: Upload files to artifacts for run-target job
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{inputs.upload_artifacts}}
|
||||
with:
|
||||
name: ${{inputs.app_name}}_coverage_report
|
||||
path: |
|
||||
${{inputs.component_path}}/${{inputs.app_name}}_coverage_report
|
||||
if-no-files-found: error
|
20
.github/workflows/websocket__build-host-tests.yml
vendored
Normal file
20
.github/workflows/websocket__build-host-tests.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: "websocket: build/host-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
|
||||
jobs:
|
||||
host_test_websocket:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
|
||||
uses: "./.github/workflows/run-host-tests.yml"
|
||||
with:
|
||||
idf_version: "latest"
|
||||
app_name: "websocket"
|
||||
app_path: "esp-protocols/components/esp_websocket_client/examples/linux"
|
||||
component_path: "esp-protocols/components/esp_websocket_client"
|
||||
upload_artifacts: true
|
@ -9,85 +9,78 @@ on:
|
||||
|
||||
jobs:
|
||||
build_websocket:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'websocket')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "latest"]
|
||||
idf_target: ["esp32"]
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
|
||||
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/esp_websocket_client/examples
|
||||
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cat sdkconfig.ci >> sdkconfig.defaults
|
||||
idf.py build
|
||||
- name: Merge binaries
|
||||
working-directory: ${{ env.TEST_DIR }}/build
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
|
||||
python -m pip install idf-build-apps
|
||||
python ./ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
for dir in `ls -d build_esp32_*`; do
|
||||
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
|
||||
zip -qur artifacts.zip $dir
|
||||
done
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: |
|
||||
${{ env.TEST_DIR }}/build/bootloader/bootloader.bin
|
||||
${{ env.TEST_DIR }}/build/partition_table/partition-table.bin
|
||||
${{ env.TEST_DIR }}/build/*.bin
|
||||
${{ env.TEST_DIR }}/build/*.elf
|
||||
${{ env.TEST_DIR }}/build/flasher_args.json
|
||||
${{ env.TEST_DIR }}/build/config/sdkconfig.h
|
||||
${{ env.TEST_DIR }}/build/config/sdkconfig.json
|
||||
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
run-target-websocket:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'websocket') && (github.repository == 'espressif/esp-protocols') }}
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push' )
|
||||
name: Target test
|
||||
needs: build_websocket
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "latest"]
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
env:
|
||||
TEST_DIR: components/esp_websocket_client/examples
|
||||
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/build/
|
||||
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
- name: Download Example Test to target
|
||||
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
|
||||
- name: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
mv $dir build
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
|
||||
done
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/*.xml
|
||||
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
|
||||
path: components/esp_websocket_client/${{ matrix.test.path }}/*.xml
|
||||
|
@ -43,7 +43,7 @@ repos:
|
||||
hooks:
|
||||
- id: eradicate
|
||||
- repo: https://github.com/espressif/check-copyright/
|
||||
rev: v1.0.1
|
||||
rev: v1.0.3
|
||||
hooks:
|
||||
- id: check-copyright
|
||||
args: ['--ignore', 'ci/check_copyright_ignore.txt', '--config', 'ci/check_copyright_config.yaml']
|
||||
@ -57,12 +57,12 @@ repos:
|
||||
hooks:
|
||||
- id: commitizen
|
||||
- id: commitizen-branch
|
||||
stages: [push]
|
||||
stages: [push, manual]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit message scopes
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, common"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx)\)\:)'
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx|examples)\)\:)'
|
||||
language: pygrep
|
||||
args: [--multiline]
|
||||
stages: [commit-msg]
|
||||
|
@ -13,3 +13,19 @@ Contributions in the form of pull requests, issue reports, and feature requests
|
||||
For quick merging, the contribution should be short, and concentrated on a single feature or topic. The larger the contribution is, the longer it would take to review it and merge it.
|
||||
|
||||
Please follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) rule when writing commit messages.
|
||||
|
||||
## Release process
|
||||
|
||||
When releasing a new component version we have to:
|
||||
|
||||
* Update the version number
|
||||
* Update the changelog
|
||||
* Create the version tag in this repository
|
||||
* Deploy the component to component registry
|
||||
* Update the documentation
|
||||
|
||||
This process is not fully automated, the first step needs to be performed manually by project maintainers running the `bump` command. Release procedure is as follows:
|
||||
* Run `ci/bump [component] [version]` (version number is optional, `cz` would automatically increment it if not present)
|
||||
* Check the updated `CHANGELOG.md` and the generated bump commit message
|
||||
* Create a PR
|
||||
Once the PR is merged, the CI job tags the merge commit, creates a new release, builds and deploys documentation and the new component to the component registry
|
||||
|
21
README.md
21
README.md
@ -1,26 +1,35 @@
|
||||
# Collection of protocol components for ESP-IDF
|
||||
|
||||
[Documentation of esp-protocol](https://espressif.github.io/esp-protocols)
|
||||
## How to use
|
||||
|
||||
The [ESP-Protocols](https://github.com/espressif/esp-protocols) repository contains a collection of protocol components for [ESP-IDF](https://github.com/espressif/esp-idf).
|
||||
Additionally, each component is available in [IDF Component Registry](https://components.espressif.com).
|
||||
Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
|
||||
## Components
|
||||
|
||||
### esp_modem
|
||||
|
||||
* Brief introduction [README](components/esp_modem/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
|
||||
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
|
||||
|
||||
### mDNS
|
||||
|
||||
* Brief introduction [README](components/mdns/README.md)
|
||||
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
|
||||
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
|
||||
* Full html [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
|
||||
* Full html [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html)
|
||||
|
||||
### esp_websocket_client
|
||||
|
||||
* Brief introduction [README](components/esp_websocket_client/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
|
||||
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_websocket_client/docs/latest/index.html)
|
||||
|
||||
### ASIO port
|
||||
|
||||
* Brief introduction [README](components/asio/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/asio/index.html)
|
||||
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
|
||||
|
||||
### esp_mqtt_cxx
|
||||
|
||||
* Brief introduction [README](components/esp_mqtt_cxx/README.md)
|
||||
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_mqtt_cxx/docs/latest/index.html)
|
||||
|
@ -7,7 +7,6 @@ This file is used in CI for esp-protocols build tests
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from idf_build_apps import build_apps, find_apps, setup_logging
|
||||
from idf_build_apps.constants import SUPPORTED_TARGETS
|
||||
@ -24,27 +23,35 @@ if __name__ == '__main__':
|
||||
default='all',
|
||||
help='Build apps for given target',
|
||||
)
|
||||
parser.add_argument('-r', '--rules', nargs='*', default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'], help='Rules how to treat configs')
|
||||
parser.add_argument('-m', '--manifests', nargs='*', default=[], help='list of manifest files')
|
||||
parser.add_argument('-d', '--delete', action='store_true', help='Delete build artifacts')
|
||||
parser.add_argument('-c', '--recursive', action='store_true', help='Build recursively')
|
||||
parser.add_argument('-l', '--linux', action='store_true', help='Include linux build (dont check warnings)')
|
||||
args = parser.parse_args()
|
||||
|
||||
IDF_PATH = os.environ['IDF_PATH']
|
||||
|
||||
print(args.paths)
|
||||
# Compose the ignore warning strings from the global list and from the environment
|
||||
ignore_warning_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),'ignore_build_warnings.txt')
|
||||
ignore_warning = open(ignore_warning_file).read().rstrip('\n').split('\n')
|
||||
if 'EXPECTED_WARNING' in os.environ:
|
||||
ignore_warning += os.environ['EXPECTED_WARNING'].split('\n')
|
||||
if args.linux:
|
||||
SUPPORTED_TARGETS.append('linux')
|
||||
ignore_warning = 'warning: ' # Ignore all common warnings on linux builds
|
||||
setup_logging(2)
|
||||
apps = find_apps(
|
||||
args.paths,
|
||||
recursive=True,
|
||||
recursive=args.recursive,
|
||||
target=args.target,
|
||||
build_dir='build_@t_@w',
|
||||
config_rules_str=[
|
||||
'sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'
|
||||
],
|
||||
config_rules_str=args.rules,
|
||||
build_log_path='build_log.txt',
|
||||
size_json_path='size.json',
|
||||
size_json_path='size.json' if not args.linux else None,
|
||||
check_warnings=True,
|
||||
preserve=True,
|
||||
manifest_files=[
|
||||
str(p) for p in Path('.').glob('**/.build-test-rules.yml')
|
||||
],
|
||||
preserve=not args.delete,
|
||||
manifest_files=args.manifests,
|
||||
default_build_targets=SUPPORTED_TARGETS,
|
||||
manifest_rootpath='.',
|
||||
)
|
||||
@ -56,5 +63,5 @@ if __name__ == '__main__':
|
||||
build_apps(apps,
|
||||
dry_run=False,
|
||||
keep_going=False,
|
||||
ignore_warning_strs=os.environ['EXPECTED_WARNING']
|
||||
if 'EXPECTED_WARNING' in os.environ else None))
|
||||
ignore_warning_strs=ignore_warning)
|
||||
)
|
||||
|
23
ci/bump
Executable file
23
ci/bump
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: bump component [version]"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
comp=$1; shift;
|
||||
|
||||
cd components/${comp}
|
||||
if ! cz bump --dry-run; then
|
||||
echo "Commitizen bump commad failed!"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
cz_bump_out=`cz bump --files-only "$@"`
|
||||
commit_title=`echo "${cz_bump_out}" | head -1`
|
||||
commit_body=`cat ../../release_notes.txt`
|
||||
|
||||
git add -u .
|
||||
git commit -m $"${commit_title}
|
||||
|
||||
${commit_body}"
|
@ -47,7 +47,7 @@ def main():
|
||||
details = '[{}](https://github.com/espressif/esp-protocols/commit/{})'.format(commit, commit)
|
||||
msg_details = git.show('-s', commit, _tty_out=False)
|
||||
# check references
|
||||
if any(str(msg_details) in s for s in ['esp-protocols/issues', 'BREAKING CHANGE', 'MAJOR CHANGE']):
|
||||
if any(s in str(msg_details) for s in ['esp-protocols/issues', 'BREAKING CHANGE', 'MAJOR CHANGE']):
|
||||
# Closes <issue>
|
||||
closes = re.findall(r'Closes ([^\d]+/(\d+))', str(msg_details), re.MULTILINE)
|
||||
if closes and closes[0] is not None:
|
||||
@ -93,6 +93,13 @@ def main():
|
||||
updated_changelog.write(orig_items)
|
||||
git.add(filename)
|
||||
|
||||
# write the current changelog entry to a local text file (removing links, captions and extra newlines)
|
||||
changelog = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', changelog)
|
||||
changelog = re.sub(r'\#\#[\#\s]*(.+)', r'\1', changelog)
|
||||
changelog = re.sub(r'\n\n', '\n', changelog)
|
||||
with open(os.path.join(root_path, 'release_notes.txt'), 'w') as release_notes:
|
||||
release_notes.write(changelog)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -29,6 +29,7 @@ DEFAULT:
|
||||
# this section sets the default license for examples and unit tests of components
|
||||
examples_and_unit_tests:
|
||||
include:
|
||||
- 'examples/**'
|
||||
- 'components/**/examples/**'
|
||||
- 'components/**/test/**'
|
||||
- 'components/**/tests/**'
|
||||
@ -57,7 +58,7 @@ slim_modem_examples:
|
||||
ignore:
|
||||
perform_check: no
|
||||
include:
|
||||
- 'components/**/docs/**'
|
||||
- '**/docs/**'
|
||||
- 'components/esp_modem/port/linux/**'
|
||||
- 'components/asio/examples/**'
|
||||
- 'components/mdns/**/esp_system_protocols_linux/**'
|
||||
|
41
ci/detect_component_bump
Executable file
41
ci/detect_component_bump
Executable file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if ! git show -s | grep -q '^Merge'; then
|
||||
echo "Not a merge commit"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
for comp in `ls components`; do
|
||||
if git log -1 -m --name-only --pretty="" | grep -q components/${comp}/idf_component.yml; then
|
||||
echo "${comp}: Component version file has changed"
|
||||
version=`grep version: components/${comp}/.cz.yaml`
|
||||
version=${version#*version: }
|
||||
|
||||
tag_format=`grep tag_format: components/${comp}/.cz.yaml`
|
||||
tag_format=${tag_format#*tag_format: }
|
||||
|
||||
eval tag=$tag_format
|
||||
# check if the tag is already created
|
||||
if [ $(git tag -l "$tag") ]; then
|
||||
echo "${comp}: version (${tag}) already exits"
|
||||
else
|
||||
echo "${comp}: Component version has been updated to ${version}"
|
||||
# creates release notes from the last entry (between first two "## sections")
|
||||
awk '/^## \[/{a++};{if(a==1){print}}' components/${comp}/CHANGELOG.md > release_notes.md
|
||||
|
||||
echo "BUMP_VERSION=${version}"
|
||||
echo "BUMP_COMPONENT=${comp}"
|
||||
echo "BUMP_TAG=${tag}"
|
||||
|
||||
# export the findings to github env, so it could be used in other jobs
|
||||
echo "BUMP_VERSION=${version}" >> "$GITHUB_ENV"
|
||||
echo "BUMP_COMPONENT=${comp}" >> "$GITHUB_ENV"
|
||||
echo "BUMP_TAG=${tag}" >> "$GITHUB_ENV"
|
||||
|
||||
exit 0;
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo "No changes in component version file"
|
1
ci/ignore_build_warnings.txt
Normal file
1
ci/ignore_build_warnings.txt
Normal file
@ -0,0 +1 @@
|
||||
DeprecationWarning: pkg_resources is deprecated as an API
|
@ -1,6 +1,5 @@
|
||||
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES esp_event)
|
||||
INCLUDE_DIRS include)
|
||||
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
@ -7,6 +7,7 @@
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <pthread.h>
|
||||
|
||||
void *create_tt(esp_timer_cb_t cb);
|
||||
|
||||
@ -37,3 +38,10 @@ esp_err_t esp_timer_delete(esp_timer_handle_t timer)
|
||||
destroy_tt(timer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int64_t esp_timer_get_time(void)
|
||||
{
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_REALTIME, &spec);
|
||||
return spec.tv_nsec / 1000 + spec.tv_sec * 1000000;
|
||||
}
|
@ -7,6 +7,12 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bsd/string.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct esp_timer *esp_timer_handle_t;
|
||||
|
||||
@ -31,3 +37,9 @@ esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
|
||||
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
|
||||
|
||||
esp_err_t esp_timer_delete(esp_timer_handle_t timer);
|
||||
|
||||
int64_t esp_timer_get_time(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
idf_component_register(SRCS freertos_linux.c queue_unique_ptr.cpp
|
||||
idf_component_register(SRCS freertos_linux.c
|
||||
osal/queue.cpp osal/event_group.cpp osal/mutex.cpp
|
||||
INCLUDE_DIRS include)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
272
common_components/linux_compat/freertos/freertos_linux.c
Normal file
272
common_components/linux_compat/freertos/freertos_linux.c
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "osal/osal_api.h"
|
||||
|
||||
static uint64_t s_semaphore_data = 0;
|
||||
|
||||
typedef enum queue_type_tag {
|
||||
MUTEX_REC,
|
||||
MUTEX,
|
||||
SEMA,
|
||||
QUEUE,
|
||||
} queue_type_t;
|
||||
|
||||
struct generic_queue_handle {
|
||||
queue_type_t type;
|
||||
size_t item_size;
|
||||
void *q;
|
||||
};
|
||||
|
||||
static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint32_t len, uint32_t item_size)
|
||||
{
|
||||
struct generic_queue_handle *h = calloc(1, sizeof(struct generic_queue_handle));
|
||||
h->item_size = len;
|
||||
h->type = type;
|
||||
switch (type) {
|
||||
default:
|
||||
case QUEUE:
|
||||
case SEMA:
|
||||
h->q = osal_queue_create();
|
||||
break;
|
||||
|
||||
case MUTEX:
|
||||
case MUTEX_REC:
|
||||
h->q = osal_mutex_create();
|
||||
break;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
|
||||
{
|
||||
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
|
||||
}
|
||||
|
||||
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
|
||||
}
|
||||
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
|
||||
{
|
||||
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
|
||||
}
|
||||
|
||||
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX) {
|
||||
osal_mutex_give(h->q);
|
||||
return pdTRUE;
|
||||
}
|
||||
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX_REC) {
|
||||
osal_mutex_give(h->q);
|
||||
return pdTRUE;
|
||||
}
|
||||
return pdFALSE;
|
||||
}
|
||||
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX) {
|
||||
osal_mutex_take(h->q);
|
||||
return pdTRUE;
|
||||
}
|
||||
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||
}
|
||||
|
||||
|
||||
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX_REC) {
|
||||
osal_mutex_take(h->q);
|
||||
return pdTRUE;
|
||||
}
|
||||
return pdFALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void vQueueDelete( QueueHandle_t xQueue )
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->q) {
|
||||
if (h->type == MUTEX || h->type == MUTEX_REC) {
|
||||
osal_mutex_delete(h->q);
|
||||
} else {
|
||||
osal_queue_delete(h->q);
|
||||
}
|
||||
}
|
||||
free(xQueue);
|
||||
}
|
||||
|
||||
QueueHandle_t xSemaphoreCreateBinary(void)
|
||||
{
|
||||
QueueHandle_t sempaphore = xQueueCreate(1, 1);
|
||||
return sempaphore;
|
||||
}
|
||||
|
||||
QueueHandle_t xSemaphoreCreateMutex(void)
|
||||
{
|
||||
return (QueueHandle_t)create_generic_queue(MUTEX, 1, 1);
|
||||
}
|
||||
|
||||
QueueHandle_t xSemaphoreCreateRecursiveMutex(void)
|
||||
{
|
||||
return (QueueHandle_t)create_generic_queue(MUTEX_REC, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
void vTaskDelete(TaskHandle_t *task)
|
||||
{
|
||||
if (task == NULL) {
|
||||
pthread_exit(0);
|
||||
}
|
||||
void *thread_rval = NULL;
|
||||
pthread_join((pthread_t)task, &thread_rval);
|
||||
}
|
||||
|
||||
void vTaskSuspend(void *task)
|
||||
{
|
||||
vTaskDelete(task);
|
||||
}
|
||||
|
||||
TickType_t xTaskGetTickCount( void )
|
||||
{
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_REALTIME, &spec);
|
||||
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
|
||||
}
|
||||
|
||||
void vTaskDelay( const TickType_t xTicksToDelay )
|
||||
{
|
||||
usleep(xTicksToDelay * 1000);
|
||||
}
|
||||
|
||||
void *pthread_task(void *params)
|
||||
{
|
||||
struct {
|
||||
void *const param;
|
||||
TaskFunction_t task;
|
||||
bool started;
|
||||
} *pthread_params = params;
|
||||
|
||||
void *const param = pthread_params->param;
|
||||
TaskFunction_t task = pthread_params->task;
|
||||
pthread_params->started = true;
|
||||
|
||||
task(param);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t usStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID)
|
||||
{
|
||||
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
|
||||
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask)
|
||||
{
|
||||
pthread_t new_thread = (pthread_t)NULL;
|
||||
pthread_attr_t attr;
|
||||
struct {
|
||||
void *const param;
|
||||
TaskFunction_t task;
|
||||
bool started;
|
||||
} pthread_params = { .param = pvParameters, .task = pvTaskCode};
|
||||
int res = pthread_attr_init(&attr);
|
||||
assert(res == 0);
|
||||
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
assert(res == 0);
|
||||
res = pthread_create(&new_thread, &attr, pthread_task, &pthread_params);
|
||||
assert(res == 0);
|
||||
|
||||
if (pvCreatedTask) {
|
||||
*pvCreatedTask = (void *)new_thread;
|
||||
}
|
||||
|
||||
// just wait till the task started so we can unwind params from the stack
|
||||
while (pthread_params.started == false) {
|
||||
usleep(1000);
|
||||
}
|
||||
return pdTRUE;
|
||||
}
|
||||
|
||||
void xTaskNotifyGive(TaskHandle_t task)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TaskHandle_t xTaskGetCurrentTaskHandle(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EventGroupHandle_t xEventGroupCreate( void )
|
||||
{
|
||||
return osal_signal_create();
|
||||
}
|
||||
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
|
||||
{
|
||||
osal_signal_delete(xEventGroup);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
|
||||
{
|
||||
return osal_signal_clear(xEventGroup, uxBitsToClear);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
|
||||
{
|
||||
return osal_signal_get(xEventGroup);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
|
||||
{
|
||||
return osal_signal_set(xEventGroup, uxBitsToSet);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
|
||||
{
|
||||
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
|
||||
}
|
@ -10,3 +10,4 @@
|
||||
|
||||
#define ESP_TASK_PRIO_MAX 25
|
||||
#define ESP_TASKD_EVENT_PRIO 5
|
||||
#define ESP_TASKD_EVENT_STACK 1024
|
@ -16,7 +16,9 @@
|
||||
typedef void *SemaphoreHandle_t;
|
||||
typedef void *QueueHandle_t;
|
||||
typedef void *TaskHandle_t;
|
||||
typedef void *EventGroupHandle_t;
|
||||
typedef uint32_t TickType_t;
|
||||
typedef TickType_t EventBits_t;
|
||||
|
||||
typedef void (*TaskFunction_t)( void * );
|
||||
typedef unsigned int UBaseType_t;
|
||||
@ -30,6 +32,5 @@ typedef int BaseType_t;
|
||||
|
||||
#define pdMS_TO_TICKS(tick) (tick)
|
||||
|
||||
uint32_t esp_get_free_heap_size(void);
|
||||
uint32_t esp_random(void);
|
||||
void vTaskSuspendAll(void);
|
@ -0,0 +1,6 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
@ -3,11 +3,4 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
//
|
||||
// Created by david on 1/13/23.
|
||||
//
|
||||
|
||||
#ifndef _QUEUE_H_
|
||||
#define _QUEUE_H_
|
||||
|
||||
#endif //_QUEUE_H_
|
||||
#pragma once
|
@ -3,11 +3,4 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
//
|
||||
// Created by david on 1/13/23.
|
||||
//
|
||||
|
||||
#ifndef _SEMAPHR_H_
|
||||
#define _SEMAPHR_H_
|
||||
|
||||
#endif //_SEMAPHR_H_
|
||||
#pragma once
|
@ -7,6 +7,10 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TaskHandle_t TaskHandle_t
|
||||
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
|
||||
|
||||
@ -26,7 +30,7 @@ BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID);
|
||||
|
||||
void xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
|
||||
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
|
||||
|
||||
TickType_t xTaskGetTickCount( void );
|
||||
|
||||
@ -35,11 +39,16 @@ void vQueueDelete( QueueHandle_t xQueue );
|
||||
QueueHandle_t xSemaphoreCreateBinary(void);
|
||||
|
||||
QueueHandle_t xSemaphoreCreateMutex(void);
|
||||
QueueHandle_t xSemaphoreCreateRecursiveMutex(void);
|
||||
|
||||
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
|
||||
|
||||
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
|
||||
|
||||
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue);
|
||||
|
||||
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask );
|
||||
|
||||
void vTaskDelete(TaskHandle_t *task);
|
||||
|
||||
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
|
||||
@ -48,3 +57,27 @@ QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
|
||||
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
|
||||
|
||||
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
|
||||
|
||||
void vTaskSuspend(void *task);
|
||||
|
||||
EventGroupHandle_t xEventGroupCreate( void );
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear );
|
||||
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait );
|
||||
|
||||
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup);
|
||||
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet );
|
||||
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
109
common_components/linux_compat/freertos/osal/event_group.cpp
Normal file
109
common_components/linux_compat/freertos/osal/event_group.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include "osal_api.h"
|
||||
|
||||
|
||||
class SignalGroup {
|
||||
|
||||
struct SignalGroupInternal {
|
||||
std::condition_variable notify;
|
||||
std::mutex m;
|
||||
uint32_t flags{ 0 };
|
||||
};
|
||||
|
||||
using SignalT = std::unique_ptr<SignalGroupInternal>;
|
||||
|
||||
public:
|
||||
|
||||
void set(uint32_t bits)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
event_group->flags |= bits;
|
||||
event_group->notify.notify_all();
|
||||
}
|
||||
|
||||
uint32_t get()
|
||||
{
|
||||
return event_group->flags;
|
||||
}
|
||||
|
||||
void clear(uint32_t bits)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
event_group->flags &= ~bits;
|
||||
event_group->notify.notify_all();
|
||||
}
|
||||
|
||||
// waiting for all and clearing if set
|
||||
bool wait(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] {
|
||||
if ((flags & event_group->flags) == flags)
|
||||
{
|
||||
event_group->flags &= ~flags;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// waiting for any bit, not clearing them
|
||||
bool wait_any(uint32_t flags, uint32_t time_ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(event_group->m);
|
||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; });
|
||||
}
|
||||
|
||||
private:
|
||||
SignalT event_group{std::make_unique<SignalGroupInternal>()};
|
||||
};
|
||||
|
||||
|
||||
void *osal_signal_create()
|
||||
{
|
||||
auto signal = new SignalGroup;
|
||||
return signal;
|
||||
}
|
||||
|
||||
void osal_signal_delete(void *s)
|
||||
{
|
||||
delete static_cast<SignalGroup *>(s);
|
||||
}
|
||||
|
||||
uint32_t osal_signal_clear(void *s, uint32_t bits)
|
||||
{
|
||||
auto signal = static_cast<SignalGroup *>(s);
|
||||
signal->clear(bits);
|
||||
return signal->get();
|
||||
}
|
||||
|
||||
uint32_t osal_signal_set(void *s, uint32_t bits)
|
||||
{
|
||||
auto signal = static_cast<SignalGroup *>(s);
|
||||
signal->set(bits);
|
||||
return signal->get();
|
||||
}
|
||||
|
||||
uint32_t osal_signal_get(void *s)
|
||||
{
|
||||
auto signal = static_cast<SignalGroup *>(s);
|
||||
return signal->get();
|
||||
}
|
||||
|
||||
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout)
|
||||
{
|
||||
auto signal = static_cast<SignalGroup *>(s);
|
||||
if (all) {
|
||||
signal->wait(flags, timeout);
|
||||
} else {
|
||||
signal->wait_any(flags, timeout);
|
||||
}
|
||||
return signal->get();
|
||||
}
|
31
common_components/linux_compat/freertos/osal/mutex.cpp
Normal file
31
common_components/linux_compat/freertos/osal/mutex.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include "osal_api.h"
|
||||
|
||||
void *osal_mutex_create()
|
||||
{
|
||||
auto mut = new std::recursive_mutex();
|
||||
return mut;
|
||||
}
|
||||
|
||||
void osal_mutex_delete(void *mut)
|
||||
{
|
||||
delete static_cast<std::recursive_mutex *>(mut);
|
||||
}
|
||||
|
||||
void osal_mutex_take(void *m)
|
||||
{
|
||||
auto mut = static_cast<std::recursive_mutex *>(m);
|
||||
mut->lock();
|
||||
}
|
||||
|
||||
void osal_mutex_give(void *m)
|
||||
{
|
||||
auto mut = static_cast<std::recursive_mutex *>(m);
|
||||
mut->unlock();
|
||||
}
|
34
common_components/linux_compat/freertos/osal/osal_api.h
Normal file
34
common_components/linux_compat/freertos/osal/osal_api.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// queue api
|
||||
void *osal_queue_create(void);
|
||||
void osal_queue_delete(void *q);
|
||||
bool osal_queue_send(void *q, uint8_t *data, size_t len);
|
||||
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms);
|
||||
|
||||
// mutex api
|
||||
void *osal_mutex_create(void);
|
||||
void osal_mutex_delete(void *m);
|
||||
void osal_mutex_take(void *m);
|
||||
void osal_mutex_give(void *m);
|
||||
|
||||
// event groups
|
||||
void *osal_signal_create(void);
|
||||
void osal_signal_delete(void *s);
|
||||
uint32_t osal_signal_clear(void *s, uint32_t bits);
|
||||
uint32_t osal_signal_set(void *s, uint32_t bits);
|
||||
uint32_t osal_signal_get(void *s);
|
||||
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
78
common_components/linux_compat/freertos/osal/queue.cpp
Normal file
78
common_components/linux_compat/freertos/osal/queue.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include "osal_api.h"
|
||||
|
||||
template <class T>
|
||||
class Queue {
|
||||
public:
|
||||
void send(std::unique_ptr<T> t)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
q.push(std::move(t));
|
||||
c.notify_one();
|
||||
}
|
||||
|
||||
std::unique_ptr<T> receive(std::chrono::milliseconds ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (q.empty()) {
|
||||
if (c.wait_for(lock, ms) == std::cv_status::timeout) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
std::unique_ptr<T> val = std::move(q.front());
|
||||
q.pop();
|
||||
return val;
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<std::unique_ptr<T>> q{};
|
||||
mutable std::mutex m{};
|
||||
std::condition_variable c{};
|
||||
};
|
||||
|
||||
using item_t = std::vector<uint8_t>;
|
||||
|
||||
void *osal_queue_create(void)
|
||||
{
|
||||
auto *q = new Queue<item_t>();
|
||||
return q;
|
||||
}
|
||||
|
||||
void osal_queue_delete(void *q)
|
||||
{
|
||||
auto *queue = static_cast<Queue<item_t> *>(q);
|
||||
delete (queue);
|
||||
}
|
||||
|
||||
bool osal_queue_send(void *q, uint8_t *data, size_t len)
|
||||
{
|
||||
auto v = std::make_unique<item_t>(len);
|
||||
v->assign(data, data + len);
|
||||
auto queue = static_cast<Queue<item_t> *>(q);
|
||||
queue->send(std::move(v));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms)
|
||||
{
|
||||
auto queue = static_cast<Queue<item_t> *>(q);
|
||||
auto v = queue->receive(std::chrono::milliseconds(ms));
|
||||
if (v != nullptr) {
|
||||
memcpy(data, (void *)v->data(), len);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
@ -6,8 +6,8 @@ Asio is a cross-platform C++ library, see https://think-async.com/Asio/. It prov
|
||||
|
||||
## Examples
|
||||
|
||||
Get started with example test :example:`examples <examples/..>`:
|
||||
Get started with examples [examples](https://github.com/espressif/esp-protocols/tree/master/components/asio/examples)
|
||||
|
||||
## Documentation
|
||||
|
||||
* View the full [html documentation](https://espressif.github.io/esp-protocols/asio/index.html)
|
||||
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
|
||||
|
@ -1,26 +0,0 @@
|
||||
build-docs --target esp32 --language en
|
||||
|
||||
cp -rf _build/en/esp32/html .
|
||||
rm -rf _build __pycache__
|
||||
|
||||
# Modifes some version and target fields of index.html
|
||||
echo "<script type="text/javascript">
|
||||
window.onload =(function() {
|
||||
var myAnchor = document.getElementById('version-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'latest';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
var myAnchor = document.getElementById('target-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'all targets';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
})();
|
||||
</script>" >> html/index.html
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
4
components/asio/examples/socks4/main/idf_component.yml
Normal file
4
components/asio/examples/socks4/main/idf_component.yml
Normal file
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
8
components/esp_modem/.cz.yaml
Normal file
8
components/esp_modem/.cz.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(modem): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_modem
|
||||
tag_format: modem-v$version
|
||||
version: 1.0.1
|
||||
version_files:
|
||||
- idf_component.yml
|
@ -1,5 +1,46 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/modem-v1.0.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Support UART clean build with IDF v5.2 ([e620eb5](https://github.com/espressif/esp-protocols/commit/e620eb5))
|
||||
- enable code coverage publishing to github pages ([4910e89](https://github.com/espressif/esp-protocols/commit/4910e89))
|
||||
- fix esp_modem build issue ([ab94566](https://github.com/espressif/esp-protocols/commit/ab94566))
|
||||
- Example to use 1.0.0 ([afb6930](https://github.com/espressif/esp-protocols/commit/afb6930))
|
||||
- Changelog to correctly pick references ([423e965](https://github.com/espressif/esp-protocols/commit/423e965))
|
||||
|
||||
### Updated
|
||||
|
||||
- docs(esp_modem): updated documents to show missed topics ([0534853](https://github.com/espressif/esp-protocols/commit/0534853))
|
||||
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/modem-v1.0.0)
|
||||
|
||||
### Major changes
|
||||
|
||||
- Enable DTE to redefine on_read() and write(cmd) directly ([DTE to support sending and receiving raw data](https://github.com/espressif/esp-protocols/commit/bf114d3))
|
||||
|
||||
### Features
|
||||
|
||||
- Add mqtt example in AT-only mode ([7547267](https://github.com/espressif/esp-protocols/commit/7547267))
|
||||
- DTE to support sending and receiving raw data ([bf114d3](https://github.com/espressif/esp-protocols/commit/bf114d3))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Changelog to correctly pick references ([c59e330](https://github.com/espressif/esp-protocols/commit/c59e330))
|
||||
- AT-only example: support MQTT over TLS on BG96 ([31d4323](https://github.com/espressif/esp-protocols/commit/31d4323))
|
||||
- AT-only example: initial adaptation to BG96 ([ceedcfc](https://github.com/espressif/esp-protocols/commit/ceedcfc))
|
||||
- warning in ap-2-pppos example when using lwip/napt ([bf32e45](https://github.com/espressif/esp-protocols/commit/bf32e45))
|
||||
- trigger PR jobs based on labels ([13eca12](https://github.com/espressif/esp-protocols/commit/13eca12))
|
||||
- Reintroduce missing CHANGELOGs ([200cbb3](https://github.com/espressif/esp-protocols/commit/200cbb3), [#235](https://github.com/espressif/esp-protocols/issues/235))
|
||||
- Use default UART CLK source ([d0814bf](https://github.com/espressif/esp-protocols/commit/d0814bf), [#241](https://github.com/espressif/esp-protocols/issues/241))
|
||||
- run CI build job for all targets ([a089e0d](https://github.com/espressif/esp-protocols/commit/a089e0d))
|
||||
|
||||
### Updated
|
||||
|
||||
- Update examples for USB v1.1 ([610372f](https://github.com/espressif/esp-protocols/commit/610372f))
|
||||
|
||||
|
||||
## [0.1.28](https://github.com/espressif/esp-protocols/commits/01c26c8)
|
||||
|
||||
|
@ -47,15 +47,3 @@ if(${target} STREQUAL "linux")
|
||||
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES COMPILE_FLAGS -Wno-format)
|
||||
endif()
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
|
||||
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
endif()
|
||||
|
@ -16,6 +16,17 @@ menu "esp-modem"
|
||||
in command mode might come fragmented in rare cases so might need to retry
|
||||
AT commands.
|
||||
|
||||
config ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
bool "Use inflatable buffer in DCE"
|
||||
default n
|
||||
help
|
||||
If enabled we will process the ongoing AT command by growing the current
|
||||
buffer (if we've run out the preconfigured buffer).
|
||||
If disabled, we simply report a failure.
|
||||
Use this if additional allocation is not a problem and you need to reliably process
|
||||
all commands, usually with sporadically longer responses than the configured buffer.
|
||||
Could be also used to defragment AT replies in CMUX mode if CMUX_DEFRAGMENT_PAYLOAD=n
|
||||
|
||||
config ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP
|
||||
int "Delay in ms to wait before creating another virtual terminal"
|
||||
default 0
|
||||
|
@ -17,5 +17,5 @@ Get started with one of the examples:
|
||||
|
||||
## Documentation
|
||||
|
||||
* Continue with esp-modem [brief overview](docs/README.md)
|
||||
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
|
||||
* Continue with esp-modem [brief overview](https://github.com/espressif/esp-protocols/tree/master/docs/esp_modem/en/README.rst)
|
||||
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
|
||||
|
@ -1,92 +0,0 @@
|
||||
# ESP MODEM
|
||||
|
||||
This component is used to communicate with modems in the command mode (using AT commands), as well as the data mode
|
||||
(over PPPoS protocol).
|
||||
The modem device is modeled with a DCE (Data Communication Equipment) object, which is composed of:
|
||||
* DTE (Data Terminal Equipment), which abstracts the terminal (currently only UART implemented).
|
||||
* PPP Netif representing a network interface communicating with the DTE using PPP protocol.
|
||||
* Module abstracting the specific device model and its commands.
|
||||
|
||||
```
|
||||
+-----+
|
||||
| DTE |--+
|
||||
+-----+ | +-------+
|
||||
+-->| DCE |
|
||||
+-------+ | |o--- set_mode(command/data)
|
||||
| Module|--->| |
|
||||
+-------+ | |o--- send_commands
|
||||
+->| |
|
||||
+------+ | +-------+
|
||||
| PPP |--+
|
||||
| netif|------------------> network events
|
||||
+------+
|
||||
```
|
||||
|
||||
## Modem components
|
||||
### DCE
|
||||
|
||||
This is the basic operational unit of the esp_modem component, abstracting a specific module in software,
|
||||
which is basically configured by
|
||||
* the I/O communication media (UART), defined by the DTE configuration
|
||||
* the specific command library supported by the device model, defined with the module type
|
||||
* network interface configuration (PPPoS config in lwip)
|
||||
|
||||
After the object is created, the application interaction with the DCE is in
|
||||
* issuing specific commands to the modem
|
||||
* switching between data and command mode
|
||||
|
||||
### DTE
|
||||
Is an abstraction of the physical interface connected to the modem. Current implementation supports only UART
|
||||
|
||||
### PPP netif
|
||||
|
||||
Is used to attach the specific network interface to a network communication protocol used by the modem. Currently implementation supports only PPPoS protocol.
|
||||
|
||||
### Module
|
||||
|
||||
Abstraction of the specific modem device. Currently the component supports SIM800, BG96, SIM7600.
|
||||
|
||||
## Use cases
|
||||
|
||||
Users interact with the esp-modem using the DCE's interface, to basically
|
||||
* Switch between command and data mode to connect to the internet via cellular network.
|
||||
* Send various commands to the device (e.g. send SMS)
|
||||
|
||||
The applications typically register handlers for network events to receive notification on the network availability and
|
||||
IP address changes.
|
||||
|
||||
Common use cases of the esp-modem are also listed as the examples:
|
||||
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
|
||||
* `examples/modem_console` -- is an example to exercise all possible module commands in a console application.
|
||||
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
|
||||
|
||||
## Extensibility
|
||||
|
||||
### CMUX
|
||||
|
||||
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
|
||||
after creating multiple virtual terminals, designating some of them solely to data mode, others solely to command mode.
|
||||
|
||||
### DTE's
|
||||
|
||||
Currently, we support only UART (and USB as a preview feature), but modern modules support other communication interfaces, such as USB, SPI.
|
||||
|
||||
### Other devices
|
||||
|
||||
Adding a new device is a must-have requirement for the esp-modem component. Different modules support different commands,
|
||||
or some commands might have a different implementation. Adding a new device means to provide a new implementation
|
||||
as a class derived from `GenericModule`, where we could add new commands or modify the existing ones.
|
||||
|
||||
## Configuration
|
||||
|
||||
Modem abstraction is configurable both compile-time and run-time.
|
||||
|
||||
### Component Kconfig
|
||||
|
||||
Compile-time configuration is provided using menuconfig. Please check the description for the CMUX mode configuration options.
|
||||
|
||||
### Runtime configuration
|
||||
|
||||
Is defined using standard configuration structures for `DTE` and `DCE` objects separately. Please find documentation of
|
||||
* :cpp:class:`esp_modem_dte_config_t`
|
||||
* :cpp:class:`esp_modem_dce_config_t`
|
@ -1,23 +0,0 @@
|
||||
# Cleanup the generated html
|
||||
rm -rf html
|
||||
|
||||
# Cleans example and test build dirs (to reduce the component size before upload)
|
||||
rm -rf ../examples/ap_to_pppos/build/ ../examples/simple_cmux_client/build/ ../examples/pppos_client/build/ ../examples/linux_modem/build/ ../examples/modem_console/build ../test/host_test/build/ ../test/target/build/
|
||||
|
||||
# Generate C++ API header of the DCE
|
||||
cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_dce.hpp
|
||||
|
||||
# Generate C API header of the modem_api.h
|
||||
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_api_commands.h
|
||||
|
||||
# RST with links to C++ API
|
||||
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
|
||||
|
||||
# Run doxygen
|
||||
doxygen
|
||||
|
||||
# Generate the docs
|
||||
python -u -m sphinx.cmd.build -b html . html
|
||||
|
||||
# Cleanup the doxygen xml's and temporary headers
|
||||
rm -rf xml esp_modem_api_commands.h esp_modem_dce.hpp cxx_api_links.rst
|
@ -1,36 +0,0 @@
|
||||
# Internal design
|
||||
|
||||
## Design decisions
|
||||
|
||||
* Use C++ with additional C API
|
||||
|
||||
* Use exceptions
|
||||
- Use macro wrapper over `try-catch` blocks when exceptions off (use `abort()` if `THROW()`)
|
||||
|
||||
* Initializes and allocates in the constructor (might throw)
|
||||
- easier code with exceptions ON, with exceptions OFF alloc/init failures are not treated as runtime error (program aborts)
|
||||
- break down long initialization in constructor into more private methods
|
||||
|
||||
* Implements different devices using inheritance from `GenericModule`, which is the most general implementation of a common modem
|
||||
- Internally uses templates with device specialization (modeled as `DCE<SpecificModule>`) which could be used as well for some special cases,
|
||||
such as implantation of a minimal device (ModuleIf), add new AT commands (oOnly in compile time), or using the Module with DTE only (no DCE, no Netif) for sending AT commands without network
|
||||
|
||||
## DCE collaboration model
|
||||
|
||||
The diagram describes how the DCE class collaborates with DTE, PPP and the device abstraction
|
||||
|
||||

|
||||
|
||||
## Terminal inheritance
|
||||
|
||||
Terminal is a class which can read or write data, and can handle callbacks when data are available. UART specialization
|
||||
is provided implementing these method using the uart driver.
|
||||
|
||||
## CMUX terminal
|
||||
|
||||
The below diagram depicts the idea of using CMUX terminal mode using the CMuxInstance class which is a terminal
|
||||
(it implements the basic read/write methods) interfacing arbitrary number of virtual terminals,
|
||||
but at the same time it is also composed of CMux class, which consumes the original terminal and uses its read/write methods
|
||||
to multiplex the terminal.
|
||||
|
||||

|
@ -14,6 +14,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
@ -1,6 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(modem-console)
|
||||
|
@ -1,5 +1,6 @@
|
||||
idf_component_register(SRCS "modem_console_main.cpp"
|
||||
"console_helper.cpp"
|
||||
"my_module_dce.cpp"
|
||||
"httpget_handle.c"
|
||||
"ping_handle.c"
|
||||
REQUIRES console esp_http_client nvs_flash
|
||||
|
@ -16,17 +16,18 @@ menu "Example Configuration"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
default EXAMPLE_MODEM_DEVICE_SHINY
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
config EXAMPLE_MODEM_DEVICE_SHINY
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SHINY"
|
||||
help
|
||||
SHINY is a GSM/GPRS module.
|
||||
It supports SHINY.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM800
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM800"
|
||||
help
|
||||
SIMCom SIM800L is a GSM/GPRS module.
|
||||
@ -36,10 +37,12 @@ menu "Example Configuration"
|
||||
help
|
||||
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7000
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM7000"
|
||||
help
|
||||
SIM7000 is a Multi-Band LTE-FDD and GSM/GPRS/EDGE module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7070
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM7070"
|
||||
help
|
||||
SIM7070 is Multi-Band CAT M and NB IoT module.
|
||||
@ -47,6 +50,11 @@ menu "Example Configuration"
|
||||
bool "SIM7600"
|
||||
help
|
||||
SIM7600 is a Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module.
|
||||
config EXAMPLE_MODEM_DEVICE_A7670
|
||||
depends on EXAMPLE_SERIAL_CONFIG_USB
|
||||
bool "A7670"
|
||||
help
|
||||
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MODEM_PPP_APN
|
||||
|
@ -3,10 +3,10 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^0.1.20"
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.0.0"
|
||||
version: "^1.1.0"
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3]"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -48,9 +48,9 @@
|
||||
} } while (0)
|
||||
|
||||
/**
|
||||
* Please update the default APN name here (this could be updated runtime)
|
||||
* Default APN name is taken from Kconfig (this could be updated runtime)
|
||||
*/
|
||||
#define DEFAULT_APN "my_apn"
|
||||
#define DEFAULT_APN CONFIG_EXAMPLE_MODEM_PPP_APN
|
||||
|
||||
#define GPIO_OUTPUT_PWRKEY (gpio_num_t)CONFIG_EXAMPLE_MODEM_PWRKEY_PIN
|
||||
#define GPIO_OUTPUT_PIN_SEL (1ULL<<GPIO_OUTPUT_PWRKEY)
|
||||
@ -89,6 +89,14 @@ void wakeup_modem(void)
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
|
||||
command_result handle_urc(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
|
||||
return command_result::TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
static RTC_RODATA_ATTR char apn_rtc[20] = DEFAULT_APN;
|
||||
@ -122,19 +130,19 @@ extern "C" void app_main(void)
|
||||
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
|
||||
auto uart_dte = create_uart_dte(&dte_config);
|
||||
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_SHINY == 1
|
||||
#if defined(CONFIG_EXAMPLE_MODEM_DEVICE_SHINY)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SHINY module...");
|
||||
auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
|
||||
auto dce = create_BG96_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM800)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module...");
|
||||
auto dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7000 module...");
|
||||
auto dce = create_SIM7000_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070 == 1
|
||||
#elif defined(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070)
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7070 module...");
|
||||
auto dce = create_SIM7070_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
@ -149,7 +157,18 @@ extern "C" void app_main(void)
|
||||
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
|
||||
while (1) {
|
||||
exit_signal.clear(1);
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296, 2); // VID, PID and interface num of BG96 modem
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_BG96_USB_CONFIG();
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_SIM7600_USB_CONFIG();
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the A7670 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_A7670_USB_CONFIG();
|
||||
#else
|
||||
#error USB modem not selected
|
||||
#endif
|
||||
const esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_USB_CONFIG(usb_config);
|
||||
ESP_LOGI(TAG, "Waiting for USB device connection...");
|
||||
auto dte = create_usb_dte(&dte_config);
|
||||
@ -159,7 +178,13 @@ extern "C" void app_main(void)
|
||||
exit_signal.set(1);
|
||||
}
|
||||
});
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
std::unique_ptr<DCE> dce = create_BG96_dce(&dce_config, dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1 || CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
|
||||
std::unique_ptr<DCE> dce = create_SIM7600_dce(&dce_config, dte, esp_netif);
|
||||
#else
|
||||
#error USB modem not selected
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error Invalid serial connection to modem.
|
||||
@ -286,8 +311,9 @@ extern "C" void app_main(void)
|
||||
|
||||
const ConsoleCommand GetOperatorName("get_operator_name", "reads the operator name", no_args, [&](ConsoleCommand * c) {
|
||||
std::string operator_name;
|
||||
int act;
|
||||
ESP_LOGI(TAG, "Reading operator name...");
|
||||
CHECK_ERR(dce->get_operator_name(operator_name), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
|
||||
CHECK_ERR(dce->get_operator_name(operator_name, act), ESP_LOGI(TAG, "OK. Operator name: %s", operator_name.c_str()));
|
||||
});
|
||||
|
||||
const struct GenericCommandArgs {
|
||||
@ -339,6 +365,20 @@ extern "C" void app_main(void)
|
||||
ESP_LOGI(TAG, "Resetting the module...");
|
||||
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
||||
});
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_SHINY
|
||||
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
static int cnt = 0;
|
||||
if (++cnt % 2) {
|
||||
ESP_LOGI(TAG, "Adding URC handler");
|
||||
dce->set_on_read(handle_urc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "URC removed");
|
||||
dce->set_on_read(nullptr);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
#endif
|
||||
|
||||
const struct SetApn {
|
||||
SetApn(): apn(STR1, nullptr, nullptr, "<apn>", "APN (Access Point Name)") {}
|
||||
CommandArgs apn;
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* Modem console example: Custom DCE
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "generate/esp_modem_command_declare.inc"
|
||||
#include "my_module_dce.hpp"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
//
|
||||
// Define preprocessor's forwarding to dce_commands definitions
|
||||
//
|
||||
|
||||
// Helper macros to handle multiple arguments of declared API
|
||||
#define ARGS0
|
||||
#define ARGS1 , p1
|
||||
#define ARGS2 , p1 , p2
|
||||
#define ARGS3 , p1 , p2 , p3
|
||||
#define ARGS4 , p1 , p2 , p3, p4
|
||||
#define ARGS5 , p1 , p2 , p3, p4, p5
|
||||
#define ARGS6 , p1 , p2 , p3, p4, p5, p6
|
||||
|
||||
#define _ARGS(x) ARGS ## x
|
||||
#define ARGS(x) _ARGS(x)
|
||||
|
||||
#define CMD_OK (1)
|
||||
#define CMD_FAIL (2)
|
||||
|
||||
//
|
||||
// Repeat all declarations and forward to the AT commands defined in esp_modem::dce_commands:: namespace
|
||||
//
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
|
||||
return_type Shiny::DCE::name(__VA_ARGS__) { return esp_modem::dce_commands::name(this ARGS(arg_nr) ); }
|
||||
|
||||
DECLARE_ALL_COMMAND_APIS(return_type name(...) )
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return Shiny::Factory::create(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Definition of the command API, which makes the Shiny::DCE "command-able class"
|
||||
* @param cmd Command to send
|
||||
* @param got_line Recv line callback
|
||||
* @param time_ms timeout in ms
|
||||
* @param separator line break separator
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
command_result Shiny::DCE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
if (!handling_urc) {
|
||||
return dte->command(cmd, got_line, time_ms, separator);
|
||||
}
|
||||
handle_cmd = got_line;
|
||||
signal.clear(CMD_OK | CMD_FAIL);
|
||||
esp_modem::DTE_Command command{cmd};
|
||||
dte->write(command);
|
||||
signal.wait_any(CMD_OK | CMD_FAIL, time_ms);
|
||||
handle_cmd = nullptr;
|
||||
if (signal.is_any(CMD_OK)) {
|
||||
return esp_modem::command_result::OK;
|
||||
}
|
||||
if (signal.is_any(CMD_FAIL)) {
|
||||
return esp_modem::command_result::FAIL;
|
||||
}
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle received data
|
||||
*
|
||||
* @param data Data received from the device
|
||||
* @param len Length of the data
|
||||
* @return standard command return code (OK|FAIL|TIMEOUT)
|
||||
*/
|
||||
command_result Shiny::DCE::handle_data(uint8_t *data, size_t len)
|
||||
{
|
||||
if (std::memchr(data, '\n', len)) {
|
||||
if (handle_urc) {
|
||||
handle_urc(data, len);
|
||||
}
|
||||
if (handle_cmd) {
|
||||
auto ret = handle_cmd(data, len);
|
||||
if (ret == esp_modem::command_result::TIMEOUT) {
|
||||
return command_result::TIMEOUT;
|
||||
}
|
||||
if (ret == esp_modem::command_result::OK) {
|
||||
signal.set(CMD_OK);
|
||||
}
|
||||
if (ret == esp_modem::command_result::FAIL) {
|
||||
signal.set(CMD_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return command_result::TIMEOUT;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -11,6 +11,8 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
|
||||
@ -28,13 +30,84 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace Shiny {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
class DCE : public esp_modem::DCE_T<MyShinyModem>, public CommandableIf {
|
||||
public:
|
||||
using DCE_T<MyShinyModem>::DCE_T;
|
||||
|
||||
command_result
|
||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms) override
|
||||
{
|
||||
return command(cmd, got_line, time_ms, '\n');
|
||||
}
|
||||
|
||||
command_result
|
||||
command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms, const char separator) override;
|
||||
|
||||
int write(uint8_t *data, size_t len) override
|
||||
{
|
||||
return dte->write(data, len);
|
||||
}
|
||||
|
||||
void on_read(got_line_cb on_data) override
|
||||
{
|
||||
return dte->on_read(on_data);
|
||||
}
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(__VA_ARGS__);
|
||||
|
||||
DECLARE_ALL_COMMAND_APIS(forwards name(...))
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
void set_on_read(esp_modem::got_line_cb on_read_cb)
|
||||
{
|
||||
if (on_read_cb == nullptr) {
|
||||
handling_urc = false;
|
||||
handle_urc = nullptr;
|
||||
dte->on_read(nullptr);
|
||||
return;
|
||||
}
|
||||
handle_urc = std::move(on_read_cb);
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->handle_data(data, len);
|
||||
return command_result::TIMEOUT;
|
||||
});
|
||||
handling_urc = true;
|
||||
}
|
||||
|
||||
private:
|
||||
got_line_cb handle_urc{nullptr};
|
||||
got_line_cb handle_cmd{nullptr};
|
||||
SignalGroup signal;
|
||||
bool handling_urc {false};
|
||||
|
||||
command_result handle_data(uint8_t *data, size_t len);
|
||||
|
||||
};
|
||||
|
||||
class Factory: public ::esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
|
||||
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<MyShinyModem, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace Shiny
|
||||
|
||||
/**
|
||||
* @brief Helper create method which employs the DCE factory for creating DCE objects templated by a custom module
|
||||
* @return unique pointer of the resultant DCE
|
||||
*/
|
||||
std::unique_ptr<esp_modem::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::unique_ptr<Shiny::DCE> create_shiny_dce(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return esp_modem::dce_factory::Factory::build_unique<MyShinyModem>(config, std::move(dte), netif);
|
||||
}
|
||||
esp_netif_t *netif);
|
||||
|
@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(modem_tcp_client)
|
10
components/esp_modem/examples/modem_tcp_client/README.md
Normal file
10
components/esp_modem/examples/modem_tcp_client/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Modem TCP client
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)
|
||||
|
||||
### Supported IDF versions
|
||||
|
||||
This example is supported from IDF `v4.4`.
|
@ -0,0 +1,11 @@
|
||||
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||
set(device_srcs sock_commands_bg96.cpp)
|
||||
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
||||
set(device_srcs sock_commands_sim7600.cpp)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "modem_client.cpp"
|
||||
"sock_dce.cpp"
|
||||
"${device_srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
@ -0,0 +1,96 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
config EXAMPLE_MODEM_DEVICE_BG96
|
||||
bool "BG96"
|
||||
help
|
||||
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7600
|
||||
bool "SIM7600"
|
||||
help
|
||||
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MODEM_APN
|
||||
string "Set MODEM APN"
|
||||
default "internet"
|
||||
help
|
||||
Set APN (Access Point Name), a logical name to choose data network
|
||||
|
||||
menu "UART Configuration"
|
||||
config EXAMPLE_MODEM_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
default 25
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
default 26
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RTS_PIN
|
||||
int "RTS Pin Number"
|
||||
default 27
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RTS.
|
||||
|
||||
config EXAMPLE_MODEM_UART_CTS_PIN
|
||||
int "CTS Pin Number"
|
||||
default 23
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART CTS.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE
|
||||
int "UART Event Task Stack Size"
|
||||
range 2000 6000
|
||||
default 2048
|
||||
help
|
||||
Stack size of UART event task.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY
|
||||
int "UART Event Task Priority"
|
||||
range 3 22
|
||||
default 5
|
||||
help
|
||||
Priority of UART event task.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE
|
||||
int "UART Event Queue Size"
|
||||
range 10 40
|
||||
default 30
|
||||
help
|
||||
Length of UART event queue.
|
||||
|
||||
config EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE
|
||||
int "UART Pattern Queue Size"
|
||||
range 10 40
|
||||
default 20
|
||||
help
|
||||
Length of UART pattern queue.
|
||||
|
||||
config EXAMPLE_MODEM_UART_TX_BUFFER_SIZE
|
||||
int "UART TX Buffer Size"
|
||||
range 256 2048
|
||||
default 512
|
||||
help
|
||||
Buffer size of UART TX buffer.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RX_BUFFER_SIZE
|
||||
int "UART RX Buffer Size"
|
||||
range 256 2048
|
||||
default 1024
|
||||
help
|
||||
Buffer size of UART RX buffer.
|
||||
endmenu
|
||||
|
||||
endmenu
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* PPPoS Client Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define BROKER_URL "mqtt.eclipseprojects.io"
|
||||
|
||||
static const char *TAG = "modem_client";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
static const int CONNECT_BIT = BIT0;
|
||||
static const int GOT_DATA_BIT = BIT2;
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
|
||||
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
|
||||
esp_mqtt_client_handle_t client = event->client;
|
||||
int msg_id;
|
||||
switch ((esp_mqtt_event_id_t)event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/esp-pppos", 0);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "esp32-pppos", 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
xEventGroupSetBits(event_group, GOT_DATA_BIT);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
|
||||
/* Init and register system/core components */
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
event_group = xEventGroupCreate();
|
||||
|
||||
/* Configure and create the UART DTE */
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
/* setup UART specific configuration based on kconfig options */
|
||||
dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
|
||||
dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
|
||||
dte_config.uart_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
|
||||
dte_config.uart_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
|
||||
dte_config.uart_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
|
||||
dte_config.uart_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
|
||||
dte_config.uart_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
|
||||
dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE * 2;
|
||||
dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
|
||||
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
|
||||
|
||||
auto dte = esp_modem::create_uart_dte(&dte_config);
|
||||
assert(dte);
|
||||
|
||||
/* Configure the DCE */
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
|
||||
|
||||
/* create the DCE and initialize network manually (using AT commands) */
|
||||
auto dce = sock_dce::create(&dce_config, std::move(dte));
|
||||
if (!dce->init_network()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
|
||||
dce->init_sock(8883);
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
#else
|
||||
mqtt_config.uri = "mqtt://127.0.0.1";
|
||||
mqtt_config.message_retransmit_timeout = 10000;
|
||||
#endif
|
||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
||||
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
if (!dce->start(BROKER_URL, 8883)) {
|
||||
ESP_LOGE(TAG, "Failed to start DCE");
|
||||
return;
|
||||
}
|
||||
while (1) {
|
||||
while (dce->perform_sock()) {
|
||||
ESP_LOGV(TAG, "...performing");
|
||||
}
|
||||
ESP_LOGE(TAG, "Loop exit.. retrying");
|
||||
// handle disconnections errors
|
||||
if (!dce->init_network()) {
|
||||
ESP_LOGE(TAG, "Failed to reinit network");
|
||||
return;
|
||||
}
|
||||
if (!dce->start("test.mosquitto.org", 1883)) {
|
||||
ESP_LOGI(TAG, "Network reinitialized, retrying");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "cxx_include/esp_modem_types.hpp"
|
||||
#include "socket_commands.inc"
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
//using namespace esp_modem;
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);
|
||||
|
||||
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include "sock_commands.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
|
||||
static const char *TAG = "sock_commands";
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
command_result net_open(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string out;
|
||||
auto ret = dce_commands::generic_get_string(t, "AT+QISTATE?\r", out, 1000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
if (out.find("+QISTATE: 0") != std::string::npos) {
|
||||
ESP_LOGV(TAG, "%s", out.data() );
|
||||
ESP_LOGD(TAG, "Already there");
|
||||
return command_result::FAIL;
|
||||
} else if (out.empty()) {
|
||||
return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000);
|
||||
}
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result net_close(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
|
||||
esp_modem::Task::Delay(1000);
|
||||
return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000);
|
||||
}
|
||||
|
||||
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string ip_open = R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
|
||||
auto ret = dce_commands::generic_command(t, ip_open, "+QIOPEN: 0,0", "ERROR", timeout);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "%s Failed", __func__ );
|
||||
return ret;
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_close(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
|
||||
}
|
||||
|
||||
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
assert(0); // Remove when fix done
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result tcp_recv(CommandableIf *t, uint8_t *data, size_t len, size_t &out_len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
assert(0); // Remove when fix done
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result get_ip(CommandableIf *t, std::string &ip)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string out;
|
||||
auto ret = dce_commands::generic_get_string(t, "AT+QIACT?\r", out, 5000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
auto pos = out.find("+QIACT: 1");
|
||||
auto property = 0;
|
||||
while (pos != std::string::npos) {
|
||||
// Looking for: +QIACT: <contextID>,<context_state>,<context_type>,<IP_address>
|
||||
if (property++ == 3) { // ip is after 3rd comma (as a 4rd property of QIACT string)
|
||||
ip = out.substr(++pos);
|
||||
// strip quotes if present
|
||||
auto quote1 = ip.find('"');
|
||||
auto quote2 = ip.rfind('"');
|
||||
if (quote1 != std::string::npos && quote2 != std::string::npos) {
|
||||
ip = ip.substr(quote1 + 1, quote2 - 1);
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
pos = out.find(',', ++pos);
|
||||
}
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
} // sock_commands
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
{
|
||||
const int MIN_MESSAGE = 6;
|
||||
size_t actual_len = 0;
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+QIRD: ";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == nullptr) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
|
||||
if (next_nl == nullptr) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "cannot convert");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
|
||||
if (actual_len == 0) {
|
||||
ESP_LOGD(TAG, "no data received");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (actual_len > buffer_size) {
|
||||
ESP_LOGE(TAG, "TOO BIG");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
recv_data = next_nl + 1;
|
||||
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
|
||||
if (actual_len > first_data_len) {
|
||||
::send(sock, recv_data, first_data_len, 0);
|
||||
data_to_recv = actual_len - first_data_len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
::send(sock, recv_data, actual_len, 0);
|
||||
} else if (data_to_recv > len) { // continue sending
|
||||
::send(sock, recv_data, len, 0);
|
||||
data_to_recv -= len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
||||
::send(sock, recv_data, data_to_recv, 0);
|
||||
actual_len = data_to_recv;
|
||||
}
|
||||
|
||||
// "OK" after the data
|
||||
char *last_pos = nullptr;
|
||||
if (actual_len + 1 + 2 /* OK */ > len) {
|
||||
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
|
||||
if (last_pos == nullptr || last_pos[1] != 'K') {
|
||||
data_to_recv = 0;
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
if (last_pos != nullptr && (char *)data + len - last_pos - 2 > MIN_MESSAGE) {
|
||||
// check for async replies after the Recv header
|
||||
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos);
|
||||
check_async_replies(status::RECEIVING, response);
|
||||
}
|
||||
// check if some other data?
|
||||
start_receiving(0);
|
||||
data_to_recv = 0;
|
||||
return ret::OK;
|
||||
}
|
||||
|
||||
|
||||
Responder::ret Responder::send(uint8_t *data, size_t len)
|
||||
{
|
||||
if (send_stat < 3) {
|
||||
if (memchr(data, '>', len) == NULL) {
|
||||
if (send_stat++ < 2) {
|
||||
return Responder::ret::NEED_MORE_DATA;
|
||||
}
|
||||
ESP_LOGE(TAG, "Missed >");
|
||||
return ret::FAIL;
|
||||
}
|
||||
auto written = dte->write(&buffer[0], data_to_send);
|
||||
if (written != data_to_send) {
|
||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||
return ret::FAIL;
|
||||
}
|
||||
data_to_send = 0;
|
||||
send_stat = 3;
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::send(std::string_view response)
|
||||
{
|
||||
if (send_stat == 3) {
|
||||
if (response.find("SEND OK") != std::string::npos) {
|
||||
send_cmd("AT+QISEND=0,0\r");
|
||||
send_stat++;
|
||||
return ret::IN_PROGRESS;
|
||||
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Sending buffer full");
|
||||
return ret::FAIL;
|
||||
} else if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to sent");
|
||||
return ret::FAIL;
|
||||
}
|
||||
} else if (send_stat == 4) {
|
||||
constexpr std::string_view head = "+QISEND: ";
|
||||
if (response.find(head) != std::string::npos) {
|
||||
// Parsing +QISEND: <total_send_length>,<ackedbytes>,<unackedbytes>
|
||||
size_t head_pos = response.find(head);
|
||||
response = response.substr(head_pos + head.size());
|
||||
int pos, property = 0;
|
||||
int total = 0, ack = 0, unack = 0;
|
||||
while ((pos = response.find(',')) != std::string::npos) {
|
||||
auto next_comma = (char *)memchr(response.data(), ',', response.size());
|
||||
|
||||
// extract value
|
||||
size_t value;
|
||||
if (std::from_chars(response.data(), next_comma, value).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "cannot convert");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
switch (property++) {
|
||||
case 0: total = value;
|
||||
break;
|
||||
case 1: ack = value;
|
||||
break;
|
||||
default:
|
||||
return ret::FAIL;
|
||||
}
|
||||
response = response.substr(pos + 1);
|
||||
}
|
||||
if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (ack < total) {
|
||||
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
|
||||
if (total - ack > 64) {
|
||||
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
|
||||
return ret::NEED_MORE_TIME;
|
||||
}
|
||||
}
|
||||
send_stat = 0;
|
||||
return ret::OK;
|
||||
} else if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to check sending");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to open");
|
||||
return ret::FAIL;
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
} else if (response.find("+QIRD: ") != std::string::npos) {
|
||||
static constexpr std::string_view head = "+QIRD: ";
|
||||
size_t head_pos = response.find(head);
|
||||
// Parsing +QIURC: <total_receive_length>,<have_read_length>,<unread_length>
|
||||
response = response.substr(head_pos + head.size());
|
||||
int next_cr = response.find('\r');
|
||||
if (next_cr != std::string::npos) {
|
||||
response = response.substr(next_cr - 2, next_cr);
|
||||
if (response.find(",0") != std::string::npos) {
|
||||
ESP_LOGV(TAG, "Receiving done");
|
||||
} else {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
}
|
||||
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||
{
|
||||
if (state == status::SENDING) {
|
||||
return send(data, len);
|
||||
}
|
||||
if (state == status::RECEIVING) {
|
||||
return recv(data, len);
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
status Responder::pending()
|
||||
{
|
||||
send_cmd("AT+QISEND=0,0\r");
|
||||
return status::SENDING;
|
||||
}
|
||||
|
||||
|
||||
} // sock_dce
|
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include "sock_commands.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
|
||||
static const char *TAG = "sock_commands";
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
command_result net_open(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string response;
|
||||
auto ret = dce_commands::generic_get_string(term, "AT+NETOPEN?\r", response, 1000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s", response.data() );
|
||||
if (response.find("+NETOPEN: 1") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "Already there");
|
||||
ret = command_result::OK;
|
||||
} else if (response.find("+NETOPEN: 0") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "Need to setup");
|
||||
ret = dce_commands::generic_command(term, "AT+NETOPEN\r", "+NETOPEN: 1", "+NETOPEN: 0", 10000);
|
||||
} else {
|
||||
return command_result::FAIL;
|
||||
}
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
return dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result net_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return dce_commands::generic_command(term, "AT+NETCLOSE\r", "+NETCLOSE:", "ERROR", 30000);
|
||||
}
|
||||
|
||||
command_result tcp_open(CommandableIf *term, const std::string &host, int port, int timeout)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
auto ret = dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 50000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Setting Rx mode failed!");
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string ip_open = R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
|
||||
ret = dce_commands::generic_command(term, ip_open, "+CIPOPEN: 0,0", "ERROR", timeout);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "%s Failed", __func__ );
|
||||
return ret;
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return dce_commands::generic_command(term, "AT+CIPCLOSE=0\r", "+CIPCLOSE:", "ERROR", 10000);
|
||||
}
|
||||
|
||||
command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string send = "AT+CIPSEND=0," + std::to_string(len) + "\r";
|
||||
auto ret = term->command(send, [&](uint8_t *data, size_t len) {
|
||||
std::string_view response((char *)data, len);
|
||||
ESP_LOGI(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find('>') != std::string::npos) {
|
||||
return command_result::OK;
|
||||
}
|
||||
return command_result::TIMEOUT;
|
||||
}, 50000, '>');
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = command_result::TIMEOUT;
|
||||
ESP_LOGW(TAG, "Before setting...");
|
||||
term->on_read([&ret](uint8_t *cmd_data, size_t cmd_len) {
|
||||
std::string_view response((char *)cmd_data, cmd_len);
|
||||
ESP_LOGW(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
|
||||
|
||||
if (response.find("+CIPSEND:") != std::string::npos) {
|
||||
ret = command_result::OK;
|
||||
} else if (response.find("ERROR") != std::string::npos) {
|
||||
ret = command_result::FAIL;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
ESP_LOGW(TAG, "Before writing...");
|
||||
auto written = term->write(data, len);
|
||||
if (written != len) {
|
||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||
return command_result::FAIL;
|
||||
}
|
||||
uint8_t ctrl_z = '\x1A';
|
||||
term->write(&ctrl_z, 1);
|
||||
int count = 0;
|
||||
while (ret == command_result::TIMEOUT && count++ < 1000 ) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
term->on_read(nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
command_result tcp_recv(CommandableIf *term, uint8_t *data, size_t len, size_t &out_len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string out;
|
||||
auto ret = dce_commands::generic_get_string(term, "AT+CIPRXGET=4,0\r", out);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
constexpr std::string_view pattern = "+CIPRXGET: 4,0,";
|
||||
if (out.find(pattern) == std::string::npos) {
|
||||
|
||||
return command_result::FAIL;
|
||||
}
|
||||
size_t data_len;
|
||||
if (std::from_chars(out.data() + pattern.size(), out.data() + out.size(), data_len).ec == std::errc::invalid_argument) {
|
||||
return command_result::FAIL;
|
||||
}
|
||||
ESP_LOGD(TAG, "size=%d", data_len);
|
||||
if (data_len == 0) {
|
||||
out_len = data_len;
|
||||
return command_result::OK;
|
||||
}
|
||||
return term->command("AT+CIPRXGET=2,0,100\r", [&](uint8_t *cmd_data, size_t cmd_len) {
|
||||
char pattern[] = "+CIPRXGET: 2,0,";
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, cmd_data, cmd_len, ESP_LOG_DEBUG);
|
||||
char *pos = strstr((char *)cmd_data, pattern);
|
||||
if (pos == nullptr) {
|
||||
return command_result::FAIL;
|
||||
}
|
||||
auto p1 = memchr(pos + sizeof(pattern) - 1, ',', 4);
|
||||
if (p1 == nullptr) {
|
||||
return command_result::FAIL;
|
||||
}
|
||||
*(char *)p1 = '\0';
|
||||
size_t actual_len = atoi(pos + sizeof(pattern) - 1);
|
||||
ESP_LOGD(TAG, "actual len=%d", actual_len);
|
||||
|
||||
pos = strchr((char *)p1 + 1, '\n');
|
||||
if (pos == nullptr) {
|
||||
ESP_LOGE(TAG, "not found");
|
||||
return command_result::FAIL;
|
||||
}
|
||||
if (actual_len > len) {
|
||||
ESP_LOGE(TAG, "TOO BIG");
|
||||
return command_result::FAIL;
|
||||
}
|
||||
out_len = actual_len;
|
||||
memcpy(data, pos + 1, actual_len);
|
||||
pos = strstr((char *)pos + 1 + actual_len, "OK");
|
||||
if (pos == nullptr) {
|
||||
ESP_LOGE(TAG, "ok NOT FOUND");
|
||||
return command_result::FAIL;
|
||||
}
|
||||
return command_result::OK;
|
||||
}, 50000);
|
||||
}
|
||||
|
||||
command_result get_ip(CommandableIf *term, std::string &ip)
|
||||
{
|
||||
std::string resp;
|
||||
auto ret = dce_commands::generic_get_string(term, "AT+IPADDR\r", resp, 5000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
ip = resp;
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result set_rx_mode(CommandableIf *term, int mode)
|
||||
{
|
||||
return dce_commands::generic_command(term, "AT+CIPRXGET=" + std::to_string(mode) + "\r", "OK", "ERROR", 5000);
|
||||
}
|
||||
|
||||
} // sock_commands
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
{
|
||||
const int MIN_MESSAGE = 6;
|
||||
size_t actual_len = 0;
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == nullptr) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (head_pos - (char *)data > MIN_MESSAGE) {
|
||||
// check for async replies before the Recv header
|
||||
std::string_view response((char *)data, head_pos - (char *)data);
|
||||
check_async_replies(status::RECEIVING, response);
|
||||
}
|
||||
|
||||
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
||||
if (next_comma == nullptr) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "cannot convert");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */);
|
||||
if (next_nl == nullptr) {
|
||||
ESP_LOGE(TAG, "not found");
|
||||
return ret::FAIL;
|
||||
}
|
||||
if (actual_len > buffer_size) {
|
||||
ESP_LOGE(TAG, "TOO BIG");
|
||||
return ret::FAIL;
|
||||
}
|
||||
size_t total_len = 0;
|
||||
if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "cannot convert");
|
||||
return ret::FAIL;
|
||||
}
|
||||
read_again = (total_len > 0);
|
||||
recv_data = next_nl + 1;
|
||||
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
|
||||
if (actual_len > first_data_len) {
|
||||
::send(sock, recv_data, first_data_len, 0);
|
||||
data_to_recv = actual_len - first_data_len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
::send(sock, recv_data, actual_len, 0);
|
||||
} else if (data_to_recv > len) { // continue sending
|
||||
::send(sock, recv_data, len, 0);
|
||||
data_to_recv -= len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
||||
::send(sock, recv_data, data_to_recv, 0);
|
||||
actual_len = data_to_recv;
|
||||
}
|
||||
|
||||
// "OK" after the data
|
||||
char *last_pos = nullptr;
|
||||
if (actual_len + 1 + 2 /* OK */ > len) {
|
||||
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
|
||||
if (last_pos == nullptr || last_pos[1] != 'K') {
|
||||
data_to_recv = 0;
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
if (last_pos != nullptr && (char *)data + len - last_pos - 2 > MIN_MESSAGE) {
|
||||
// check for async replies after the Recv header
|
||||
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2);
|
||||
check_async_replies(status::RECEIVING, response);
|
||||
}
|
||||
data_to_recv = 0;
|
||||
if (read_again) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
}
|
||||
return ret::OK;
|
||||
}
|
||||
|
||||
Responder::ret Responder::send(uint8_t *data, size_t len)
|
||||
{
|
||||
if (send_stat == 0) {
|
||||
if (memchr(data, '>', len) == NULL) {
|
||||
ESP_LOGE(TAG, "Missed >");
|
||||
return ret::FAIL;
|
||||
}
|
||||
auto written = dte->write(&buffer[0], data_to_send);
|
||||
if (written != data_to_send) {
|
||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||
return ret::FAIL;
|
||||
}
|
||||
data_to_send = 0;
|
||||
uint8_t ctrl_z = '\x1A';
|
||||
dte->write(&ctrl_z, 1);
|
||||
send_stat++;
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::send(std::string_view response)
|
||||
{
|
||||
if (send_stat == 1) {
|
||||
if (response.find("+CIPSEND:") != std::string::npos) {
|
||||
send_stat = 0;
|
||||
return ret::OK;
|
||||
}
|
||||
if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to sent");
|
||||
send_stat = 0;
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to open");
|
||||
return ret::FAIL;
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find("+CIPRXGET: 1") != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
|
||||
}
|
||||
|
||||
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||
{
|
||||
if (state == status::SENDING) {
|
||||
return send(data, len);
|
||||
}
|
||||
if (state == status::RECEIVING) {
|
||||
return recv(data, len);
|
||||
}
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
status Responder::pending()
|
||||
{
|
||||
return status::PENDING;
|
||||
}
|
||||
|
||||
|
||||
} // sock_dce
|
326
components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp
Normal file
326
components/esp_modem/examples/modem_tcp_client/main/sock_dce.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
|
||||
#include "sock_dce.hpp"
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
{
|
||||
if (listen_sock == -1) {
|
||||
ESP_LOGE(TAG, "Listening socket not ready");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (sock == -1) { // no active socket, need to accept one first
|
||||
return accept_sock();
|
||||
}
|
||||
|
||||
// we have a socket, let's check the status
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 500000,
|
||||
};
|
||||
if (state == status::PENDING) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
state = at.pending();
|
||||
return true;
|
||||
}
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sock, &fdset);
|
||||
FD_SET(data_ready_fd, &fdset);
|
||||
int s = select(std::max(sock, data_ready_fd) + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s == 0) {
|
||||
ESP_LOGV(TAG, "perform select timeout...");
|
||||
return true;
|
||||
} else if (s < 0) {
|
||||
ESP_LOGE(TAG, "select error %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (FD_ISSET(sock, &fdset) && !sock_to_at()) {
|
||||
return false;
|
||||
}
|
||||
if (FD_ISSET(data_ready_fd, &fdset) && !at_to_sock()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_DATA:
|
||||
return;
|
||||
case Responder::ret::IN_PROGRESS:
|
||||
break;
|
||||
case Responder::ret::NEED_MORE_TIME:
|
||||
state = status::PENDING;
|
||||
return;
|
||||
}
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_TIME:
|
||||
state = status::PENDING;
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_DATA:
|
||||
case Responder::ret::IN_PROGRESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DCE::close_sock()
|
||||
{
|
||||
if (sock > 0) {
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
while (net_close() != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to close network");
|
||||
return;
|
||||
}
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
bool DCE::at_to_sock()
|
||||
{
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %x", data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE(TAG, "Unexpected state %d", state);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE(TAG, "Unexpected state %d", state);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "read error %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
} else if (len == 0) {
|
||||
ESP_LOGE(TAG, "EOF %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
at.start_sending(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::accept_sock()
|
||||
{
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 500000,
|
||||
};
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(listen_sock, &fdset);
|
||||
int s = select(listen_sock + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s > 0 && FD_ISSET(listen_sock, &fdset)) {
|
||||
struct sockaddr_in source_addr = {};
|
||||
socklen_t addr_len = sizeof(source_addr);
|
||||
sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGD(TAG, "Socket accepted!");
|
||||
FD_ZERO(&fdset);
|
||||
return true;
|
||||
} else if (s == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DCE::init_sock(int port)
|
||||
{
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
if (listen_sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
// inet_aton("127.0.0.1", &addr.sin_addr);
|
||||
|
||||
int err = bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DCE::start(std::string host, int port)
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
tcp_close();
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->perform_at(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
return false;
|
||||
}
|
||||
state = status::CONNECTING;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::init_network()
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
while (sync() != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to sync up");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "Modem in sync");
|
||||
i = 0;
|
||||
while (setup_data_mode() != true) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to setup pdp/data");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "PDP configured");
|
||||
i = 0;
|
||||
while (net_open() != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to open network");
|
||||
return false;
|
||||
}
|
||||
net_close();
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "Network opened");
|
||||
i = 0;
|
||||
std::string ip_addr;
|
||||
while (get_ip(ip_addr) != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed obtain an IP address");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class Factory: public ::esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
|
||||
{
|
||||
return esp_modem::dce_factory::Factory::build_module_T<DCE, std::unique_ptr<DCE>>(config, std::move(dte));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
|
||||
{
|
||||
return Factory::create(config, std::move(dte));
|
||||
}
|
||||
|
||||
// Helper macros to handle multiple arguments of declared API
|
||||
#define ARGS0
|
||||
#define ARGS1 , p1
|
||||
#define ARGS2 , p1 , p2
|
||||
#define ARGS3 , p1 , p2 , p3
|
||||
|
||||
#define EXPAND_ARGS(x) ARGS ## x
|
||||
#define ARGS(x) EXPAND_ARGS(x)
|
||||
|
||||
//
|
||||
// Repeat all declarations and forward to the AT commands defined in ::sock_commands namespace
|
||||
//
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
|
||||
esp_modem::return_type DCE::name(__VA_ARGS__) { return sock_commands::name(dte.get() ARGS(arg_nr) ); }
|
||||
|
||||
DECLARE_SOCK_COMMANDS(return_type name(...) )
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
} // namespace sock_dce
|
108
components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp
Normal file
108
components/esp_modem/examples/modem_tcp_client/main/sock_dce.hpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "socket_commands.inc"
|
||||
#include "sock_commands.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
|
||||
enum class status {
|
||||
IDLE,
|
||||
CONNECTING,
|
||||
SENDING,
|
||||
RECEIVING,
|
||||
FAILED,
|
||||
PENDING
|
||||
};
|
||||
|
||||
class Responder {
|
||||
public:
|
||||
enum class ret {
|
||||
OK, FAIL, IN_PROGRESS, NEED_MORE_DATA, NEED_MORE_TIME
|
||||
};
|
||||
Responder(int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg):
|
||||
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
|
||||
ret process_data(status state, uint8_t *data, size_t len);
|
||||
ret check_async_replies(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
bool start_connecting(std::string host, int port);
|
||||
status pending();
|
||||
uint8_t *get_buf()
|
||||
{
|
||||
return &buffer[0];
|
||||
}
|
||||
size_t get_buf_len()
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
private:
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
ret recv(uint8_t *data, size_t len);
|
||||
ret send(uint8_t *data, size_t len);
|
||||
ret send(std::string_view response);
|
||||
ret connect(std::string_view response);
|
||||
void send_cmd(std::string_view command)
|
||||
{
|
||||
dte->write((uint8_t *) command.begin(), command.size());
|
||||
}
|
||||
std::array<uint8_t, buffer_size> buffer;
|
||||
size_t data_to_recv = 0;
|
||||
bool read_again = false;
|
||||
int &sock;
|
||||
int &data_ready_fd;
|
||||
int send_stat = 0;
|
||||
size_t data_to_send = 0;
|
||||
std::shared_ptr<esp_modem::DTE> &dte;
|
||||
};
|
||||
|
||||
class DCE : public ::esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
public:
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(__VA_ARGS__);
|
||||
|
||||
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
bool init_network();
|
||||
bool start(std::string host, int port);
|
||||
|
||||
void init_sock(int port);
|
||||
|
||||
bool perform_sock();
|
||||
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
|
||||
void close_sock();
|
||||
bool accept_sock();
|
||||
bool sock_to_at();
|
||||
bool at_to_sock();
|
||||
|
||||
void perform_at(uint8_t *data, size_t len);
|
||||
|
||||
status state{status::IDLE};
|
||||
static constexpr uint8_t IDLE = 1;
|
||||
Responder at{sock, data_ready_fd, dte};
|
||||
int sock {-1};
|
||||
int listen_sock {-1};
|
||||
int data_ready_fd {-1};
|
||||
};
|
||||
|
||||
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte);
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "generate/esp_modem_command_declare_helper.inc"
|
||||
|
||||
#define DECLARE_SOCK_COMMANDS(...) \
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, 3, STRING_IN(p1, host), INT_IN(p2, port), INT_IN(p3, timeout)) \
|
||||
\
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, 1, STRING_OUT(p1, addr)) \
|
||||
\
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, 1, INT_IN(p1, mode))
|
@ -15,12 +15,12 @@ menu "Example Configuration"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM800
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM800"
|
||||
help
|
||||
SIMCom SIM800L is a GSM/GPRS module.
|
||||
@ -30,10 +30,12 @@ menu "Example Configuration"
|
||||
help
|
||||
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7000
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM7000"
|
||||
help
|
||||
SIM7000 is a Multi-Band LTE-FDD and GSM/GPRS/EDGE module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7070
|
||||
depends on EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "SIM7070"
|
||||
help
|
||||
SIM7070 is Multi-Band CAT M and NB IoT module.
|
||||
@ -41,6 +43,11 @@ menu "Example Configuration"
|
||||
bool "SIM7600"
|
||||
help
|
||||
SIM7600 is a Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module.
|
||||
config EXAMPLE_MODEM_DEVICE_A7670
|
||||
depends on EXAMPLE_SERIAL_CONFIG_USB
|
||||
bool "A7670"
|
||||
help
|
||||
A7670X is Multi-Band LTE-FDD/LTE-TDD/GSM/GPRS/EDGE module.
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MODEM_PPP_APN
|
||||
|
@ -3,10 +3,10 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^0.1.23"
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.0.0"
|
||||
version: "^1.1.0"
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3]"
|
||||
|
@ -208,14 +208,28 @@ void app_main(void)
|
||||
|
||||
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
|
||||
while (1) {
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296, 2); // VID, PID and interface num of BG96 modem
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_BG96_USB_CONFIG();
|
||||
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_BG96;
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_SIM7600_USB_CONFIG();
|
||||
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_SIM7600;
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_A7670 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the A7670 module...");
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_A7670_USB_CONFIG();
|
||||
esp_modem_dce_device_t usb_dev_type = ESP_MODEM_DCE_SIM7600;
|
||||
#else
|
||||
#error USB modem not selected
|
||||
#endif
|
||||
const esp_modem_dte_config_t dte_usb_config = ESP_MODEM_DTE_DEFAULT_USB_CONFIG(usb_config);
|
||||
ESP_LOGI(TAG, "Waiting for USB device connection...");
|
||||
esp_modem_dce_t *dce = esp_modem_new_dev_usb(ESP_MODEM_DCE_BG96, &dte_usb_config, &dce_config, esp_netif);
|
||||
esp_modem_dce_t *dce = esp_modem_new_dev_usb(usb_dev_type, &dte_usb_config, &dce_config, esp_netif);
|
||||
assert(dce);
|
||||
esp_modem_set_error_cb(dce, usb_terminal_error_handler);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // Although the DTE should be ready after USB enumeration, sometimes it fails to respond without this delay
|
||||
ESP_LOGI(TAG, "Modem connected, waiting 10 seconds for boot...");
|
||||
vTaskDelay(pdMS_TO_TICKS(10000)); // Give DTE some time to boot
|
||||
|
||||
#else
|
||||
#error Invalid serial connection to modem.
|
||||
|
@ -1,6 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
|
||||
|
@ -175,6 +175,9 @@ extern "C" void app_main(void)
|
||||
#endif
|
||||
assert(dce);
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
StatusHandler handler;
|
||||
|
||||
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
if (command_result::OK != dce->set_flow_control(2, 2)) {
|
||||
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
|
||||
@ -215,8 +218,6 @@ extern "C" void app_main(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
StatusHandler handler;
|
||||
if (!handler.wait_for(StatusHandler::IP_Event, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
|
||||
return;
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: "0.1.28"
|
||||
version: "1.0.1"
|
||||
description: esp modem
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
|
||||
dependencies:
|
||||
|
@ -22,6 +22,19 @@ namespace dce_commands {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Generic AT command to be send with pass and fail phrases
|
||||
*
|
||||
* @param t Commandable object (anything that can accept commands)
|
||||
* @param command Command to be sent do the commandable object
|
||||
* @param pass_phrase String to be present in the reply to pass this command
|
||||
* @param fail_phrase String to be present in the reply to fail this command
|
||||
* @param timeout_ms Timeout in ms
|
||||
*/
|
||||
command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
const std::string &pass_phrase,
|
||||
const std::string &fail_phrase, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc
|
||||
*/
|
||||
|
@ -69,6 +69,10 @@ public:
|
||||
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
|
||||
}
|
||||
|
||||
explicit Creator(std::shared_ptr<DTE> dte): dte(std::move(dte)), device(nullptr), netif(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~Creator()
|
||||
{
|
||||
if (device != nullptr) {
|
||||
@ -232,6 +236,14 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T_Module>
|
||||
static std::unique_ptr<DCE> create_unique_dce_from(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
private:
|
||||
ModemType m;
|
||||
|
||||
|
@ -29,6 +29,13 @@ class CMux;
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct DTE_Command {
|
||||
DTE_Command(const std::string &cmd): data((uint8_t *)cmd.c_str()), len(cmd.length()) {}
|
||||
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* DTE (Data Terminal Equipment) class
|
||||
*/
|
||||
@ -54,7 +61,9 @@ public:
|
||||
* @param len Data len to write
|
||||
* @return number of bytes written
|
||||
*/
|
||||
int write(uint8_t *data, size_t len);
|
||||
int write(uint8_t *data, size_t len) override;
|
||||
|
||||
int write(DTE_Command command);
|
||||
|
||||
/**
|
||||
* @brief Reading from the underlying terminal
|
||||
@ -70,6 +79,15 @@ public:
|
||||
*/
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
|
||||
|
||||
/**
|
||||
* @brief Sets read callback for manual command processing
|
||||
* Note that this API also locks the command API, which can only be used
|
||||
* after you remove the callback by dte->on_read(nullptr)
|
||||
*
|
||||
* @param on_data Function to be called when a command response is available
|
||||
*/
|
||||
void on_read(got_line_cb on_data) override;
|
||||
|
||||
/**
|
||||
* @brief Sets DTE error callback
|
||||
* @param f Function to be called on DTE error
|
||||
@ -111,10 +129,10 @@ protected:
|
||||
}
|
||||
friend class Scoped<DTE>; /*!< Declaring "Scoped<DTE> lock(dte)" locks this instance */
|
||||
private:
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
|
||||
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
|
||||
void exit_cmux_internal(); /*!< Cleanup CMUX */
|
||||
|
||||
Lock internal_lock{}; /*!< Locks DTE operations */
|
||||
unique_buffer buffer; /*!< DTE buffer */
|
||||
@ -122,9 +140,73 @@ private:
|
||||
std::shared_ptr<Terminal> primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */
|
||||
std::shared_ptr<Terminal> secondary_term; /*!< Secondary terminal for this DTE */
|
||||
modem_mode mode; /*!< DTE operation mode */
|
||||
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||
command_result result; /*!< Command result of the currently exectuted command */
|
||||
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
/**
|
||||
* @brief Implements an extra buffer that is used to capture partial reads from underlying terminals
|
||||
* when we run out of the standard buffer
|
||||
*/
|
||||
struct extra_buffer {
|
||||
extra_buffer(): buffer(nullptr) {}
|
||||
~extra_buffer()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
std::vector<uint8_t> *buffer;
|
||||
size_t consumed{0};
|
||||
void grow(size_t need_size);
|
||||
void deflate()
|
||||
{
|
||||
grow(0);
|
||||
consumed = 0;
|
||||
}
|
||||
[[nodiscard]] uint8_t *begin() const
|
||||
{
|
||||
return &buffer->at(0);
|
||||
}
|
||||
[[nodiscard]] uint8_t *current() const
|
||||
{
|
||||
return &buffer->at(0) + consumed;
|
||||
}
|
||||
} inflatable;
|
||||
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
|
||||
/**
|
||||
* @brief Set internal command callbacks to the underlying terminal.
|
||||
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
|
||||
*/
|
||||
void set_command_callbacks();
|
||||
|
||||
/**
|
||||
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
|
||||
*/
|
||||
struct command_cb {
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
got_line_cb got_line; /*!< Supplied command callback */
|
||||
Lock line_lock{}; /*!< Command callback locking mechanism */
|
||||
char separator{}; /*!< Command reply separator (end of line/processing unit) */
|
||||
command_result result{}; /*!< Command return code */
|
||||
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
|
||||
bool wait_for_line(uint32_t time_ms); /*!< Waiting for command processing */
|
||||
void set(got_line_cb l, char s = '\n') /*!< Sets the command callback atomically */
|
||||
{
|
||||
Scoped<Lock> lock(line_lock);
|
||||
if (l) {
|
||||
// if we set the line callback, we have to reset the signal and the result
|
||||
signal.clear(GOT_LINE);
|
||||
result = command_result::TIMEOUT;
|
||||
}
|
||||
got_line = std::move(l);
|
||||
separator = s;
|
||||
}
|
||||
void give_up() /*!< Reports other than timeout error when processing replies (out of buffer) */
|
||||
{
|
||||
result = command_result::FAIL;
|
||||
signal.set(GOT_LINE);
|
||||
}
|
||||
} command_cb; /*!< Command callback utility class */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -54,9 +54,10 @@ public:
|
||||
*/
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void receive(uint8_t *data, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len);
|
||||
|
||||
static esp_err_t esp_modem_post_attach(esp_netif_t *esp_netif, void *args);
|
||||
|
@ -80,6 +80,9 @@ public:
|
||||
*/
|
||||
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
|
||||
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) = 0;
|
||||
|
||||
virtual int write(uint8_t *data, size_t len) = 0;
|
||||
virtual void on_read(got_line_cb on_data) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -11,36 +11,11 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#ifndef _ESP_MODEM_COMMAND_DECLARE_INC_
|
||||
#define _ESP_MODEM_COMMAND_DECLARE_INC_
|
||||
#include "generate/esp_modem_command_declare_helper.inc"
|
||||
|
||||
|
||||
// Parameters
|
||||
// * handle different parameters for C++ and C API
|
||||
// * make parameter unique names, so they could be easily referenced and forwarded
|
||||
#define _ARG(param, name) param
|
||||
#define INT_IN(param, name) int _ARG(param, name)
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#define STRING_IN(param, name) const std::string& _ARG(param, name)
|
||||
#define STRING_OUT(param, name) std::string& _ARG(param, name)
|
||||
#define BOOL_IN(param, name) const bool _ARG(param, name)
|
||||
#define BOOL_OUT(param, name) bool& _ARG(param, name)
|
||||
#define INT_OUT(param, name) int& _ARG(param, name)
|
||||
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
|
||||
|
||||
#define STRUCT_OUT(struct_name, p1) struct_name& p1
|
||||
#else
|
||||
#define STRING_IN(param, name) const char* _ARG(param, name)
|
||||
#define STRING_OUT(param, name) char* _ARG(param, name)
|
||||
#define BOOL_IN(param, name) const bool _ARG(param, name)
|
||||
#define BOOL_OUT(param, name) bool* _ARG(param, name)
|
||||
#define INT_OUT(param, name) int* _ARG(param, name)
|
||||
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
|
||||
#define STRUCT_OUT(struct_name, p1) esp_modem_ ## struct_name ## _t* p1
|
||||
#endif
|
||||
|
||||
#define DECLARE_ALL_COMMAND_APIS(...) \
|
||||
/**
|
||||
* @brief Sends the initial AT sequence to sync up with the device
|
||||
@ -331,5 +306,3 @@ public:
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _ESP_MODEM_COMMAND_DECLARE_INC_
|
||||
|
@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
// Parameters
|
||||
// * handle different parameters for C++ and C API
|
||||
// * make parameter unique names, so they could be easily referenced and forwarded
|
||||
#define _ARG(param, name) param
|
||||
#define INT_IN(param, name) int _ARG(param, name)
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#define STRING_IN(param, name) const std::string& _ARG(param, name)
|
||||
#define STRING_OUT(param, name) std::string& _ARG(param, name)
|
||||
#define BOOL_IN(param, name) const bool _ARG(param, name)
|
||||
#define BOOL_OUT(param, name) bool& _ARG(param, name)
|
||||
#define INT_OUT(param, name) int& _ARG(param, name)
|
||||
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
|
||||
|
||||
#define STRUCT_OUT(struct_name, p1) struct_name& p1
|
||||
#else
|
||||
#define STRING_IN(param, name) const char* _ARG(param, name)
|
||||
#define STRING_OUT(param, name) char* _ARG(param, name)
|
||||
#define BOOL_IN(param, name) const bool _ARG(param, name)
|
||||
#define BOOL_OUT(param, name) bool* _ARG(param, name)
|
||||
#define INT_OUT(param, name) int* _ARG(param, name)
|
||||
#define INTEGER_LIST_IN(param, name) const int* _ARG(param, name)
|
||||
#define STRUCT_OUT(struct_name, p1) esp_modem_ ## struct_name ## _t* p1
|
||||
#endif
|
@ -21,10 +21,17 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
|
||||
netif.stop();
|
||||
auto signal = std::make_shared<SignalGroup>();
|
||||
std::weak_ptr<SignalGroup> weak_signal = signal;
|
||||
dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
|
||||
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
|
||||
// post the transitioning data to the network layers if it contains PPP SOF marker
|
||||
if (memchr(data, 0x7E, len))
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (PPP)", data, len, ESP_LOG_DEBUG);
|
||||
netif.receive(data, len);
|
||||
}
|
||||
// treat the transitioning data as a textual message if it contains a newline char
|
||||
if (memchr(data, '\n', len))
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
|
||||
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
|
||||
std::string_view response((char *) data, len);
|
||||
for (auto &it : pass)
|
||||
@ -39,6 +46,7 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
|
||||
});
|
||||
netif.wait_until_ppp_exits();
|
||||
if (!signal->wait(1, 2000)) {
|
||||
dte.set_read_cb(nullptr);
|
||||
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -17,53 +17,118 @@ static const size_t dte_default_buffer_size = 1000;
|
||||
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
|
||||
buffer(config->dte_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(std::unique_ptr<Terminal> terminal):
|
||||
buffer(dte_default_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
|
||||
buffer(config->dte_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
|
||||
mode(modem_mode::DUAL_MODE) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
|
||||
buffer(dte_default_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
|
||||
mode(modem_mode::DUAL_MODE) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
void DTE::set_command_callbacks()
|
||||
{
|
||||
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
Scoped<Lock> l(command_cb.line_lock);
|
||||
if (command_cb.got_line == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (data) {
|
||||
// For terminals which post data directly with the callback (CMUX)
|
||||
// we cannot defragment unless we allocate, but
|
||||
// we'll try to process the data on the actual buffer
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
if (inflatable.consumed != 0) {
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
std::memcpy(inflatable.current(), data, len);
|
||||
data = inflatable.begin();
|
||||
}
|
||||
if (command_cb.process_line(data, inflatable.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
// at this point we're sure that the data processing hasn't finished,
|
||||
// and we have to grow the inflatable buffer (if enabled) or give up
|
||||
if (inflatable.consumed == 0) {
|
||||
inflatable.grow(len);
|
||||
std::memcpy(inflatable.begin(), data, len);
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
if (command_cb.process_line(data, 0, len)) {
|
||||
return true;
|
||||
}
|
||||
// cannot inflate and the processing hasn't finishes in the first iteration -> report a failure
|
||||
command_cb.give_up();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
// data == nullptr: Terminals which request users to read current data
|
||||
// we're able to use DTE's buffer to defragment it; as long as we consume less that the buffer size
|
||||
if (buffer.size > buffer.consumed) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
|
||||
if (command_cb.process_line(data, buffer.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
buffer.consumed += len;
|
||||
return false;
|
||||
}
|
||||
// we have used the entire DTE's buffer, need to use the inflatable buffer to continue
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
if (inflatable.consumed == 0) {
|
||||
inflatable.grow(buffer.size + len);
|
||||
std::memcpy(inflatable.begin(), buffer.get(), buffer.size);
|
||||
inflatable.consumed = buffer.size;
|
||||
} else {
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
}
|
||||
len = primary_term->read(inflatable.current(), len);
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
// cannot inflate -> report a failure
|
||||
command_cb.give_up();
|
||||
return true;
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l(internal_lock);
|
||||
result = command_result::TIMEOUT;
|
||||
signal.clear(GOT_LINE);
|
||||
primary_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) {
|
||||
if (!data) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
|
||||
} else {
|
||||
buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment
|
||||
}
|
||||
if (memchr(data + buffer.consumed, separator, len)) {
|
||||
result = got_line(data, buffer.consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
signal.set(GOT_LINE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
buffer.consumed += len;
|
||||
return false;
|
||||
});
|
||||
Scoped<Lock> l1(internal_lock);
|
||||
command_cb.set(got_line, separator);
|
||||
primary_term->write((uint8_t *)command.c_str(), command.length());
|
||||
auto got_lf = signal.wait(GOT_LINE, time_ms);
|
||||
if (got_lf && result == command_result::TIMEOUT) {
|
||||
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
command_cb.wait_for_line(time_ms);
|
||||
command_cb.set(nullptr);
|
||||
buffer.consumed = 0;
|
||||
primary_term->set_read_cb(nullptr);
|
||||
return result;
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
inflatable.deflate();
|
||||
#endif
|
||||
return command_cb.result;
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
|
||||
@ -76,12 +141,22 @@ bool DTE::exit_cmux()
|
||||
if (!cmux_term->deinit()) {
|
||||
return false;
|
||||
}
|
||||
exit_cmux_internal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DTE::exit_cmux_internal()
|
||||
{
|
||||
if (!cmux_term) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ejected = cmux_term->detach();
|
||||
// return the ejected terminal and buffer back to this DTE
|
||||
primary_term = std::move(ejected.first);
|
||||
buffer = std::move(ejected.second);
|
||||
secondary_term = primary_term;
|
||||
return true;
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
bool DTE::setup_cmux()
|
||||
@ -90,14 +165,21 @@ bool DTE::setup_cmux()
|
||||
if (cmux_term == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cmux_term->init()) {
|
||||
exit_cmux_internal();
|
||||
cmux_term = nullptr;
|
||||
return false;
|
||||
}
|
||||
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
|
||||
if (primary_term == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
|
||||
secondary_term = std::make_unique<CMuxInstance>(cmux_term, 1);
|
||||
if (primary_term == nullptr || secondary_term == nullptr) {
|
||||
exit_cmux_internal();
|
||||
cmux_term = nullptr;
|
||||
return false;
|
||||
}
|
||||
set_command_callbacks();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -119,6 +201,7 @@ bool DTE::set_mode(modem_mode m)
|
||||
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
|
||||
// mode stays the same, but need to swap terminals (as command has been switched)
|
||||
secondary_term.swap(primary_term);
|
||||
set_command_callbacks();
|
||||
} else {
|
||||
mode = m;
|
||||
}
|
||||
@ -161,6 +244,7 @@ bool DTE::set_mode(modem_mode m)
|
||||
// manual CMUX transitions: Swap terminals
|
||||
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
|
||||
secondary_term.swap(primary_term);
|
||||
set_command_callbacks();
|
||||
return true;
|
||||
}
|
||||
mode = modem_mode::UNDEF;
|
||||
@ -169,6 +253,10 @@ bool DTE::set_mode(modem_mode m)
|
||||
|
||||
void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
|
||||
{
|
||||
if (f == nullptr) {
|
||||
set_command_callbacks();
|
||||
return;
|
||||
}
|
||||
on_data = std::move(f);
|
||||
secondary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
if (!data) { // if no data available from terminal callback -> need to explicitly read some
|
||||
@ -202,6 +290,69 @@ int DTE::write(uint8_t *data, size_t len)
|
||||
return secondary_term->write(data, len);
|
||||
}
|
||||
|
||||
int DTE::write(DTE_Command command)
|
||||
{
|
||||
return primary_term->write(command.data, command.len);
|
||||
}
|
||||
|
||||
void DTE::on_read(got_line_cb on_read_cb)
|
||||
{
|
||||
if (on_read_cb == nullptr) {
|
||||
primary_term->set_read_cb(nullptr);
|
||||
internal_lock.unlock();
|
||||
return;
|
||||
}
|
||||
internal_lock.lock();
|
||||
primary_term->set_read_cb([this, on_read_cb](uint8_t *data, size_t len) {
|
||||
if (!data) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data, buffer.size);
|
||||
}
|
||||
auto res = on_read_cb(data, len);
|
||||
if (res == command_result::OK || res == command_result::FAIL) {
|
||||
primary_term->set_read_cb(nullptr);
|
||||
internal_lock.unlock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
|
||||
{
|
||||
if (memchr(data + consumed, separator, len)) {
|
||||
result = got_line(data, consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
signal.set(GOT_LINE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DTE::command_cb::wait_for_line(uint32_t time_ms)
|
||||
{
|
||||
auto got_lf = signal.wait(command_cb::GOT_LINE, time_ms);
|
||||
if (got_lf && result == command_result::TIMEOUT) {
|
||||
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
return got_lf;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
void DTE::extra_buffer::grow(size_t need_size)
|
||||
{
|
||||
if (need_size == 0) {
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
} else if (buffer == nullptr) {
|
||||
buffer = new std::vector<uint8_t>(need_size);
|
||||
} else {
|
||||
buffer->resize(need_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implemented here to keep all headers C++11 compliant
|
||||
*/
|
||||
|
@ -65,9 +65,7 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
|
||||
void Netif::receive(uint8_t *data, size_t len)
|
||||
{
|
||||
if (signal.is_any(PPP_STARTED)) {
|
||||
esp_netif_receive(driver.base.netif, data, len, nullptr);
|
||||
}
|
||||
esp_netif_receive(driver.base.netif, data, len, nullptr);
|
||||
}
|
||||
|
||||
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
|
||||
@ -89,8 +87,8 @@ void Netif::start()
|
||||
receive(data, len);
|
||||
return true;
|
||||
});
|
||||
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
|
||||
signal.set(PPP_STARTED);
|
||||
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void Netif::stop()
|
||||
|
@ -8,6 +8,11 @@
|
||||
#include "esp_modem_config.h"
|
||||
#include "uart_resource.hpp"
|
||||
|
||||
#ifndef UART_HW_FIFO_LEN
|
||||
// to build with IDF <= v5.1
|
||||
#define UART_HW_FIFO_LEN(uart_nr) UART_FIFO_LEN
|
||||
#endif
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
uart_resource::~uart_resource()
|
||||
@ -18,7 +23,7 @@ uart_resource::~uart_resource()
|
||||
}
|
||||
|
||||
uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHandle_t *event_queue, int fd)
|
||||
: port(-1)
|
||||
: port(UART_NUM_MAX)
|
||||
{
|
||||
esp_err_t res;
|
||||
|
||||
@ -44,9 +49,9 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
|
||||
ESP_MODEM_THROW_IF_ERROR(res, "config uart gpio failed");
|
||||
/* Set flow control threshold */
|
||||
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
|
||||
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_HW_FIFO_LEN(config->port_num) - 8);
|
||||
} else if (config->flow_control == ESP_MODEM_FLOW_CONTROL_SW) {
|
||||
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_FIFO_LEN - 8);
|
||||
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_HW_FIFO_LEN(config->port_num) - 8);
|
||||
}
|
||||
ESP_MODEM_THROW_IF_ERROR(res, "config uart flow control failed");
|
||||
|
||||
|
@ -64,7 +64,6 @@ public:
|
||||
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
|
||||
{
|
||||
ESP_MODEM_THROW_IF_FALSE(signal.wait(TASK_PARAMS, 1000), "Failed to set UART task params");
|
||||
on_read = std::move(f);
|
||||
}
|
||||
|
||||
@ -91,7 +90,6 @@ private:
|
||||
static const size_t TASK_INIT = BIT0;
|
||||
static const size_t TASK_START = BIT1;
|
||||
static const size_t TASK_STOP = BIT2;
|
||||
static const size_t TASK_PARAMS = BIT3;
|
||||
|
||||
QueueHandle_t event_queue;
|
||||
uart_resource uart;
|
||||
@ -118,9 +116,7 @@ void UartTerminal::task()
|
||||
return; // exits to the static method where the task gets deleted
|
||||
}
|
||||
while (signal.is_any(TASK_START)) {
|
||||
signal.set(TASK_PARAMS);
|
||||
if (get_event(event, 100)) {
|
||||
signal.clear(TASK_PARAMS);
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
uart_get_buffered_data_len(uart.port, &len);
|
||||
|
@ -13,15 +13,3 @@ idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
|
||||
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_COMPILER_CXX_EXCEPTIONS")
|
||||
target_compile_definitions(${esp_modem} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
|
||||
target_link_options(${esp_modem} INTERFACE -fsanitize=address -fsanitize=undefined)
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
|
||||
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
endif()
|
||||
|
@ -12,3 +12,12 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
|
||||
idf_component_get_property(esp_modem esp_modem COMPONENT_LIB)
|
||||
target_compile_options(${esp_modem} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${esp_modem} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
endif()
|
||||
|
13
components/esp_mqtt_cxx/README.md
Normal file
13
components/esp_mqtt_cxx/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# esp_mqtt_cxx
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_mqtt_cxx)
|
||||
|
||||
The ESP MQTT client is a wrapper over the esp_mqtt client with the goal of providing a higher level API.
|
||||
|
||||
## Examples
|
||||
|
||||
Get started with [examples](https://github.com/espressif/esp-protocols/tree/master/components/esp_mqtt_cxx/examples)
|
||||
|
||||
## Documentation
|
||||
|
||||
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/docs/latest/esp_mqtt_cxx/index.html)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user