mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-23 07:12:32 +01:00
Compare commits
112 Commits
eppp-v1.1.
...
mqtt_cxx-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
072781fcd9 | ||
|
|
7ce85c7389 | ||
|
|
1cc2c97022 | ||
|
|
f41c4a0ad0 | ||
|
|
7336451109 | ||
|
|
c5b9375b4c | ||
|
|
25513d0dc3 | ||
|
|
c0cf91e174 | ||
|
|
d979e1b333 | ||
|
|
9f9c6c12f3 | ||
|
|
742adea26d | ||
|
|
2535b3fefc | ||
|
|
ba0ef10038 | ||
|
|
f1e35977e5 | ||
|
|
25735ead9c | ||
|
|
bf9e955514 | ||
|
|
726c41f842 | ||
|
|
c7de9251ed | ||
|
|
0f6235f13e | ||
|
|
6c2c2cd22b | ||
|
|
9e0bcd4b08 | ||
|
|
07b1bcdc34 | ||
|
|
f20a234f65 | ||
|
|
63082b996d | ||
|
|
27d43277d2 | ||
|
|
4d8d25a345 | ||
|
|
bed116d98b | ||
|
|
9ec006a3e4 | ||
|
|
245b5a2ffb | ||
|
|
b9ea0c31ce | ||
|
|
4aa0e4ba49 | ||
|
|
c48c2ebe7e | ||
|
|
0ccaf2c0bb | ||
|
|
ed569d8509 | ||
|
|
9b2b1f680d | ||
|
|
370dfecc15 | ||
|
|
e50c5eb40e | ||
|
|
6f6110e30e | ||
|
|
41f7157ffb | ||
|
|
858f67c280 | ||
|
|
83ffeb0d12 | ||
|
|
6e99202a18 | ||
|
|
fb5279ae88 | ||
|
|
b70cc3fc09 | ||
|
|
911c2dbe9f | ||
|
|
c620855d56 | ||
|
|
cccfdd9315 | ||
|
|
ecc7258093 | ||
|
|
0caea67542 | ||
|
|
11a8567598 | ||
|
|
9b80a7ef7d | ||
|
|
12028ab250 | ||
|
|
68e299a357 | ||
|
|
2681b9b3c6 | ||
|
|
782b7cd119 | ||
|
|
3141d6cab5 | ||
|
|
7b8770e2fc | ||
|
|
6153c0002a | ||
|
|
3d5e11b82f | ||
|
|
eacc3a0aa8 | ||
|
|
2826287d43 | ||
|
|
3bfa00389d | ||
|
|
ace7fca8c6 | ||
|
|
2b2f009a65 | ||
|
|
1444d575f0 | ||
|
|
081eef88cf | ||
|
|
8b0704eaf4 | ||
|
|
4889dd6fcb | ||
|
|
134247d88f | ||
|
|
e772ce673d | ||
|
|
3d4712b905 | ||
|
|
67188fd7b4 | ||
|
|
4d7c6848b2 | ||
|
|
1e83bee4fe | ||
|
|
3838485229 | ||
|
|
ba3377b262 | ||
|
|
e5bed394ee | ||
|
|
65b58aa05a | ||
|
|
d1e6708063 | ||
|
|
318bca1657 | ||
|
|
9e1b9cdd20 | ||
|
|
7f424325d8 | ||
|
|
9ef228f247 | ||
|
|
5068f2217e | ||
|
|
1ceb42c5a2 | ||
|
|
e52a5757f1 | ||
|
|
732cd29ec0 | ||
|
|
7a203cf085 | ||
|
|
d665e6f18e | ||
|
|
2e269640c6 | ||
|
|
c078c36361 | ||
|
|
90ddb04e53 | ||
|
|
cee3bdea9d | ||
|
|
fa96de3bd7 | ||
|
|
18f0d02806 | ||
|
|
bfa604b5f6 | ||
|
|
92a31187ff | ||
|
|
0197c994ee | ||
|
|
487a746d14 | ||
|
|
af6bb1b5ee | ||
|
|
f5e62e83e9 | ||
|
|
cad527d2fc | ||
|
|
16cc2dcffb | ||
|
|
d622e41a54 | ||
|
|
ca6e39aac7 | ||
|
|
e45944e143 | ||
|
|
fe657b9737 | ||
|
|
453be4cd79 | ||
|
|
018ba58ec5 | ||
|
|
67c682d911 | ||
|
|
91915ce1c7 | ||
|
|
54eb002758 |
24
.github/workflows/asio__build-target-test.yml
vendored
24
.github/workflows/asio__build-target-test.yml
vendored
@@ -81,18 +81,24 @@ jobs:
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }}
|
||||
path: ${{ env.TEST_DIR }}/${{ matrix.example }}/build
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Download Example Test to target ${{ matrix.config }}
|
||||
run: |
|
||||
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 ${{ env.TEST_DIR }}/${{ matrix.example }}/build/flash_image.bin
|
||||
- name: Run Example Test ${{ matrix.example }} on target
|
||||
working-directory: ${{ env.TEST_DIR }}/${{ matrix.example }}
|
||||
run: |
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: ifconfig-basic, path: "components/console_cmd_ifconfig/examples"}]
|
||||
include:
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: mqtt_ssl_auth_console, path: "components/console_cmd_mqtt/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: ping-basic, path: "components/console_cmd_ping/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: wifi-basic, path: "components/console_cmd_wifi/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: console_basic, path: "components/console_simple_init/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
2
.github/workflows/eppp__build.yml
vendored
2
.github/workflows/eppp__build.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3"]
|
||||
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
|
||||
@@ -13,10 +13,7 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
include:
|
||||
- idf_ver: "latest"
|
||||
warning: "Warning: The smallest app partition is nearly full"
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
@@ -27,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full"
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
@@ -75,7 +72,7 @@ jobs:
|
||||
needs: build_all_examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4", "latest"]
|
||||
idf_ver: ["release-v5.5", "latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- modem
|
||||
|
||||
24
.github/workflows/lws_build.yml
vendored
24
.github/workflows/lws_build.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Libwebsockets build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on:
|
||||
@@ -65,14 +65,24 @@ jobs:
|
||||
with:
|
||||
name: lws_target_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: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
21
.github/workflows/mdns__build-target-test.yml
vendored
21
.github/workflows/mdns__build-target-test.yml
vendored
@@ -24,6 +24,11 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
if [[ "${{ matrix.idf_ver }}" == "latest" ]]; then
|
||||
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'"
|
||||
else
|
||||
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_GENERIC'"
|
||||
fi
|
||||
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 -d
|
||||
@@ -71,6 +76,22 @@ jobs:
|
||||
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
|
||||
working-directory: components/mdns/${{ matrix.test.path }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
|
||||
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
95
.github/workflows/mdns__host-tests.yml
vendored
95
.github/workflows/mdns__host-tests.yml
vendored
@@ -29,6 +29,8 @@ jobs:
|
||||
# Build host tests app (with all configs and targets supported)
|
||||
python ./ci/build_apps.py components/mdns/tests/host_test/
|
||||
cd components/mdns/tests/host_test
|
||||
ls -la
|
||||
ls -ls build*
|
||||
# First run the linux_app and send a quick A query and a reverse query
|
||||
./build_linux_app/mdns_host.elf &
|
||||
python dnsfixture.py A myesp.local --ip_only | xargs python dnsfixture.py X
|
||||
@@ -55,8 +57,11 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd components/mdns/tests/test_afl_fuzz_host/
|
||||
make INSTR=off
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
idf.py reconfigure
|
||||
mkdir build2 && cd build2
|
||||
cmake ..
|
||||
cmake --build .
|
||||
- name: Test no malloc functions
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -68,3 +73,89 @@ jobs:
|
||||
diff -q $file /tmp/$file || exit 1
|
||||
echo "OK"
|
||||
done
|
||||
|
||||
host_unit_test:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Unit tests on host
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
- name: Install bsdlib and ruby
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev ruby
|
||||
- name: Build and run unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
idf.py reconfigure
|
||||
mkdir build2 && cd build2
|
||||
cmake -DUNIT_TESTS=test_receiver ..
|
||||
cmake --build .
|
||||
ctest --extra-verbose
|
||||
cd ..
|
||||
mkdir build3 && cd build3
|
||||
cmake -DUNIT_TESTS=test_sender ..
|
||||
cmake --build .
|
||||
ctest --extra-verbose
|
||||
|
||||
|
||||
fuzz_test:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns-fuzz') || github.event_name == 'push'
|
||||
name: Fuzzer tests for mdns lib
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: aflplusplus/aflplusplus:v4.34c
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout ESP-IDF
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: espressif/esp-idf
|
||||
path: idf
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Necessary Libs
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev
|
||||
|
||||
- name: Run AFL++
|
||||
shell: bash
|
||||
run: |
|
||||
export IDF_PATH=$GITHUB_WORKSPACE/idf
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
pip install dnslib
|
||||
cd input && python generate_cases.py && cd ..
|
||||
cmake -B build2 -S . -G "Ninja" -DCMAKE_C_COMPILER=afl-cc
|
||||
cmake --build build2
|
||||
timeout 10m afl-fuzz -i input -o out -- build2/mdns_host_unit_test || \
|
||||
if [ $? -eq 124 ]; then # timeout exit code
|
||||
if [ -n "$(find out/default/crashes -type f 2>/dev/null)" ]; then
|
||||
echo "Crashes found!";
|
||||
tar -czf out/default/crashes.tar.gz -C out/default crashes;
|
||||
exit 1;
|
||||
fi
|
||||
else
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
- name: Upload Crash Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fuzz-crashes
|
||||
path: components/mdns/tests/host_unit_test/out/default/crashes.tar.gz
|
||||
if-no-files-found: ignore
|
||||
|
||||
13
.github/workflows/modem__build-host-tests.yml
vendored
13
.github/workflows/modem__build-host-tests.yml
vendored
@@ -13,13 +13,8 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
|
||||
include:
|
||||
- idf_ver: "release-v5.0"
|
||||
example: "simple_cmux_client"
|
||||
warning: "Warning: The smallest app partition is nearly full"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@@ -30,9 +25,9 @@ jobs:
|
||||
- if: ${{ matrix.skip_config }}
|
||||
run: rm -f $GITHUB_WORKSPACE/protocols/components/esp_modem/examples/${{ matrix.example }}/sdkconfig.ci.${{ matrix.skip_config }}*
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
shell: bash
|
||||
env:
|
||||
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full\nwarning: unknown kconfig symbol 'ESP32_PANIC_PRINT_HALT'"
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
@@ -44,7 +39,7 @@ jobs:
|
||||
name: Build tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
|
||||
idf_ver: ["latest","release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
test: ["target", "target_ota", "target_iperf", "target_urc"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
4
.github/workflows/modem__target-test.yml
vendored
4
.github/workflows/modem__target-test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32c3"]
|
||||
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
|
||||
include:
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
name: Run Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32c3"]
|
||||
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
|
||||
include:
|
||||
|
||||
48
.github/workflows/mosq__build.yml
vendored
48
.github/workflows/mosq__build.yml
vendored
@@ -13,13 +13,15 @@ jobs:
|
||||
name: Mosquitto build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker", "serverless_mqtt"]
|
||||
exclude:
|
||||
# serverless_mqtt is not supported on v5.1 and master (esp-peer dependency)
|
||||
- idf_ver: "release-v5.1"
|
||||
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker"]
|
||||
include:
|
||||
# serverless_mqtt is not supported on >=v6.0 (esp-peer dependency)
|
||||
- idf_ver: "release-v5.3"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "latest"
|
||||
- idf_ver: "release-v5.4"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "release-v5.5"
|
||||
example: "serverless_mqtt"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -61,7 +63,7 @@ jobs:
|
||||
needs: build_mosq
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
idf_ver: ["release-v5.4", "release-v5.5", "release-v6.0", "latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
@@ -77,6 +79,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
@@ -120,7 +136,7 @@ jobs:
|
||||
name: Build IDF tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["release-v5.5"] # TODO: add release-v6.0/latest with esp-mqtt directly
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -165,7 +181,7 @@ jobs:
|
||||
needs: build_idf_tests_with_mosq
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["release-v5.5"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
@@ -180,6 +196,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2" --upgrade
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
|
||||
7
.github/workflows/mqtt_cxx__build.yml
vendored
7
.github/workflows/mqtt_cxx__build.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: mqtt-basic, path: "components/esp_mqtt_cxx/examples" }]
|
||||
test: [ { app: mqtt-basic, path: "components/esp_mqtt_cxx/examples" }, { app: test, path: "components/esp_mqtt_cxx/test/unit" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@@ -25,8 +25,7 @@ jobs:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: ${{matrix.test.path}}
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
||||
python ./ci/build_apps.py ./${{ matrix.test.path }} --target ${{ matrix.idf_target }} -vv --preserve-all -c
|
||||
|
||||
16
.github/workflows/sockutls_build.yml
vendored
16
.github/workflows/sockutls_build.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
pip install idf-build-apps --upgrade
|
||||
python ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
@@ -87,6 +87,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
2
.github/workflows/tls_cxx__build.yml
vendored
2
.github/workflows/tls_cxx__build.yml
vendored
@@ -26,5 +26,5 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
pip install idf-build-apps --upgrade
|
||||
python ./ci/build_apps.py ./components/mbedtls_cxx/${{ matrix.test.path }} -vv --preserve-all
|
||||
|
||||
@@ -65,14 +65,27 @@ jobs:
|
||||
with:
|
||||
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Install Python packages
|
||||
- name: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
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: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
|
||||
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
@@ -56,7 +56,7 @@ if __name__ == '__main__':
|
||||
build_dir='build_@t_@w',
|
||||
config_rules_str=args.rules,
|
||||
build_log_filename='build_log.txt',
|
||||
size_json_filename='size.json' if not args.linux else None,
|
||||
size_json_filename=None,
|
||||
check_warnings=True,
|
||||
manifest_files=args.manifests,
|
||||
default_build_targets=SUPPORTED_TARGETS,
|
||||
|
||||
@@ -4,3 +4,5 @@ Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' ins
|
||||
Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead.
|
||||
Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead.
|
||||
Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead.
|
||||
warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'
|
||||
WARNING: The following Kconfig variables were used in "if" clauses, but not
|
||||
|
||||
@@ -6,3 +6,4 @@ dpkt
|
||||
pytest
|
||||
idf_build_apps
|
||||
netifaces
|
||||
esptool>=5.1.0
|
||||
|
||||
63
common_components/modem_sim/esp32c6.sdkconfig.defaults
Normal file
63
common_components/modem_sim/esp32c6.sdkconfig.defaults
Normal file
@@ -0,0 +1,63 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_IDF_TARGET="esp32c6"
|
||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||
CONFIG_APP_PROJECT_VER_FROM_CONFIG=y
|
||||
CONFIG_APP_PROJECT_VER="v4.1.0.0-dev"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="module_config/module_esp32c6_default/partitions_at.csv"
|
||||
CONFIG_PARTITION_TABLE_MD5=n
|
||||
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_FILE="module_config/module_esp32c6_default/at_customize.csv"
|
||||
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_OFFSET=0x1e000
|
||||
CONFIG_ESP_TLS_PSK_VERIFICATION=y
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=n
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
|
||||
CONFIG_HTTPD_MAX_URI_LEN=1024
|
||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
|
||||
CONFIG_RTC_CLK_CAL_CYCLES=1024
|
||||
CONFIG_ESP_PHY_MAC_BB_PD=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
CONFIG_ESP_TASK_WDT_PANIC=y
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
|
||||
CONFIG_ESP_DEBUG_OCDAWARE=n
|
||||
CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y
|
||||
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0
|
||||
CONFIG_FATFS_LFN_HEAP=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=n
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
|
||||
CONFIG_LWIP_MAX_SOCKETS=16
|
||||
CONFIG_LWIP_SO_LINGER=y
|
||||
CONFIG_LWIP_SO_RCVBUF=y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=y
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=y
|
||||
CONFIG_LWIP_IPV6_AUTOCONFIG=y
|
||||
CONFIG_LWIP_TCP_MAXRTX=6
|
||||
CONFIG_LWIP_TCP_SYNMAXRTX=3
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_SNTP_MAX_SERVERS=3
|
||||
CONFIG_LWIP_SNTP_STARTUP_DELAY=n
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
|
||||
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n
|
||||
CONFIG_MBEDTLS_HAVE_TIME_DATE=y
|
||||
CONFIG_MBEDTLS_DHM_C=y
|
||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
||||
CONFIG_VFS_SUPPORT_TERMIOS=n
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
CONFIG_AT_PROCESS_TASK_STACK_SIZE=6144
|
||||
CONFIG_AT_MDNS_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_WPS_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_SMARTCONFIG_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_PING_COMMAND_SUPPORT=n
|
||||
CONFIG_LWIP_IP_FORWARD=y
|
||||
CONFIG_LWIP_IPV4_NAPT=y
|
||||
@@ -2,9 +2,10 @@
|
||||
set -e
|
||||
|
||||
# Create directory "modem_sim_esp32", go inside it
|
||||
# Usage: ./install.sh [platform] [module]
|
||||
# Usage: ./install.sh [platform] [module] [uart_tx_pin] [uart_rx_pin]
|
||||
|
||||
SCRIPT_DIR=$(pwd)
|
||||
UPDATE_UART_PINS_SCRIPT="$(cd "$(dirname "$0")" && pwd)/update_uart_pins.py"
|
||||
mkdir -p modem_sim_esp32
|
||||
cd modem_sim_esp32
|
||||
|
||||
@@ -39,6 +40,8 @@ mkdir -p build
|
||||
# Default values for platform and module
|
||||
platform="PLATFORM_ESP32"
|
||||
module="WROOM-32"
|
||||
uart_tx_pin=""
|
||||
uart_rx_pin=""
|
||||
|
||||
# Override defaults if parameters are provided
|
||||
if [ ! -z "$1" ]; then
|
||||
@@ -47,18 +50,49 @@ fi
|
||||
if [ ! -z "$2" ]; then
|
||||
module="$2"
|
||||
fi
|
||||
if [ ! -z "$3" ]; then
|
||||
uart_tx_pin="$3"
|
||||
fi
|
||||
if [ ! -z "$4" ]; then
|
||||
uart_rx_pin="$4"
|
||||
fi
|
||||
|
||||
target="${platform##*_}"
|
||||
target="${target,,}"
|
||||
|
||||
# Use provided pins for description when present; otherwise keep defaults
|
||||
description="4MB, Wi-Fi + BLE, OTA, TX:17 RX:16"
|
||||
if [ -n "$uart_tx_pin" ] || [ -n "$uart_rx_pin" ]; then
|
||||
desc_tx=${uart_tx_pin:-17}
|
||||
desc_rx=${uart_rx_pin:-16}
|
||||
description="4MB, Wi-Fi + BLE, OTA, TX:${desc_tx} RX:${desc_rx}"
|
||||
fi
|
||||
|
||||
# Create file "build/module_info.json" with content
|
||||
cat > build/module_info.json << EOF
|
||||
{
|
||||
"platform": "$platform",
|
||||
"module": "$module",
|
||||
"description": "4MB, Wi-Fi + BLE, OTA, TX:17 RX:16",
|
||||
"description": "$description",
|
||||
"silence": 0
|
||||
}
|
||||
EOF
|
||||
|
||||
cp "$SCRIPT_DIR/sdkconfig.defaults" "module_config/module_esp32_default/sdkconfig.defaults"
|
||||
# Optionally update UART pins in factory_param_data.csv for the selected module
|
||||
if [ -n "$uart_tx_pin" ] || [ -n "$uart_rx_pin" ]; then
|
||||
csv_path="components/customized_partitions/raw_data/factory_param/factory_param_data.csv"
|
||||
if [ ! -f "$csv_path" ]; then
|
||||
echo "Warning: $csv_path not found; skipping UART pin update."
|
||||
else
|
||||
python3 "$UPDATE_UART_PINS_SCRIPT" "$platform" "$module" "$uart_tx_pin" "$uart_rx_pin" "$csv_path"
|
||||
echo "Updated UART pins in $csv_path"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copy the platform-specific sdkconfig.defaults file if it exists
|
||||
if [ -f "$SCRIPT_DIR/${target}.sdkconfig.defaults" ]; then
|
||||
cp "$SCRIPT_DIR/${target}.sdkconfig.defaults" "module_config/module_${target}_default/sdkconfig.defaults"
|
||||
fi
|
||||
|
||||
echo "Installation completed successfully!"
|
||||
echo "Created modem_sim_esp32 directory with esp-at repository and configuration"
|
||||
|
||||
46
common_components/modem_sim/update_uart_pins.py
Normal file
46
common_components/modem_sim/update_uart_pins.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import csv
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def update_uart_pins(platform: str, module: str, tx_pin: str, rx_pin: str, csv_path: str) -> None:
|
||||
path = pathlib.Path(csv_path)
|
||||
rows = []
|
||||
found = False
|
||||
|
||||
with path.open(newline="") as f:
|
||||
reader = csv.DictReader(f)
|
||||
fieldnames = reader.fieldnames
|
||||
for row in reader:
|
||||
if row.get("platform") == platform and row.get("module_name") == module:
|
||||
if tx_pin:
|
||||
row["uart_tx_pin"] = tx_pin
|
||||
if rx_pin:
|
||||
row["uart_rx_pin"] = rx_pin
|
||||
found = True
|
||||
rows.append(row)
|
||||
|
||||
if not found:
|
||||
print(f"Warning: no row updated for platform={platform} module={module}")
|
||||
|
||||
with path.open("w", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 6:
|
||||
print("Usage: update_uart_pins.py <platform> <module> <uart_tx_pin> <uart_rx_pin> <csv_path>")
|
||||
return 1
|
||||
|
||||
platform, module, tx_pin, rx_pin, csv_path = sys.argv[1:6]
|
||||
update_uart_pins(platform, module, tx_pin, rx_pin, csv_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -9,4 +9,8 @@ dependencies:
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
||||
espressif/ethernet_init:
|
||||
version: '>=0.0.7'
|
||||
matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
- if: idf_version <6.0
|
||||
version: '==0.3.0'
|
||||
|
||||
@@ -9,3 +9,7 @@ dependencies:
|
||||
version: '>=1.1.0'
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(console): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py console_cmd_ping
|
||||
tag_format: console_cmd_ping-v$version
|
||||
version: 1.1.0
|
||||
version: 1.2.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [1.2.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.2.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for interface argument ([90ddb04e](https://github.com/espressif/esp-protocols/commit/90ddb04e))
|
||||
|
||||
## [1.1.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -48,7 +48,7 @@ For more details refer [IDF Component Manager](https://docs.espressif.com/projec
|
||||
|
||||
### ping:
|
||||
```
|
||||
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
|
||||
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] [-I <n>] <host>
|
||||
send ICMP ECHO_REQUEST to network hosts
|
||||
-W, --timeout=<t> Time to wait for a response, in seconds
|
||||
-i, --interval=<t> Wait interval seconds between sending each packet
|
||||
@@ -56,6 +56,7 @@ ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
|
||||
-c, --count=<n> Stop after sending count packets
|
||||
-Q, --tos=<n> Set Type of Service related bits in IP datagrams
|
||||
-T, --ttl=<n> Set Time to Live related bits in IP datagrams
|
||||
-I, --interface=<n> Set Interface number 0=no-interface selected, >0 netif number + 1 (1 is usually 'lo0')
|
||||
<host> Host address
|
||||
|
||||
getaddrinfo [-f <AF>] [-F <FLAGS>]... [-p <port>] <hostname>
|
||||
@@ -121,8 +122,8 @@ getaddrinfo -f AF_INET -F AI_PASSIVE www.example.com
|
||||
ping www.example.com
|
||||
```
|
||||
|
||||
2. To specify additional options, such as timeout, interval, packet size, etc.:
|
||||
2. To specify additional options, such as timeout, interval, packet size, interface, etc.:
|
||||
|
||||
```
|
||||
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 www.example.com
|
||||
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 -I 0 www.example.com
|
||||
```
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -99,6 +99,7 @@ static struct {
|
||||
struct arg_int *count;
|
||||
struct arg_int *tos;
|
||||
struct arg_int *ttl;
|
||||
struct arg_int *interface;
|
||||
struct arg_str *host;
|
||||
struct arg_end *end;
|
||||
} ping_args;
|
||||
@@ -137,6 +138,10 @@ static int do_ping_cmd(int argc, char **argv)
|
||||
config.ttl = (uint32_t)(ping_args.ttl->ival[0]);
|
||||
}
|
||||
|
||||
if (ping_args.interface->count > 0) {
|
||||
config.interface = (uint32_t)(ping_args.interface->ival[0]);
|
||||
}
|
||||
|
||||
// parse IP address
|
||||
struct sockaddr_in6 sock_addr6;
|
||||
ip_addr_t target_addr = {0};
|
||||
@@ -155,12 +160,12 @@ static int do_ping_cmd(int argc, char **argv)
|
||||
}
|
||||
if (res->ai_family == AF_INET) {
|
||||
#if CONFIG_LWIP_IPV4
|
||||
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
|
||||
struct in_addr addr4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
|
||||
#endif
|
||||
} else {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr;
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
|
||||
#endif
|
||||
}
|
||||
@@ -204,6 +209,7 @@ esp_err_t console_cmd_ping_register(void)
|
||||
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
|
||||
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
|
||||
ping_args.ttl = arg_int0("T", "ttl", "<n>", "Set Time to Live related bits in IP datagrams");
|
||||
ping_args.interface = arg_int0("I", "interface", "<n>", "Set Interface number");
|
||||
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
|
||||
ping_args.end = arg_end(1);
|
||||
const esp_console_cmd_t ping_cmd = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 1.1.0
|
||||
version: 1.2.0
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ping
|
||||
description: The component provides a console where the 'ping' command can be executed.
|
||||
dependencies:
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(eppp): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py eppp_link
|
||||
tag_format: eppp-v$version
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.4](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.4)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed missing freertos deps ([f1e35977](https://github.com/espressif/esp-protocols/commit/f1e35977))
|
||||
- Add optional mqtt dependency ([911c2dbe](https://github.com/espressif/esp-protocols/commit/911c2dbe))
|
||||
|
||||
## [1.1.3](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.3)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "esp_serial_slave_link/essl_sdio.h"
|
||||
#include "eppp_sdio.h"
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
|
||||
@@ -5,3 +5,7 @@ dependencies:
|
||||
override_path: ../../..
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
|
||||
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
|
||||
dependencies:
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(modem): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_modem
|
||||
tag_format: modem-v$version
|
||||
version: 1.4.0
|
||||
version: 2.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## [2.0.0](https://github.com/espressif/esp-protocols/commits/modem-v2.0.0)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp) ([Use generated AT command definitions for IDE navigation](https://github.com/espressif/esp-protocols/commit/e2fa1110))
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for multiple connection in AT based example ([2826287d](https://github.com/espressif/esp-protocols/commit/2826287d))
|
||||
- Add enhanced URC observer API ([4889dd6f](https://github.com/espressif/esp-protocols/commit/4889dd6f))
|
||||
- Support esp-modem use without PPP ([858f8570](https://github.com/espressif/esp-protocols/commit/858f8570), [#851](https://github.com/espressif/esp-protocols/issues/851))
|
||||
- Modem simulator based on esp-at ([e5787e3d](https://github.com/espressif/esp-protocols/commit/e5787e3d))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Update tests and examples to use modem-v2.0 ([4aa0e4ba](https://github.com/espressif/esp-protocols/commit/4aa0e4ba))
|
||||
- Replace MQTT client with simple ping command ([0ccaf2c0](https://github.com/espressif/esp-protocols/commit/0ccaf2c0))
|
||||
- Replace MQTT client with simple ping command ([9b2b1f68](https://github.com/espressif/esp-protocols/commit/9b2b1f68))
|
||||
- Update example to use optional mqtt deps ([3141d6ca](https://github.com/espressif/esp-protocols/commit/3141d6ca))
|
||||
- Minor fixed in the test code ([e772ce67](https://github.com/espressif/esp-protocols/commit/e772ce67))
|
||||
- Add missing set_echo() C wrapper ([d1e67080](https://github.com/espressif/esp-protocols/commit/d1e67080), [#926](https://github.com/espressif/esp-protocols/issues/926))
|
||||
- Fix modem console dependencies ([453be4cd](https://github.com/espressif/esp-protocols/commit/453be4cd))
|
||||
- Address build issues ([018ba58e](https://github.com/espressif/esp-protocols/commit/018ba58e))
|
||||
- Fix driver dependency issue on v6.0 ([67c682d9](https://github.com/espressif/esp-protocols/commit/67c682d9))
|
||||
- Fix CI build issues with IDFv6.0 ([15140e04](https://github.com/espressif/esp-protocols/commit/15140e04))
|
||||
- Add support for ESP-AT based tcp-client example ([14d3cb6b](https://github.com/espressif/esp-protocols/commit/14d3cb6b))
|
||||
- Use idf-build-apps for building target tests ([e9d9b3a8](https://github.com/espressif/esp-protocols/commit/e9d9b3a8))
|
||||
- Make MQTT public broker endpoint configurable ([6d541194](https://github.com/espressif/esp-protocols/commit/6d541194))
|
||||
- Fix URC handling in DTE data callback ([93029946](https://github.com/espressif/esp-protocols/commit/93029946))
|
||||
- Use another public broker for examples and tests ([fac2edbe](https://github.com/espressif/esp-protocols/commit/fac2edbe))
|
||||
- Fix incompatible iterator in std::search() in new gcc ([ed0f6334](https://github.com/espressif/esp-protocols/commit/ed0f6334))
|
||||
- Fix autodetect to support ACFC mode in PPP frames ([8b328a69](https://github.com/espressif/esp-protocols/commit/8b328a69), [#801](https://github.com/espressif/esp-protocols/issues/801))
|
||||
- Fix get_network_registration_state() to accept two params ([5f54d907](https://github.com/espressif/esp-protocols/commit/5f54d907), [#826](https://github.com/espressif/esp-protocols/issues/826))
|
||||
- Consume buffer after handled URC ([6eceb28f](https://github.com/espressif/esp-protocols/commit/6eceb28f))
|
||||
- Use generated AT command definitions for IDE navigation ([e2fa1110](https://github.com/espressif/esp-protocols/commit/e2fa1110), !BREAKING)
|
||||
|
||||
## [1.4.0](https://github.com/espressif/esp-protocols/commits/modem-v1.4.0)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -11,7 +11,12 @@ else()
|
||||
src/esp_modem_uart.cpp
|
||||
src/esp_modem_term_uart.cpp
|
||||
src/esp_modem_netif.cpp)
|
||||
set(dependencies driver esp_event esp_netif)
|
||||
set(dependencies esp_event esp_netif)
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
list(APPEND dependencies esp_driver_uart)
|
||||
else()
|
||||
list(APPEND dependencies driver)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -100,10 +100,10 @@ void wifi_init_softap(void)
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&wifi_event_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_EVENT_ANY_ID,
|
||||
&wifi_event_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
@@ -120,7 +120,7 @@ void wifi_init_softap(void)
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -9,5 +9,4 @@ idf_component_register(SRCS "modem_console_main.cpp"
|
||||
"${command_dir}/my_module_dce.cpp"
|
||||
"httpget_handle.c"
|
||||
"ping_handle.c"
|
||||
REQUIRES console esp_http_client nvs_flash
|
||||
INCLUDE_DIRS "." "${command_dir}")
|
||||
|
||||
@@ -3,7 +3,7 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.2.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -97,10 +97,21 @@ void wakeup_modem(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
command_result handle_urc(uint8_t *data, size_t len)
|
||||
esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo &info)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
|
||||
return command_result::TIMEOUT;
|
||||
// Log buffer information for debugging
|
||||
ESP_LOGI(TAG, "URC Buffer Info: total_size=%zu, processed_offset=%zu, new_data_size=%zu, command_active=%s",
|
||||
info.buffer_total_size, info.processed_offset, info.new_data_size,
|
||||
info.is_command_active ? "true" : "false");
|
||||
|
||||
// Log the new data content
|
||||
if (info.new_data_size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", info.new_data_start, info.new_data_size, ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
// For console example, we just log and don't consume anything
|
||||
// This allows the data to be processed by command handlers
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -381,14 +392,14 @@ extern "C" void app_main(void)
|
||||
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
||||
});
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
const ConsoleCommand HandleURC("urc", "toggle enhanced urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
static int cnt = 0;
|
||||
if (++cnt % 2) {
|
||||
ESP_LOGI(TAG, "Adding URC handler");
|
||||
dce->set_urc(handle_urc);
|
||||
ESP_LOGI(TAG, "Adding enhanced URC handler");
|
||||
dce->set_enhanced_urc(handle_enhanced_urc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "URC removed");
|
||||
dce->set_urc(nullptr);
|
||||
ESP_LOGI(TAG, "Enhanced URC removed");
|
||||
dce->set_enhanced_urc(nullptr);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -22,3 +22,18 @@ To enable this mode, please set `EXAMPLE_CUSTOM_TCP_TRANSPORT=y`
|
||||
This configuration could be used with any network library, which is connecting to a localhost endpoint instead of remote one. This example creates a localhost listener which basically mimics the remote endpoint by forwarding the traffic between the library and the TCP/socket layer of the modem (which is already secure if the TLS is used in the network library)
|
||||
|
||||

|
||||
|
||||
### Multi-connection support
|
||||
|
||||
This example supports opening multiple TCP connections concurrently when the modem firmware allows it.
|
||||
|
||||
- ESP-AT: Multi-connection mode is enabled via `AT+CIPMUX=1`. The example assigns a unique link ID per DCE instance and includes the link ID in `CIPSTART/CIPSEND/CIPRECVDATA` commands.
|
||||
- BG96/SIM7600: The example uses module-specific multi-connection syntax (for example `QIOPEN/CIPOPEN` with a connection ID) and tracks link IDs internally.
|
||||
|
||||
How it works:
|
||||
- The `sock_dce` layer creates multiple DCE instances over a shared DTE. A lightweight mutex coordinates access to the UART so only one DCE issues AT commands at a time.
|
||||
- Asynchronous URCs (for example `+IPD`, `+QIURC`, `+CIPRXGET: 1,<cid>`) wake the corresponding DCE which then performs receive operations for its link.
|
||||
|
||||
Usage:
|
||||
- `app_main` starts two DCE tasks to demonstrate concurrent connections. Adjust the number of DCE instances as needed.
|
||||
- For ESP-AT, ensure your firmware supports `CIPMUX=1` and passive receive (`CIPRECVTYPE`).
|
||||
|
||||
@@ -44,6 +44,18 @@ menu "Example Configuration"
|
||||
help
|
||||
Set APN (Access Point Name), a logical name to choose data network
|
||||
|
||||
config EXAMPLE_USE_TLS
|
||||
bool "Use TLS for MQTT broker"
|
||||
default n
|
||||
help
|
||||
Enable TLS for connection to the MQTT broker.
|
||||
|
||||
config EXAMPLE_BROKER_HOST
|
||||
string "MQTT broker host"
|
||||
default "test.mosquitto.org"
|
||||
help
|
||||
Hostname or IP address of the MQTT broker.
|
||||
|
||||
menu "UART Configuration"
|
||||
config EXAMPLE_MODEM_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
@@ -14,6 +15,29 @@
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
|
||||
|
||||
// Definition of the static member variables
|
||||
std::vector<DCE *> DCE::dce_list{};
|
||||
bool DCE::network_init = false;
|
||||
int Responder::s_link_id = 0;
|
||||
SemaphoreHandle_t Responder::s_dte_mutex{};
|
||||
|
||||
// Constructor - add this DCE instance to the static list
|
||||
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
|
||||
: Module(std::move(dte_arg), config)
|
||||
{
|
||||
dce_list.push_back(this);
|
||||
}
|
||||
|
||||
// Destructor - remove this DCE instance from the static list
|
||||
DCE::~DCE()
|
||||
{
|
||||
auto it = std::find(dce_list.begin(), dce_list.end(), this);
|
||||
if (it != dce_list.end()) {
|
||||
dce_list.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
if (state != status::RECEIVING) {
|
||||
std::string_view resp_sv((char *)data, len);
|
||||
at.check_urc(state, resp_sv);
|
||||
if (state == status::IDLE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Trace incoming AT bytes when handling a response; use DEBUG level
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
// Release DTE access for this link after processing data
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
ESP_LOGD(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
|
||||
|
||||
bool DCE::connect(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;
|
||||
});
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
// Take DTE mutex before starting connect for this link
|
||||
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
|
||||
|
||||
bool DCE::init()
|
||||
{
|
||||
if (network_init) {
|
||||
return true;
|
||||
}
|
||||
network_init = true;
|
||||
Responder::s_dte_mutex = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
@@ -287,6 +339,10 @@ bool DCE::init()
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
dte->on_read([](uint8_t *data, size_t len) {
|
||||
read_callback(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
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);
|
||||
ret check_urc(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
@@ -63,13 +64,19 @@ public:
|
||||
return total_len;
|
||||
}
|
||||
|
||||
// Unique link identifier used to target multi-connection AT commands
|
||||
int link_id{s_link_id++};
|
||||
// Shared mutex guarding DTE access across concurrent DCE instances
|
||||
static SemaphoreHandle_t s_dte_mutex;
|
||||
private:
|
||||
static int s_link_id;
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
printf("sending %d\n", len);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
@@ -101,6 +108,8 @@ private:
|
||||
class DCE : public Module {
|
||||
using Module::Module;
|
||||
public:
|
||||
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
|
||||
~DCE();
|
||||
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
@@ -163,6 +172,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
@@ -184,6 +197,10 @@ public:
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
@@ -224,6 +241,14 @@ public:
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static std::vector<DCE *> dce_list;
|
||||
static bool network_init;
|
||||
static void read_callback(uint8_t *data, size_t len)
|
||||
{
|
||||
for (auto dce : dce_list) {
|
||||
dce->perform_at(data, len);
|
||||
}
|
||||
}
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
void close_sock();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
@@ -14,6 +15,29 @@
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
|
||||
|
||||
// Definition of the static member variables
|
||||
std::vector<DCE*> DCE::dce_list{};
|
||||
bool DCE::network_init = false;
|
||||
int Responder::s_link_id = 0;
|
||||
SemaphoreHandle_t Responder::s_dte_mutex{};
|
||||
|
||||
// Constructor - add this DCE instance to the static list
|
||||
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
|
||||
: Module(std::move(dte_arg), config)
|
||||
{
|
||||
dce_list.push_back(this);
|
||||
}
|
||||
|
||||
// Destructor - remove this DCE instance from the static list
|
||||
DCE::~DCE()
|
||||
{
|
||||
auto it = std::find(dce_list.begin(), dce_list.end(), this);
|
||||
if (it != dce_list.end()) {
|
||||
dce_list.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
if (state != status::RECEIVING) {
|
||||
std::string_view resp_sv((char *)data, len);
|
||||
at.check_urc(state, resp_sv);
|
||||
if (state == status::IDLE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Trace incoming AT bytes when handling a response; use DEBUG level
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
// Release DTE access for this link after processing data
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
ESP_LOGD(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
|
||||
|
||||
bool DCE::connect(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;
|
||||
});
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
// Take DTE mutex before starting connect for this link
|
||||
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
|
||||
|
||||
bool DCE::init()
|
||||
{
|
||||
if (network_init) {
|
||||
return true;
|
||||
}
|
||||
network_init = true;
|
||||
Responder::s_dte_mutex = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
@@ -287,6 +339,10 @@ bool DCE::init()
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
dte->on_read([](uint8_t *data, size_t len) {
|
||||
read_callback(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
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);
|
||||
ret check_urc(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
@@ -63,13 +64,19 @@ public:
|
||||
return total_len;
|
||||
}
|
||||
|
||||
// Unique link identifier used to target multi-connection AT commands
|
||||
int link_id{s_link_id++};
|
||||
// Shared mutex guarding DTE access across concurrent DCE instances
|
||||
static SemaphoreHandle_t s_dte_mutex;
|
||||
private:
|
||||
static int s_link_id;
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
printf("sending %d\n", len);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
@@ -101,6 +108,8 @@ private:
|
||||
class DCE : public Module {
|
||||
using Module::Module;
|
||||
public:
|
||||
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
|
||||
~DCE();
|
||||
|
||||
// --- ESP-MODEM command module starts here ---
|
||||
#include "esp_modem_command_declare_helper.inc"
|
||||
@@ -141,6 +150,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
@@ -163,6 +176,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
@@ -204,6 +221,14 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static std::vector<DCE*> dce_list;
|
||||
static bool network_init;
|
||||
static void read_callback(uint8_t *data, size_t len)
|
||||
{
|
||||
for (auto dce : dce_list) {
|
||||
dce->perform_at(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
override_path: "../../../"
|
||||
version: ^2
|
||||
override_path: ../../../
|
||||
espressif/mbedtls_cxx:
|
||||
version: "*"
|
||||
override_path: "../../../../mbedtls_cxx"
|
||||
version: '*'
|
||||
override_path: ../../../../mbedtls_cxx
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -22,15 +22,26 @@
|
||||
#include "esp_log.h"
|
||||
#include "tcp_transport_mbedtls.h"
|
||||
#include "tcp_transport_at.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define BROKER_URL "test.mosquitto.org"
|
||||
#define USE_TLS CONFIG_EXAMPLE_USE_TLS
|
||||
#define BROKER_HOST CONFIG_EXAMPLE_BROKER_HOST
|
||||
#if USE_TLS
|
||||
#define BROKER_SCHEME "mqtts"
|
||||
#define BROKER_PORT 8883
|
||||
#else
|
||||
#define BROKER_SCHEME "mqtt"
|
||||
#define BROKER_PORT 1883
|
||||
#endif
|
||||
#define BROKER_URL BROKER_SCHEME "://" BROKER_HOST
|
||||
|
||||
|
||||
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 const int DCE0_DONE = BIT3;
|
||||
static const int DCE1_DONE = BIT4;
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
@@ -73,13 +84,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
||||
}
|
||||
}
|
||||
|
||||
static void perform(void* ctx);
|
||||
|
||||
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());
|
||||
// Default to INFO; individual modules use DEBUG for verbose tracing
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
|
||||
event_group = xEventGroupCreate();
|
||||
|
||||
@@ -104,30 +117,60 @@ extern "C" void app_main(void)
|
||||
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));
|
||||
auto dce = sock_dce::create(&dce_config, dte);
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
mqtt_config.broker.address.port = BROKER_PORT;
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
mqtt_config.broker.address.uri = "mqtt://127.0.0.1";
|
||||
dce->start_listening(BROKER_PORT);
|
||||
#else
|
||||
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
|
||||
esp_transport_handle_t at = esp_transport_at_init(dce.get());
|
||||
esp_transport_handle_t ssl = esp_transport_tls_init(at);
|
||||
xTaskCreate(perform, "perform", 4096, dce.get(), 4, nullptr);
|
||||
|
||||
mqtt_config.network.transport = ssl;
|
||||
/* create another DCE to serve a new connection */
|
||||
auto dce1 = sock_dce::create(&dce_config, dte);
|
||||
if (!dce1->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
xTaskCreate(perform, "perform", 4096, dce1.get(), 4, nullptr);
|
||||
|
||||
xEventGroupWaitBits(event_group, DCE0_DONE | DCE1_DONE, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
#ifdef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
// this example does never keeps both DCEs running and in tcp-transport option
|
||||
// we don't need a task to run so we exit main and keep DCE's "running"
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void perform(void* ctx)
|
||||
{
|
||||
auto dce = static_cast<sock_dce::DCE*>(ctx);
|
||||
char mqtt_client_id[] = "MQTT_CLIENT_0";
|
||||
static int counter = 0;
|
||||
const int id = counter++;
|
||||
mqtt_client_id[sizeof(mqtt_client_id) - 2] += id; // assumes a different client id per each thread
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
mqtt_config.credentials.client_id = mqtt_client_id;
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
mqtt_config.broker.address.port = BROKER_PORT + id;
|
||||
mqtt_config.broker.address.uri = BROKER_SCHEME "://127.0.0.1";
|
||||
dce->start_listening(BROKER_PORT + id);
|
||||
#else
|
||||
mqtt_config.broker.address.port = BROKER_PORT;
|
||||
mqtt_config.broker.address.uri = BROKER_URL;
|
||||
esp_transport_handle_t at = esp_transport_at_init(dce);
|
||||
#if USE_TLS
|
||||
esp_transport_handle_t ssl = esp_transport_tls_init(at);
|
||||
mqtt_config.network.transport = ssl;
|
||||
#else
|
||||
mqtt_config.network.transport = at;
|
||||
#endif // USE_TLS
|
||||
#endif // CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
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, nullptr);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
|
||||
ESP_LOGE(TAG, "Failed to start DCE");
|
||||
return;
|
||||
}
|
||||
@@ -135,18 +178,17 @@ extern "C" void app_main(void)
|
||||
while (dce->perform_sock()) {
|
||||
ESP_LOGV(TAG, "...performing");
|
||||
}
|
||||
ESP_LOGE(TAG, "Loop exit.. retrying");
|
||||
ESP_LOGD(TAG, "Loop exit.. retrying");
|
||||
// handle disconnections errors
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to reinit network");
|
||||
return;
|
||||
}
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
|
||||
ESP_LOGI(TAG, "Network reinitialized, retrying");
|
||||
}
|
||||
}
|
||||
#else
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
#endif
|
||||
|
||||
xEventGroupSetBits(event_group, id ? DCE0_DONE : DCE1_DONE);
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
@@ -109,17 +109,17 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+QISEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+QIRD=" + std::to_string(link_id) + "," + 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");
|
||||
send_cmd(std::string("AT+QIOPEN=1,") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -130,16 +130,24 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+QIRD: ";
|
||||
again:
|
||||
const std::string_view recv_data_view = std::string_view(recv_data, len);
|
||||
auto head_pos_found = recv_data_view.find(head);
|
||||
if (head_pos_found == std::string_view::npos) {
|
||||
return ret::FAIL;
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
auto *head_pos = recv_data + head_pos_found;
|
||||
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
|
||||
|
||||
if (next_nl == nullptr) {
|
||||
if (head_pos + head.size() + 1 < recv_data + len) {
|
||||
// might be that we misinterpreted the URC +QIRD: <>,<>,<> (notification) with the +QIRD: <> (read data)
|
||||
// so we try to find the next +QIRD:
|
||||
recv_data = head_pos + head.size() + 1;
|
||||
goto again;
|
||||
}
|
||||
ESP_LOGD(TAG, "no new line found");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
@@ -151,7 +159,9 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
|
||||
if (actual_len == 0) {
|
||||
ESP_LOGD(TAG, "no data received");
|
||||
return ret::FAIL;
|
||||
data_to_recv = 0;
|
||||
// return OK here, as BG96 would keep unacked data and notifies us with +QIRD: 0
|
||||
return ret::OK;
|
||||
}
|
||||
|
||||
if (actual_len > buffer_size) {
|
||||
@@ -182,6 +192,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t 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;
|
||||
ESP_LOGD(TAG, "no OK after data");
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
@@ -222,7 +233,7 @@ 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_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
|
||||
send_stat++;
|
||||
return ret::IN_PROGRESS;
|
||||
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||
@@ -267,7 +278,7 @@ Responder::ret Responder::send(std::string_view response)
|
||||
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));
|
||||
ESP_LOGD(TAG, "Need a pause: missing %d bytes acked", (total - ack));
|
||||
return ret::NEED_MORE_TIME;
|
||||
}
|
||||
}
|
||||
@@ -284,7 +295,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
|
||||
std::string open_response = "+QIOPEN: " + std::to_string(link_id) + ",0";
|
||||
if (response.find(open_response) != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -295,10 +307,9 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
Responder::ret Responder::check_urc(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) {
|
||||
if (response.find(std::string("+QIURC: \"recv\",") + std::to_string(link_id)) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
@@ -309,6 +320,9 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
response = response.substr(head_pos + head.size());
|
||||
int next_cr = response.find('\r');
|
||||
if (next_cr != std::string::npos) {
|
||||
if (next_cr < 2) {
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
response = response.substr(next_cr - 2, next_cr);
|
||||
if (response.find(",0") != std::string::npos) {
|
||||
ESP_LOGV(TAG, "Receiving done");
|
||||
@@ -318,12 +332,21 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
}
|
||||
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
|
||||
} else if (response.find(std::string("+QIURC: \"closed\",") + std::to_string(link_id)) != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Connection closed");
|
||||
return ret::FAIL;
|
||||
}
|
||||
return 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 (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
}
|
||||
if (state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
@@ -342,7 +365,7 @@ Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||
|
||||
status Responder::pending()
|
||||
{
|
||||
send_cmd("AT+QISEND=0,0\r");
|
||||
send_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
|
||||
return status::SENDING;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,12 +58,19 @@ command_result net_open(CommandableIf *t)
|
||||
}
|
||||
ESP_LOGI(TAG, "WiFi connected successfully");
|
||||
|
||||
// Set passive receive mode (1) for better control
|
||||
ret = set_rx_mode(t, 1);
|
||||
// Enable multiple connections mode
|
||||
ret = dce_commands::generic_command(t, "AT+CIPMUX=1\r\n", "OK", "ERROR", 1000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to set preferred Rx mode");
|
||||
ESP_LOGE(TAG, "Failed to enable multiple connections mode");
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGD(TAG, "Multiple connections mode enabled");
|
||||
|
||||
// Set passive receive mode (1) for better control
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(i) + ",1\r\n";
|
||||
dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
@@ -78,49 +85,20 @@ command_result net_close(CommandableIf *t)
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
|
||||
// Set single connection mode (just in case)
|
||||
auto ret = dce_commands::generic_command(t, "AT+CIPMUX=0\r\n", "OK", "ERROR", 1000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGW(TAG, "Failed to set single connection mode");
|
||||
}
|
||||
|
||||
// Establish TCP connection
|
||||
std::string tcp_cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
ret = dce_commands::generic_command(t, tcp_cmd, "CONNECT", "ERROR", timeout);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to establish TCP connection to %s:%d", host.c_str(), port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "TCP connection established to %s:%d", host.c_str(), port);
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_close(CommandableIf *t)
|
||||
{
|
||||
return command_result::OK;
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
return dce_commands::generic_command(t, "AT+CIPCLOSE\r\n", "CLOSED", "ERROR", 5000);
|
||||
// Use link ID 0 for closing connection
|
||||
const int link_id = 0;
|
||||
std::string close_cmd = "AT+CIPCLOSE=" + std::to_string(link_id) + "\r\n";
|
||||
|
||||
// In multiple connections mode, response format is: <link ID>,CLOSED
|
||||
std::string expected_response = std::to_string(link_id) + ",CLOSED";
|
||||
|
||||
return dce_commands::generic_command(t, close_cmd, expected_response, "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// This function is not used in the current implementation
|
||||
// Data sending is handled by the DCE responder
|
||||
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__);
|
||||
// This function is not used in the current implementation
|
||||
// Data receiving is handled by the DCE responder
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result get_ip(CommandableIf *t, std::string &ip)
|
||||
{
|
||||
@@ -150,9 +128,11 @@ command_result get_ip(CommandableIf *t, std::string &ip)
|
||||
|
||||
command_result set_rx_mode(CommandableIf *t, int mode)
|
||||
{
|
||||
ESP_LOGE(TAG, "%s", __func__);
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// For multiple connections mode, set receive mode for link ID 0
|
||||
const int link_id = 0;
|
||||
// Active mode (0) sends data automatically, Passive mode (1) notifies about data for reading
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(mode) + "\r\n";
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(link_id) + "," + std::to_string(mode) + "\r\n";
|
||||
return dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||
}
|
||||
|
||||
@@ -164,17 +144,20 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(len) + "\r\n");
|
||||
// For multiple connections mode, include link ID
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+CIPRECVDATA=" + std::to_string(len) + "\r\n");
|
||||
// For multiple connections mode, include link ID
|
||||
send_cmd("AT+CIPRECVDATA=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
std::string cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
// For multiple connections mode, include link ID
|
||||
std::string cmd = "AT+CIPSTART=" + std::to_string(link_id) + ",\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
send_cmd(cmd);
|
||||
return true;
|
||||
}
|
||||
@@ -187,16 +170,12 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+CIPRECVDATA:";
|
||||
|
||||
// Find the response header
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.data(), head.data() + head.size(), [](char a, char b) {
|
||||
return a == b;
|
||||
});
|
||||
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
const std::string_view recv_data_view(recv_data, len);
|
||||
const auto head_pos_found = recv_data_view.find(head);
|
||||
if (head_pos_found == std::string_view::npos) {
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
const auto *head_pos = recv_data + head_pos_found;
|
||||
// Find the end of the length field
|
||||
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
||||
if (next_comma == nullptr) {
|
||||
@@ -245,12 +224,25 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
char *ok_pos = nullptr;
|
||||
if (actual_len + 1 + 2 /* OK */ <= len) {
|
||||
ok_pos = (char *)memchr(recv_data + actual_len + 1, 'O', MIN_MESSAGE);
|
||||
if (ok_pos == nullptr || ok_pos[1] != 'K') {
|
||||
if (ok_pos == nullptr) { // || ok_pos[1] != 'K') {
|
||||
data_to_recv = 0;
|
||||
ESP_LOGE(TAG, "Missed 'OK' marker");
|
||||
return ret::OK;
|
||||
return ret::FAIL;
|
||||
}
|
||||
if (ok_pos + 1 < recv_data + len && ok_pos[1] != 'K') {
|
||||
// we ignore the condition when receiving 'O' as the last character in the last batch,
|
||||
// don't wait for the 'K' in the next run, assume the data are valid and let higher layers deal with it.
|
||||
data_to_recv = 0;
|
||||
ESP_LOGE(TAG, "Missed 'OK' marker2");
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok_pos != nullptr && (char *)data + len - ok_pos - 2 > MIN_MESSAGE) {
|
||||
// check for async replies after the Recv header
|
||||
std::string_view response((char *)ok_pos + 2 /* OK */, (char *)data + len - ok_pos);
|
||||
check_urc(status::RECEIVING, response);
|
||||
}
|
||||
// Reset and prepare for next receive
|
||||
data_to_recv = 0;
|
||||
return ret::OK;
|
||||
@@ -299,7 +291,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("CONNECT") != std::string::npos) {
|
||||
// In multiple connections mode, response format is: <link ID>,CONNECT
|
||||
if (response.find(",CONNECT") != std::string::npos || response.find("CONNECT") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "TCP connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -309,6 +302,17 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
Responder::ret Responder::check_urc(status state, std::string_view &response)
|
||||
{
|
||||
// Handle data notifications - in multiple connections mode, format is +IPD,<link ID>,<len>
|
||||
std::string expected_urc = "+IPD," + std::to_string(link_id);
|
||||
if (response.find(expected_urc) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Data available notification");
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
@@ -318,24 +322,17 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
if (response.find("WIFI CONNECTED") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "WiFi connected");
|
||||
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
|
||||
ESP_LOGW(TAG, "WiFi disconnected");
|
||||
ESP_LOGD(TAG, "WiFi disconnected");
|
||||
}
|
||||
|
||||
// Handle TCP status messages
|
||||
// Handle TCP status messages (multiple connections format: <link ID>,CONNECT or <link ID>,CLOSED)
|
||||
if (response.find("CONNECT") != std::string::npos && state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
} else if (response.find("CLOSED") != std::string::npos) {
|
||||
ESP_LOGW(TAG, "TCP connection closed");
|
||||
ESP_LOGD(TAG, "TCP connection closed");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
// Handle data notifications in active mode (if we switch to it later)
|
||||
if (response.find("+IPD,") != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Data available notification");
|
||||
}
|
||||
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -18,13 +18,13 @@ using namespace esp_modem;
|
||||
|
||||
command_result net_open(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
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() );
|
||||
ESP_LOGV(TAG, "%s", response.data());
|
||||
if (response.find("+NETOPEN: 1") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "Already there");
|
||||
ret = command_result::OK;
|
||||
@@ -42,23 +42,23 @@ command_result net_open(CommandableIf *term)
|
||||
|
||||
command_result net_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
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__ );
|
||||
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__ );
|
||||
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__ );
|
||||
ESP_LOGE(TAG, "%s Failed", __func__);
|
||||
return ret;
|
||||
}
|
||||
return command_result::OK;
|
||||
@@ -66,13 +66,13 @@ command_result tcp_open(CommandableIf *term, const std::string &host, int port,
|
||||
|
||||
command_result tcp_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
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__ );
|
||||
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);
|
||||
@@ -86,10 +86,10 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
return ret;
|
||||
}
|
||||
ret = command_result::TIMEOUT;
|
||||
ESP_LOGW(TAG, "Before setting...");
|
||||
ESP_LOGD(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());
|
||||
ESP_LOGD(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
|
||||
|
||||
if (response.find("+CIPSEND:") != std::string::npos) {
|
||||
ret = command_result::OK;
|
||||
@@ -98,7 +98,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
ESP_LOGW(TAG, "Before writing...");
|
||||
ESP_LOGD(TAG, "Before writing...");
|
||||
auto written = term->write(data, len);
|
||||
if (written != len) {
|
||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||
@@ -107,7 +107,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
uint8_t ctrl_z = '\x1A';
|
||||
term->write(&ctrl_z, 1);
|
||||
int count = 0;
|
||||
while (ret == command_result::TIMEOUT && count++ < 1000 ) {
|
||||
while (ret == command_result::TIMEOUT && count++ < 1000) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
term->on_read(nullptr);
|
||||
@@ -116,7 +116,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
|
||||
command_result tcp_recv(CommandableIf *term, uint8_t *data, size_t len, size_t &out_len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
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) {
|
||||
@@ -195,17 +195,17 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+CIPRXGET=2," + std::to_string(link_id) + "," + 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");
|
||||
send_cmd(std::string("AT+CIPOPEN=") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
size_t actual_len = 0;
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
|
||||
const std::string head = std::string("+CIPRXGET: 2,") + std::to_string(link_id) + ",";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
@@ -329,7 +329,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
|
||||
std::string open_response = "+CIPOPEN: " + std::to_string(link_id) + ",0";
|
||||
if (response.find(open_response) != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -340,14 +341,22 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
Responder::ret Responder::check_urc(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) {
|
||||
// 1. When <mode> is set to 1 and the 2-4 mode will take effect.
|
||||
// 2. If AT+CIPRXGET=1, it will report +CIPRXGET: 1,<cid>(multi client) when
|
||||
const std::string expected = std::string("+CIPRXGET: 1,") + std::to_string(link_id);
|
||||
if (response.find(expected) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
return 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 (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
idf: '>=4.1.0'
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
||||
version: ^2
|
||||
override_path: ../../../
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.2.0"
|
||||
version: ^1.2.0
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3, esp32p4]"
|
||||
- if: idf_version >=4.4
|
||||
- if: target in [esp32s2, esp32s3, esp32p4]
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -16,8 +16,10 @@
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_modem_api.h"
|
||||
#include "esp_console.h"
|
||||
#include "console_ping.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
@@ -35,7 +37,6 @@ static const char *TAG = "pppos_example";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
static const int CONNECT_BIT = BIT0;
|
||||
static const int DISCONNECT_BIT = BIT1;
|
||||
static const int GOT_DATA_BIT = BIT2;
|
||||
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
|
||||
@@ -64,47 +65,6 @@ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED
|
||||
#define CHECK_USB_DISCONNECTION(event_group)
|
||||
#endif
|
||||
|
||||
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=%" PRIu32, base, event_id);
|
||||
esp_mqtt_event_handle_t event = 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, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, 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, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 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;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
@@ -160,6 +120,11 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
|
||||
|
||||
// Initialize console REPL, register ping and start it
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
ESP_ERROR_CHECK(console_cmd_ping_register());
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
/* Configure the PPP netif */
|
||||
esp_err_t err;
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
|
||||
@@ -251,7 +216,7 @@ void app_main(void)
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
|
||||
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT);
|
||||
if (err != ESP_OK) {
|
||||
@@ -270,7 +235,7 @@ void app_main(void)
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
|
||||
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
|
||||
/* Run the modem demo app */
|
||||
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
|
||||
@@ -340,15 +305,11 @@ void app_main(void)
|
||||
}
|
||||
|
||||
/* Config MQTT */
|
||||
esp_mqtt_client_config_t mqtt_config = {
|
||||
.broker.address.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI,
|
||||
};
|
||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
||||
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
int ping_ret_val;
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
|
||||
#if CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
|
||||
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
esp_modem_pause_net(dce, true);
|
||||
err = esp_modem_get_signal_quality(dce, &rssi, &ber);
|
||||
if (err != ESP_OK) {
|
||||
@@ -357,14 +318,15 @@ void app_main(void)
|
||||
}
|
||||
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
|
||||
esp_modem_pause_net(dce, false);
|
||||
esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
#endif // CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
|
||||
|
||||
ESP_LOGI(TAG, "Waiting for MQTT data");
|
||||
xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
if (ping_ret_val != 0) {
|
||||
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
|
||||
}
|
||||
CHECK_USB_DISCONNECTION(event_group);
|
||||
|
||||
esp_mqtt_client_destroy(mqtt_client);
|
||||
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
@@ -8,16 +8,12 @@ def test_pppos_connect(dut):
|
||||
steps:
|
||||
1. initializes connection with SIM800
|
||||
2. checks we get an IP
|
||||
3. checks for the MQTT events
|
||||
3. checks that the ping command works
|
||||
4. checks that the client cleanly disconnects
|
||||
"""
|
||||
# Check the sequence of connecting, publishing, disconnecting
|
||||
dut.expect('Modem Connect to PPP Server', timeout=90)
|
||||
# Check for MQTT connection and the data event
|
||||
dut.expect('MQTT_EVENT_CONNECTED')
|
||||
dut.expect('MQTT_EVENT_DATA')
|
||||
dut.expect('TOPIC=/ci/esp-modem/pppos-client')
|
||||
dut.expect('DATA=esp32-pppos')
|
||||
dut.expect('Ping command finished with return value: 0', timeout=30)
|
||||
# Check that we have disconnected
|
||||
dut.expect('User interrupted event')
|
||||
# And can use commands again
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
idf_component_register(SRCS "simple_cmux_client_main.cpp" "simple_mqtt_client.cpp"
|
||||
idf_component_register(SRCS "simple_cmux_client_main.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -21,11 +21,13 @@
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "simple_mqtt_client.hpp"
|
||||
#include "esp_vfs_dev.h" // For optional VFS support
|
||||
#include "esp_https_ota.h" // For potential OTA configuration
|
||||
#include "vfs_resource/vfs_create.hpp"
|
||||
#include "SIM7070_gnss.hpp"
|
||||
#include "esp_console.h"
|
||||
#include "console_ping.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
|
||||
@@ -35,18 +37,13 @@
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_HW
|
||||
#endif
|
||||
|
||||
#define BROKER_URL CONFIG_BROKER_URI
|
||||
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
static const char *TAG = "cmux_example";
|
||||
|
||||
class StatusHandler {
|
||||
public:
|
||||
static constexpr auto IP_Event = SignalGroup::bit0;
|
||||
static constexpr auto MQTT_Connect = SignalGroup::bit1;
|
||||
static constexpr auto MQTT_Data = SignalGroup::bit2;
|
||||
static constexpr auto IP_Event = SignalGroup::bit0;
|
||||
|
||||
StatusHandler()
|
||||
{
|
||||
@@ -58,12 +55,6 @@ public:
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_event);
|
||||
}
|
||||
|
||||
void handle_mqtt(MqttClient *client)
|
||||
{
|
||||
mqtt_client = client;
|
||||
client->register_handler(ESP_EVENT_ANY_ID, on_event, this);
|
||||
}
|
||||
|
||||
esp_err_t wait_for(decltype(IP_Event) event, int milliseconds)
|
||||
{
|
||||
return signal.wait_any(event, milliseconds);
|
||||
@@ -80,8 +71,6 @@ private:
|
||||
auto *handler = static_cast<StatusHandler *>(arg);
|
||||
if (base == IP_EVENT) {
|
||||
handler->ip_event(event, data);
|
||||
} else {
|
||||
handler->mqtt_event(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,27 +81,16 @@ private:
|
||||
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
|
||||
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
signal.set(IP_Event);
|
||||
} else if (id == IP_EVENT_PPP_LOST_IP) {
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
signal.set(IP_Event);
|
||||
}
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
}
|
||||
|
||||
void mqtt_event(int32_t event, void *data)
|
||||
{
|
||||
if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::CONNECT)) {
|
||||
signal.set(MQTT_Connect);
|
||||
} else if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::DATA)) {
|
||||
ESP_LOGI(TAG, " TOPIC: %s", mqtt_client->get_topic(data).c_str());
|
||||
ESP_LOGI(TAG, " DATA: %s", mqtt_client->get_data(data).c_str());
|
||||
signal.set(MQTT_Data);
|
||||
}
|
||||
}
|
||||
|
||||
esp_modem::SignalGroup signal{};
|
||||
MqttClient *mqtt_client{nullptr};
|
||||
ip_event_t ip_event_type;
|
||||
ip_event_t ip_event_type{};
|
||||
};
|
||||
|
||||
|
||||
@@ -122,6 +100,11 @@ extern "C" void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
// Initialize console REPL, register ping and start it
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
ESP_ERROR_CHECK(console_cmd_ping_register());
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
/* Configure and create the DTE */
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
/* setup UART specific configuration based on kconfig options */
|
||||
@@ -175,7 +158,7 @@ extern "C" void app_main(void)
|
||||
#endif
|
||||
assert(dce);
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
/* Try to connect to the network */
|
||||
StatusHandler handler;
|
||||
|
||||
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
@@ -224,24 +207,14 @@ extern "C" void app_main(void)
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_GOT_IP) {
|
||||
std::cout << "Got IP address" << std::endl;
|
||||
|
||||
/* When connected to network, subscribe and publish some MQTT data */
|
||||
MqttClient mqtt(BROKER_URL);
|
||||
handler.handle_mqtt(&mqtt);
|
||||
mqtt.connect();
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Connect, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot connect to %s within specified timeout... exiting", BROKER_URL);
|
||||
/* When connected to network, we can ping the internet */
|
||||
int ping_ret_val;
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
if (ping_ret_val != 0) {
|
||||
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
|
||||
return;
|
||||
}
|
||||
std::cout << "Connected" << std::endl;
|
||||
|
||||
mqtt.subscribe(CONFIG_EXAMPLE_MQTT_TEST_TOPIC);
|
||||
mqtt.publish(CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA);
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Data, 60000)) {
|
||||
ESP_LOGE(TAG, "Didn't receive published data within specified timeout... exiting");
|
||||
return;
|
||||
}
|
||||
std::cout << "Received MQTT data" << std::endl;
|
||||
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGE(TAG, "PPP client has lost connection... exiting");
|
||||
return;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* PPPoS Client Example
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "mqtt_client.h"
|
||||
#include "simple_mqtt_client.hpp"
|
||||
|
||||
/**
|
||||
* Reference to the MQTT event base
|
||||
*/
|
||||
ESP_EVENT_DECLARE_BASE(MQTT_EVENTS);
|
||||
|
||||
/**
|
||||
* Thin wrapper around C mqtt_client
|
||||
*/
|
||||
struct MqttClientHandle {
|
||||
explicit MqttClientHandle(const std::string &uri)
|
||||
{
|
||||
esp_mqtt_client_config_t config = { };
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
config.broker.address.uri = uri.c_str();
|
||||
#else
|
||||
config.uri = uri.c_str();
|
||||
#endif
|
||||
client = esp_mqtt_client_init(&config);
|
||||
}
|
||||
|
||||
~MqttClientHandle()
|
||||
{
|
||||
esp_mqtt_client_destroy(client);
|
||||
}
|
||||
|
||||
esp_mqtt_client_handle_t client;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Definitions of MqttClient methods
|
||||
*/
|
||||
MqttClient::MqttClient(const std::string &uri):
|
||||
h(std::unique_ptr<MqttClientHandle>(new MqttClientHandle(uri)))
|
||||
{}
|
||||
|
||||
void MqttClient::connect()
|
||||
{
|
||||
esp_mqtt_client_start(h->client);
|
||||
}
|
||||
|
||||
int32_t MqttClient::get_event(MqttClient::Event ev)
|
||||
{
|
||||
switch (ev) {
|
||||
case Event::CONNECT: {
|
||||
return MQTT_EVENT_CONNECTED;
|
||||
}
|
||||
case Event::DATA:
|
||||
return MQTT_EVENT_DATA;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MqttClient::publish(const std::string &topic, const std::string &data, int qos)
|
||||
{
|
||||
return esp_mqtt_client_publish(h->client, topic.c_str(), data.c_str(), 0, qos, 0);
|
||||
}
|
||||
|
||||
int MqttClient::subscribe(const std::string &topic, int qos)
|
||||
{
|
||||
return esp_mqtt_client_subscribe(h->client, topic.c_str(), qos);
|
||||
}
|
||||
|
||||
std::string MqttClient::get_topic(void *event_data)
|
||||
{
|
||||
auto event = (esp_mqtt_event_handle_t)event_data;
|
||||
if (event == nullptr || event->client != h->client)
|
||||
return {};
|
||||
|
||||
return std::string(event->topic, event->topic_len);
|
||||
}
|
||||
|
||||
std::string MqttClient::get_data(void *event_data)
|
||||
{
|
||||
auto event = (esp_mqtt_event_handle_t)event_data;
|
||||
if (event == nullptr || event->client != h->client)
|
||||
return {};
|
||||
|
||||
return std::string(event->data, event->data_len);
|
||||
}
|
||||
|
||||
void MqttClient::register_handler(int32_t event_id, esp_event_handler_t event_handler, void *arg)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_mqtt_client_register_event(h->client, MQTT_EVENT_ANY, event_handler, arg));
|
||||
}
|
||||
|
||||
MqttClient::~MqttClient() = default;
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* PPPoS Client Example
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
struct MqttClientHandle;
|
||||
|
||||
/**
|
||||
* @brief Simple MQTT client wrapper
|
||||
*/
|
||||
class MqttClient {
|
||||
public:
|
||||
enum class Event {
|
||||
CONNECT,
|
||||
DATA,
|
||||
};
|
||||
|
||||
explicit MqttClient(const std::string &uri);
|
||||
~MqttClient();
|
||||
|
||||
/**
|
||||
* @brief Start the mqtt-client
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* @brief Publish to topic
|
||||
* @param topic Topic to publish
|
||||
* @param data Data to publish
|
||||
* @param qos QoS (0 by default)
|
||||
* @return message id
|
||||
*/
|
||||
int publish(const std::string &topic, const std::string &data, int qos = 0);
|
||||
|
||||
/**
|
||||
* @brief Subscribe to a topic
|
||||
* @param topic Topic to subscribe
|
||||
* @param qos QoS (0 by default)
|
||||
* @return message id
|
||||
*/
|
||||
int subscribe(const std::string &topic, int qos = 0);
|
||||
|
||||
/**
|
||||
* @brief Get topic from event data
|
||||
* @return String topic
|
||||
*/
|
||||
std::string get_topic(void *);
|
||||
|
||||
/**
|
||||
* @brief Get published data from event
|
||||
* @return String representation of the data
|
||||
*/
|
||||
std::string get_data(void *);
|
||||
|
||||
/**
|
||||
* @brief Register MQTT event
|
||||
* @param id Event id
|
||||
* @param event_handler Event handler
|
||||
* @param event_handler_arg Event handler parameters
|
||||
*/
|
||||
void register_handler(int32_t id, esp_event_handler_t event_handler, void *event_handler_arg);
|
||||
|
||||
/**
|
||||
* @brief Convert internal MQTT event to standard ESPEvent
|
||||
* @param ev internal mqtt event
|
||||
* @return corresponding esp_event id
|
||||
*/
|
||||
static int32_t get_event(Event ev);
|
||||
|
||||
private:
|
||||
std::unique_ptr<MqttClientHandle> h;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
@@ -6,21 +6,10 @@ from __future__ import print_function, unicode_literals
|
||||
def test_cmux_connection(dut):
|
||||
"""
|
||||
steps:
|
||||
1. initializes connection with SIM800
|
||||
2. checks we get an IP
|
||||
3. checks for the MQTT events
|
||||
1. checks we're in CMUX mode and get an IP
|
||||
2. checks for ping command
|
||||
"""
|
||||
# Get topic and data from Kconfig
|
||||
topic = ''
|
||||
data = ''
|
||||
try:
|
||||
topic = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_TOPIC')
|
||||
data = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_DATA')
|
||||
except Exception:
|
||||
print('ENV_TEST_FAILURE: Cannot find broker url in sdkconfig')
|
||||
raise
|
||||
# Check the sequence of connecting, publishing, disconnecting
|
||||
dut.expect('Modem has correctly entered multiplexed')
|
||||
# Check for MQTT connection and the data event
|
||||
dut.expect(f'TOPIC: {topic}')
|
||||
dut.expect(f'DATA: {data}')
|
||||
# Check we're in CMUX mode and get an IP
|
||||
dut.expect('Modem has correctly entered multiplexed command/data mode', timeout=60)
|
||||
# Check for ping command
|
||||
dut.expect('Ping command finished with return value: 0', timeout=30)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "1.4.0"
|
||||
version: "2.0.0"
|
||||
description: Library for communicating with cellular modems in command and data modes
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
|
||||
issues: https://github.com/espressif/esp-protocols/issues
|
||||
|
||||
@@ -103,6 +103,11 @@ public:
|
||||
{
|
||||
dte->set_urc_cb(on_read_cb);
|
||||
}
|
||||
|
||||
void set_enhanced_urc(esp_modem::DTE::enhanced_urc_cb enhanced_cb)
|
||||
{
|
||||
dte->set_enhanced_urc_cb(enhanced_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -115,6 +115,42 @@ public:
|
||||
{
|
||||
command_cb.urc_handler = std::move(line_cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enhanced URC handler with buffer consumption control
|
||||
* @param buffer_info Information about the current buffer state
|
||||
* @return Information about how much of the buffer to consume
|
||||
*/
|
||||
struct UrcBufferInfo {
|
||||
const uint8_t* buffer_start; // Start of entire buffer
|
||||
size_t buffer_total_size; // Total buffer size
|
||||
size_t processed_offset; // Offset of already processed data
|
||||
size_t new_data_size; // Size of new data since last call
|
||||
const uint8_t* new_data_start; // Pointer to start of new data
|
||||
bool is_command_active; // Whether a command is currently waiting for response
|
||||
};
|
||||
|
||||
enum class UrcConsumeResult {
|
||||
CONSUME_NONE, // Don't consume anything, continue waiting
|
||||
CONSUME_PARTIAL, // Consume only part of the buffer
|
||||
CONSUME_ALL // Consume entire buffer
|
||||
};
|
||||
|
||||
struct UrcConsumeInfo {
|
||||
UrcConsumeResult result;
|
||||
size_t consume_size; // How many bytes to consume (0 = none, SIZE_MAX = all)
|
||||
};
|
||||
|
||||
typedef std::function<UrcConsumeInfo(const UrcBufferInfo &)> enhanced_urc_cb;
|
||||
|
||||
/**
|
||||
* @brief Set enhanced URC callback with buffer consumption control
|
||||
* @param enhanced_cb Enhanced callback that can control buffer consumption
|
||||
*/
|
||||
void set_enhanced_urc_cb(enhanced_urc_cb enhanced_cb)
|
||||
{
|
||||
command_cb.enhanced_urc_handler = std::move(enhanced_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -171,6 +207,33 @@ private:
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
|
||||
void exit_cmux_internal(); /*!< Cleanup CMUX */
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
/**
|
||||
* @brief Buffer state tracking for enhanced URC processing
|
||||
*/
|
||||
struct BufferState {
|
||||
size_t total_processed = 0; /*!< Total bytes processed by URC handlers */
|
||||
size_t last_urc_processed = 0; /*!< Last offset processed by URC */
|
||||
bool command_waiting = false; /*!< Whether command is waiting for response */
|
||||
size_t command_start_offset = 0; /*!< Where current command response started */
|
||||
} buffer_state;
|
||||
|
||||
/**
|
||||
* @brief Update buffer state when new data arrives
|
||||
* @param new_data_size Size of new data added to buffer
|
||||
*/
|
||||
void update_buffer_state(size_t new_data_size);
|
||||
|
||||
/**
|
||||
* @brief Create URC buffer information for enhanced handlers
|
||||
* @param data Buffer data pointer
|
||||
* @param consumed Already consumed bytes
|
||||
* @param len New data length
|
||||
* @return UrcBufferInfo structure with complete buffer context
|
||||
*/
|
||||
UrcBufferInfo create_urc_info(uint8_t* data, size_t consumed, size_t len);
|
||||
#endif
|
||||
|
||||
Lock internal_lock{}; /*!< Locks DTE operations */
|
||||
unique_buffer buffer; /*!< DTE buffer */
|
||||
std::shared_ptr<CMux> cmux_term; /*!< Primary terminal for this DTE */
|
||||
@@ -216,6 +279,7 @@ private:
|
||||
struct command_cb {
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
got_line_cb urc_handler {}; /*!< URC callback if enabled */
|
||||
enhanced_urc_cb enhanced_urc_handler {}; /*!< Enhanced URC callback with consumption control */
|
||||
#endif
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
got_line_cb got_line; /*!< Supplied command callback */
|
||||
@@ -223,7 +287,7 @@ private:
|
||||
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 process_line(uint8_t *data, size_t consumed, size_t len, DTE* dte = nullptr); /*!< Lets the processing callback handle one line (processing unit) */
|
||||
bool wait_for_line(uint32_t time_ms) /*!< Waiting for command processing */
|
||||
{
|
||||
return signal.wait_any(command_cb::GOT_LINE, time_ms);
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
#define MPPE_SUPPORT 0
|
||||
|
||||
#define PPP_MAXIDLEFLAG 0
|
||||
#define PPP_MAXIDLEFLAG 100
|
||||
|
||||
#define PRINTPKT_SUPPORT 1
|
||||
#define PPP_PROTOCOLNAME 1
|
||||
|
||||
@@ -104,7 +104,7 @@ static void ppp_link_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
static u32_t ppp_output_cb(struct ppp_pcb_s *pcb, const void *data, u32_t len, void *ctx)
|
||||
static u32_t ppp_output_cb(struct ppp_pcb_s *pcb, u8_t *data, u32_t len, void *ctx)
|
||||
{
|
||||
esp_netif_t *netif = (esp_netif_t *)ctx;
|
||||
if (netif->transmit) {
|
||||
|
||||
@@ -163,6 +163,15 @@ extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce_wrap, bool *pin)
|
||||
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_echo(esp_modem_dce_t *dce_wrap, const bool echo_on)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return command_response_to_esp_err(dce_wrap->dce->set_echo(echo_on));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce_wrap, bool txt)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
|
||||
@@ -65,6 +65,10 @@ void DTE::set_command_callbacks()
|
||||
{
|
||||
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
Scoped<Lock> l(command_cb.line_lock);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Update buffer state when new data arrives
|
||||
update_buffer_state(len);
|
||||
#endif
|
||||
#ifndef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
if (command_cb.got_line == nullptr || command_cb.result != command_result::TIMEOUT) {
|
||||
return false; // this line has been processed already (got OK or FAIL previously)
|
||||
@@ -80,7 +84,7 @@ void DTE::set_command_callbacks()
|
||||
std::memcpy(inflatable.current(), data, len);
|
||||
data = inflatable.begin();
|
||||
}
|
||||
if (command_cb.process_line(data, inflatable.consumed, len)) {
|
||||
if (command_cb.process_line(data, inflatable.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
// at this point we're sure that the data processing hasn't finished,
|
||||
@@ -92,7 +96,7 @@ void DTE::set_command_callbacks()
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
if (command_cb.process_line(data, 0, len)) {
|
||||
if (command_cb.process_line(data, 0, len, this)) {
|
||||
return true;
|
||||
}
|
||||
// cannot inflate and the processing hasn't finishes in the first iteration, but continue
|
||||
@@ -105,7 +109,7 @@ void DTE::set_command_callbacks()
|
||||
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)) {
|
||||
if (command_cb.process_line(data, buffer.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
buffer.consumed += len;
|
||||
@@ -121,7 +125,7 @@ void DTE::set_command_callbacks()
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
}
|
||||
len = primary_term->read(inflatable.current(), len);
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
@@ -150,10 +154,19 @@ void DTE::set_command_callbacks()
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l1(internal_lock);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Track command start
|
||||
buffer_state.command_waiting = true;
|
||||
buffer_state.command_start_offset = buffer_state.total_processed;
|
||||
#endif
|
||||
command_cb.set(got_line, separator);
|
||||
primary_term->write((uint8_t *)command.c_str(), command.length());
|
||||
command_cb.wait_for_line(time_ms);
|
||||
command_cb.set(nullptr);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Track command end
|
||||
buffer_state.command_waiting = false;
|
||||
#endif
|
||||
buffer.consumed = 0;
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
inflatable.deflate();
|
||||
@@ -365,18 +378,54 @@ void DTE::on_read(got_line_cb on_read_cb)
|
||||
});
|
||||
}
|
||||
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len, DTE* dte)
|
||||
{
|
||||
// returning true indicates that the processing finished and lower layers can destroy the accumulated buffer
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
bool consume_buffer = false;
|
||||
if (urc_handler) {
|
||||
consume_buffer = urc_handler(data, consumed + len) != command_result::TIMEOUT;
|
||||
// Call enhanced URC handler if registered
|
||||
if (enhanced_urc_handler && dte) {
|
||||
// Create buffer info for enhanced URC handler
|
||||
UrcBufferInfo buffer_info = dte->create_urc_info(data, consumed, len);
|
||||
|
||||
// Call enhanced URC handler
|
||||
UrcConsumeInfo consume_info = enhanced_urc_handler(buffer_info);
|
||||
|
||||
// Handle consumption control
|
||||
switch (consume_info.result) {
|
||||
case UrcConsumeResult::CONSUME_NONE:
|
||||
// Don't consume anything, continue with command processing
|
||||
break;
|
||||
|
||||
case UrcConsumeResult::CONSUME_PARTIAL:
|
||||
// Consume only specified amount
|
||||
dte->buffer_state.last_urc_processed += consume_info.consume_size;
|
||||
// Adjust data pointers for command processing
|
||||
data += consume_info.consume_size;
|
||||
consumed = (consumed + len) - consume_info.consume_size;
|
||||
len = 0;
|
||||
break;
|
||||
|
||||
case UrcConsumeResult::CONSUME_ALL:
|
||||
// Consume entire buffer
|
||||
dte->buffer_state.last_urc_processed = consumed + len;
|
||||
return true; // Signal buffer consumption
|
||||
}
|
||||
}
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return consume_buffer; // this line has been processed already (got OK or FAIL previously)
|
||||
|
||||
// Fallback to legacy URC handler if enhanced handler not set
|
||||
if (urc_handler) {
|
||||
bool consume_buffer = urc_handler(data, consumed + len) != command_result::TIMEOUT;
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return consume_buffer; // this line has been processed already (got OK or FAIL previously)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Continue with normal command processing
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return false; // Command processing continues
|
||||
}
|
||||
|
||||
if (memchr(data + consumed, separator, len)) {
|
||||
result = got_line(data, consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
@@ -423,3 +472,22 @@ void DTE::extra_buffer::grow(size_t need_size)
|
||||
*/
|
||||
unique_buffer::unique_buffer(size_t size):
|
||||
data(std::make_unique<uint8_t[]>(size)), size(size), consumed(0) {}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
void DTE::update_buffer_state(size_t new_data_size)
|
||||
{
|
||||
buffer_state.total_processed += new_data_size;
|
||||
}
|
||||
|
||||
DTE::UrcBufferInfo DTE::create_urc_info(uint8_t* data, size_t consumed, size_t len)
|
||||
{
|
||||
return {
|
||||
.buffer_start = data,
|
||||
.buffer_total_size = consumed + len,
|
||||
.processed_offset = buffer_state.last_urc_processed,
|
||||
.new_data_size = (consumed + len) - buffer_state.last_urc_processed,
|
||||
.new_data_start = data + buffer_state.last_urc_processed,
|
||||
.is_command_active = buffer_state.command_waiting
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -223,9 +223,9 @@ static int ppp_cmd_iperf(int argc, char **argv)
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
(uint16_t) cfg.source_ip4 & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 24) & 0xFF,
|
||||
cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
@@ -234,6 +234,18 @@ static int ppp_cmd_iperf(int argc, char **argv)
|
||||
iperf_start(&cfg);
|
||||
return 0;
|
||||
}
|
||||
static int restart(int argc, char **argv)
|
||||
{
|
||||
ESP_LOGI("main", "Restarting");
|
||||
esp_restart();
|
||||
return 0;
|
||||
}
|
||||
static int heap_size(int argc, char **argv)
|
||||
{
|
||||
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
|
||||
ESP_LOGI(TAG, "min heap size: %" PRIu32, heap_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_pppd(void)
|
||||
{
|
||||
@@ -286,4 +298,25 @@ void register_pppd(void)
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
|
||||
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "restart",
|
||||
.help = "Restart the program",
|
||||
.hint = NULL,
|
||||
.func = &restart,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
|
||||
}
|
||||
{
|
||||
const esp_console_cmd_t heap_cmd = {
|
||||
.command = "heap",
|
||||
.help = "Get minimum size of free heap memory that was available during program execution",
|
||||
.hint = NULL,
|
||||
.func = &heap_size,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&heap_cmd));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.1.0"
|
||||
version: "^2"
|
||||
override_path: "../../.."
|
||||
espressif/iperf-cmd: "^0.1.1"
|
||||
|
||||
@@ -2,7 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../.." "../../../mbedtls_cxx")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ota_test)
|
||||
|
||||
14
components/esp_modem/test/target_ota/main/idf_component.yml
Normal file
14
components/esp_modem/test/target_ota/main/idf_component.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: '^2'
|
||||
override_path: ../../..
|
||||
espressif/mbedtls_cxx:
|
||||
version: '*'
|
||||
override_path: ../../../../mbedtls_cxx
|
||||
idf:
|
||||
version: '>=4.1.0'
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
@@ -1,4 +1,4 @@
|
||||
CONFIG_TEST_DEVICE_MODEM_GENERIC=y
|
||||
CONFIG_TEST_OTA_URI="https://raw.githubusercontent.com/espressif/esp-protocols/master/components/esp_modem/test/target_ota/bin/blink.bin"
|
||||
CONFIG_TEST_OTA_CA_CERT="MIIEvjCCA6agAwIBAgIQBtjZBNVYQ0b2ii+nVCJ+xDANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaME8xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBSU0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6aqXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddng9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuWraKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGBAfr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21reacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBgjCCAX4wEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUt2ui6qiqhIx56rTaD5iyxZV2ufQwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMD0GA1UdIAQ2MDQwCwYJYIZIAYb9bAIBMAcGBWeBDAEBMAgGBmeBDAECATAIBgZngQwBAgIwCAYGZ4EMAQIDMA0GCSqGSIb3DQEBCwUAA4IBAQCAMs5eC91uWg0Kr+HWhMvAjvqFcO3aXbMM9yt1QP6FCvrzMXi3cEsaiVi6gL3zax3pfs8LulicWdSQ0/1s/dCYbbdxglvPbQtaCdB73sRD2Cqk3p5BJl+7j5nL3a7hqG+fh/50tx8bIKuxT8b1Z11dmzzp/2n3YWzW2fP9NsarA4h20ksudYbj/NhVfSbCEXffPgK2fPOre3qGNm+499iTcc+G33Mw+nur7SpZyEKEOxEXGlLzyQ4UfaJbcme6ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697EA7sKPPcw7+uvTPyLNhBzPvOk"
|
||||
CONFIG_TEST_OTA_CA_CERT="MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTflMrY="
|
||||
CONFIG_TEST_OTA_CN="github.com"
|
||||
|
||||
152
components/esp_modem/test/target_urc/README.md
Normal file
152
components/esp_modem/test/target_urc/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# ESP Modem Enhanced URC Test
|
||||
|
||||
## Overview
|
||||
|
||||
This test validates the enhanced URC (Unsolicited Result Code) interface with buffer consumption control. It demonstrates the new `set_enhanced_urc()` API that provides granular control over buffer consumption and complete buffer visibility.
|
||||
|
||||
## Test Configuration
|
||||
|
||||
- **Target**: ESP-AT device with HTTP server
|
||||
- **UART**: 115200 baud, 8N1, TX=17, RX=18
|
||||
- **Buffer Size**: 1024 bytes
|
||||
- **Timeout**: 15 seconds
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. Enhanced URC Handler Registration
|
||||
- **Objective**: Verify enhanced URC handler can be registered
|
||||
- **Method**: Call `set_enhanced_urc()` with custom handler
|
||||
- **Expected**: Handler receives `UrcBufferInfo` with complete buffer context
|
||||
|
||||
### 2. Buffer Visibility
|
||||
- **Objective**: Verify URC handler receives complete buffer information
|
||||
- **Method**: Log buffer state information in handler
|
||||
- **Expected**: Handler receives `buffer_start`, `buffer_total_size`, `processed_offset`, `new_data_size`, `is_command_active`
|
||||
|
||||
### 3. Granular Consumption Control
|
||||
- **Objective**: Verify handler can consume partial buffer data
|
||||
- **Method**: Process HTTP URCs line-by-line using `CONSUME_PARTIAL`
|
||||
- **Expected**: Each complete line is consumed individually, remaining data preserved
|
||||
|
||||
### 4. Multi-part Response Handling
|
||||
- **Objective**: Verify handling of chunked HTTP responses
|
||||
- **Method**: Process 9 HTTP chunks from ESP-AT server
|
||||
- **Expected**: All chunks processed correctly with proper offset tracking
|
||||
|
||||
### 5. Transfer Completion Detection
|
||||
- **Objective**: Verify detection of transfer completion message
|
||||
- **Method**: Search for "Transfer completed" in buffer
|
||||
- **Expected**: Completion detected and event group signaled
|
||||
|
||||
### 6. Command State Awareness
|
||||
- **Objective**: Verify handler knows command state
|
||||
- **Method**: Check `is_command_active` flag during processing
|
||||
- **Expected**: Flag correctly reflects command state
|
||||
|
||||
## Test Implementation
|
||||
|
||||
The test uses an ESP-AT device running an HTTP server that sends chunked responses. The enhanced URC handler processes each HTTP URC line individually and detects completion.
|
||||
|
||||
### Key Components
|
||||
|
||||
```cpp
|
||||
// Enhanced URC handler registration
|
||||
set_enhanced_urc(handle_enhanced_urc);
|
||||
|
||||
// Handler implementation
|
||||
static esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo& info)
|
||||
{
|
||||
// Process HTTP URCs with granular consumption control
|
||||
if (line.starts_with("+HTTPCGET:")) {
|
||||
// Consume this line only
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, line_end + 1};
|
||||
}
|
||||
|
||||
// Check for completion
|
||||
if (buffer.find("Transfer completed") != std::string_view::npos) {
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_ALL, 0};
|
||||
}
|
||||
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
### Successful Test Run
|
||||
```
|
||||
I (908) urc_test: Starting Enhanced URC Test
|
||||
I (938) urc_test: Start HTTP server...(0)
|
||||
I (948) urc_test: HTTP GET...(43)
|
||||
I (1228) urc_test: HTTP URC: +HTTPCGET:27,=== Async Response #4 ===
|
||||
I (2778) urc_test: HTTP URC: +HTTPCGET:61,[1/9] [633135 ms] This is a simulated slow server response.
|
||||
I (4288) urc_test: HTTP URC: +HTTPCGET:71,[2/9] [634639 ms] Chunk 1: The ESP-AT HTTP server is demonstrating...
|
||||
I (5788) urc_test: HTTP URC: +HTTPCGET:73,[3/9] [636143 ms] Chunk 2: ...asynchronous chunked transfer encoding...
|
||||
I (7288) urc_test: HTTP URC: +HTTPCGET:72,[4/9] [637647 ms] Chunk 3: ...with artificial delays between chunks...
|
||||
I (8788) urc_test: HTTP URC: +HTTPCGET:74,[5/9] [639151 ms] Chunk 4: ...to simulate real-world network conditions.
|
||||
I (10288) urc_test: HTTP URC: +HTTPCGET:62,[6/9] [640655 ms] Chunk 5: Processing data... please wait...
|
||||
I (11788) urc_test: HTTP URC: +HTTPCGET:63,[7/9] [642159 ms] Chunk 6: Still processing... almost done...
|
||||
I (13288) urc_test: HTTP URC: +HTTPCGET:61,[8/9] [643663 ms] Chunk 7: Final chunk - transfer complete!
|
||||
I (14758) urc_test: HTTP URC: +HTTPCGET:43,[9/9] [645168 ms] === END OF RESPONSE ===
|
||||
I (15258) urc_test: Transfer completed detected in buffer!
|
||||
I (15298) urc_test: Enhanced URC test completed successfully!
|
||||
I (15308) urc_test: The enhanced URC handler successfully processed all HTTP chunks
|
||||
I (15308) urc_test: with granular buffer consumption control
|
||||
```
|
||||
|
||||
### Debug Output (with ESP_LOG_LEVEL_DEBUG)
|
||||
```
|
||||
D (958) urc_test: URC Buffer Info: total_size=43, processed_offset=0, new_data_size=43, command_active=false
|
||||
D (958) urc_test: Buffer content (first 43 chars): AT+HTTPCGET="http://127.0.0.1:8080/async"
|
||||
D (968) urc_test: Other data: AT+HTTPCGET="http://127.0.0.1:8080/async"
|
||||
D (1218) urc_test: URC Buffer Info: total_size=85, processed_offset=43, new_data_size=42, command_active=false
|
||||
D (1228) urc_test: Consuming 40 bytes (line_end=82, processed_offset=43)
|
||||
D (2778) urc_test: Consuming 76 bytes (line_end=158, processed_offset=83)
|
||||
```
|
||||
|
||||
### Failed Test (Timeout)
|
||||
```
|
||||
I (908) urc_test: Starting Enhanced URC Test
|
||||
I (948) urc_test: HTTP GET...(43)
|
||||
E (15385) urc_test: Enhanced URC test timed out
|
||||
I (15385) urc_test: Enhanced URC test done
|
||||
```
|
||||
|
||||
## Test Validation
|
||||
|
||||
### Success Criteria
|
||||
- All 9 HTTP chunks processed successfully
|
||||
- Transfer completion detected within 15 seconds
|
||||
- No buffer arithmetic errors (no negative `new_data_size`)
|
||||
- Proper consumption control (line-by-line processing)
|
||||
|
||||
### Failure Indicators
|
||||
- Test timeout (15 seconds)
|
||||
- Buffer arithmetic errors (negative `new_data_size`)
|
||||
- Missing HTTP chunks
|
||||
- Transfer completion not detected
|
||||
|
||||
## Dependencies
|
||||
|
||||
- ESP-AT device with HTTP server capability
|
||||
- UART connection configured
|
||||
- `CONFIG_ESP_MODEM_URC_HANDLER=y`
|
||||
- FreeRTOS event groups
|
||||
|
||||
## Build and Run
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
## Comparison with Legacy URC
|
||||
|
||||
| Feature | Legacy URC | Enhanced URC |
|
||||
|---------|------------|--------------|
|
||||
| Buffer Consumption | All or nothing | Granular (partial) |
|
||||
| Buffer Visibility | None | Complete context |
|
||||
| Command State | Unknown | Known (`is_command_active`) |
|
||||
| Processing State | Unknown | Tracked (`processed_offset`) |
|
||||
| Multi-part Support | Limited | Full support |
|
||||
@@ -3,5 +3,5 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -3,7 +3,26 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file urc_test.cpp
|
||||
* @brief Enhanced URC (Unsolicited Result Code) Test
|
||||
*
|
||||
* This test demonstrates the new enhanced URC interface with buffer consumption control.
|
||||
* It tests the following features:
|
||||
*
|
||||
* 1. Enhanced URC Handler Registration: Uses set_enhanced_urc() instead of set_urc()
|
||||
* 2. Buffer Visibility: URC handler receives complete buffer information
|
||||
* 3. Granular Consumption Control: Handler can consume none, partial, or all buffer data
|
||||
* 4. Processing State Awareness: Handler knows what data is new vs. already processed
|
||||
* 5. Command State Awareness: Handler knows if a command is currently active
|
||||
*
|
||||
* The test works with ESP-AT HTTP server that sends chunked responses, demonstrating
|
||||
* how the enhanced URC handler can process multi-part responses with precise control
|
||||
* over buffer consumption.
|
||||
*/
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
@@ -67,7 +86,7 @@ public:
|
||||
bool http_get(const std::string &url)
|
||||
{
|
||||
std::string command = "AT+HTTPCGET=\"" + url + "\"\r\n";
|
||||
set_urc(handle_urc);
|
||||
set_enhanced_urc(handle_enhanced_urc);
|
||||
auto ret = dte->write(esp_modem::DTE_Command(command));
|
||||
ESP_LOGI(TAG, "HTTP GET...(%d)", static_cast<int>(ret));
|
||||
return ret > 0;
|
||||
@@ -82,25 +101,88 @@ public:
|
||||
|
||||
static constexpr int transfer_completed = 1;
|
||||
private:
|
||||
static esp_modem::command_result handle_urc(uint8_t *data, size_t len)
|
||||
static esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo &info)
|
||||
{
|
||||
static int start_chunk = 0;
|
||||
static int end_chunk = 0;
|
||||
std::string_view chunk((const char*)data + start_chunk, len - start_chunk);
|
||||
int newline = chunk.find('\n');
|
||||
if (newline == std::string_view::npos) {
|
||||
end_chunk = len; // careful, this grows buffer usage
|
||||
printf(".");
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
// Log buffer information for debugging
|
||||
ESP_LOGD(TAG, "URC Buffer Info: total_size=%zu, processed_offset=%zu, new_data_size=%zu, command_active=%s",
|
||||
info.buffer_total_size, info.processed_offset, info.new_data_size,
|
||||
info.is_command_active ? "true" : "false");
|
||||
|
||||
// Debug: Show buffer content (first 200 chars)
|
||||
if (info.buffer_total_size > 0) {
|
||||
size_t debug_len = std::min(info.buffer_total_size, (size_t)200);
|
||||
ESP_LOGD(TAG, "Buffer content (first %zu chars): %.*s",
|
||||
debug_len, (int)debug_len, (const char*)info.buffer_start);
|
||||
}
|
||||
printf("%.*s\n", newline, (char*)data + start_chunk);
|
||||
start_chunk = end_chunk;
|
||||
// check for the last one
|
||||
constexpr char last_chunk[] = "Transfer completed";
|
||||
if (memmem(data, len, last_chunk, sizeof(last_chunk) - 1) != nullptr) {
|
||||
|
||||
// Create string view of the entire buffer for processing
|
||||
std::string_view buffer((const char*)info.buffer_start, info.buffer_total_size);
|
||||
|
||||
// First, check if we have the completion message anywhere in the buffer
|
||||
if (buffer.find("Transfer completed") != std::string_view::npos) {
|
||||
ESP_LOGI(TAG, "Transfer completed detected in buffer!");
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
// Consume everything
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_ALL, 0};
|
||||
}
|
||||
return esp_modem::command_result::OK;
|
||||
|
||||
// Process from the last processed offset
|
||||
size_t search_start = info.processed_offset;
|
||||
|
||||
// Look for complete lines starting from the processed offset
|
||||
while (search_start < info.buffer_total_size) {
|
||||
size_t line_end = buffer.find('\n', search_start);
|
||||
|
||||
if (line_end == std::string_view::npos) {
|
||||
// No complete line found, wait for more data
|
||||
ESP_LOGD(TAG, "Waiting for more data... (search_start=%zu, total_size=%zu)",
|
||||
search_start, info.buffer_total_size);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
|
||||
// Found a complete line, process it
|
||||
std::string_view line = buffer.substr(search_start, line_end - search_start);
|
||||
|
||||
// Remove carriage return if present
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.remove_suffix(1);
|
||||
}
|
||||
|
||||
// Check if this is an HTTP URC
|
||||
if (line.starts_with("+HTTPCGET:")) {
|
||||
ESP_LOGI(TAG, "HTTP URC: %.*s", (int)line.length(), line.data());
|
||||
|
||||
// Check for transfer completion - look for "Transfer completed" anywhere in the line
|
||||
if (line.find("Transfer completed") != std::string_view::npos) {
|
||||
ESP_LOGI(TAG, "Transfer completed detected!");
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
}
|
||||
|
||||
// Consume this line (including the newline)
|
||||
size_t consume_size = line_end + 1 - info.processed_offset;
|
||||
ESP_LOGD(TAG, "Consuming %zu bytes (line_end=%zu, processed_offset=%zu)",
|
||||
consume_size, line_end, info.processed_offset);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, consume_size};
|
||||
|
||||
} else if (line.starts_with("+HTTPCGET")) {
|
||||
// Partial HTTP URC, don't consume yet
|
||||
ESP_LOGD(TAG, "Partial HTTP URC: %.*s", (int)line.length(), line.data());
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
|
||||
} else if (!line.empty()) {
|
||||
// Other data, log and consume
|
||||
ESP_LOGD(TAG, "Other data: %.*s", (int)line.length(), line.data());
|
||||
size_t consume_size = line_end + 1 - info.processed_offset;
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, consume_size};
|
||||
}
|
||||
|
||||
// Move to next line
|
||||
search_start = line_end + 1;
|
||||
}
|
||||
|
||||
// Processed all available data
|
||||
ESP_LOGD(TAG, "Processed all available data");
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,8 +213,8 @@ extern "C" void app_main(void)
|
||||
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
dte_config.dte_buffer_size = 1024;
|
||||
dte_config.uart_config.tx_io_num = 18;
|
||||
dte_config.uart_config.rx_io_num = 17;
|
||||
dte_config.uart_config.tx_io_num = 17;
|
||||
dte_config.uart_config.rx_io_num = 18;
|
||||
auto uart_dte = esp_modem::create_uart_dte(&dte_config);
|
||||
if (uart_dte == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create UART DTE");
|
||||
@@ -144,15 +226,24 @@ extern "C" void app_main(void)
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting Enhanced URC Test");
|
||||
ESP_LOGI(TAG, "This test demonstrates the new enhanced URC interface with buffer consumption control");
|
||||
|
||||
dce->start_http_server();
|
||||
|
||||
ESP_LOGI(TAG, "Sending HTTP GET request with enhanced URC handler");
|
||||
dce->http_get("http://127.0.0.1:8080/async");
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_event_group, 1, pdTRUE, pdFALSE, pdMS_TO_TICKS(15000));
|
||||
if (bits & DCE::transfer_completed) {
|
||||
ESP_LOGI(TAG, "Request finished!");
|
||||
ESP_LOGI(TAG, "Enhanced URC test completed successfully!");
|
||||
ESP_LOGI(TAG, "The enhanced URC handler successfully processed all HTTP chunks");
|
||||
ESP_LOGI(TAG, "with granular buffer consumption control");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Enhanced URC test timed out");
|
||||
}
|
||||
|
||||
dce->sync();
|
||||
vEventGroupDelete(s_event_group);
|
||||
ESP_LOGI(TAG, "Done");
|
||||
ESP_LOGI(TAG, "Enhanced URC test done");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(mqtt_cxx): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_mqtt_cxx
|
||||
tag_format: mqtt_cxx-v$version
|
||||
version: 0.4.0
|
||||
version: 0.5.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## [0.5.0](https://github.com/espressif/esp-protocols/commits/mqtt_cxx-v0.5.0)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Implement simple unit tests ([f41c4a0a](https://github.com/espressif/esp-protocols/commit/f41c4a0a))
|
||||
- Fix to construct in two steps ([d979e1b3](https://github.com/espressif/esp-protocols/commit/d979e1b3), [#631](https://github.com/espressif/esp-protocols/issues/631))
|
||||
- Add explicit dependency on esp-mqtt if needed ([3d5e11b8](https://github.com/espressif/esp-protocols/commit/3d5e11b8))
|
||||
|
||||
## [0.4.0](https://github.com/espressif/esp-protocols/commits/mqtt_cxx-v0.4.0)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -163,7 +163,20 @@ Client::Client(esp_mqtt_client_config_t const &config) : handler(esp_mqtt_clien
|
||||
throw MQTTException(ESP_FAIL);
|
||||
};
|
||||
CHECK_THROW_SPECIFIC(esp_mqtt_client_register_event(handler.get(), MQTT_EVENT_ANY, mqtt_event_handler, this), mqtt::MQTTException);
|
||||
}
|
||||
|
||||
void Client::start()
|
||||
{
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
CHECK_THROW_SPECIFIC(esp_mqtt_client_start(handler.get()), mqtt::MQTTException);
|
||||
started = true;
|
||||
}
|
||||
|
||||
bool Client::is_started() const noexcept
|
||||
{
|
||||
return started;
|
||||
}
|
||||
|
||||
void Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) noexcept
|
||||
|
||||
@@ -83,8 +83,9 @@ extern "C" void app_main(void)
|
||||
idf::mqtt::Configuration config{};
|
||||
|
||||
MyClient client{broker, credentials, config};
|
||||
client.start();
|
||||
while (true) {
|
||||
constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
vTaskDelay(xDelay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ extern "C" void app_main(void)
|
||||
mqtt::Configuration config{};
|
||||
|
||||
MyClient client{broker, credentials, config};
|
||||
client.start();
|
||||
|
||||
while (true) {
|
||||
constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "0.4.0"
|
||||
version: "0.5.0"
|
||||
description: C++ APIs for ESP-MQTT library
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_mqtt_cxx
|
||||
issues: https://github.com/espressif/esp-protocols/issues
|
||||
@@ -8,4 +8,8 @@ dependencies:
|
||||
espressif/esp-idf-cxx: "^1.0.0-beta"
|
||||
# Required IDF version
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
version: ">=5.0,<6.0"
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -165,6 +165,19 @@ public:
|
||||
*/
|
||||
Client(const esp_mqtt_client_config_t &config);
|
||||
|
||||
/**
|
||||
* @brief Start the underlying esp-mqtt client
|
||||
*
|
||||
* Must be called after the derived class has finished constructing to avoid
|
||||
* events being dispatched to partially constructed objects.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Check whether start() has been called
|
||||
*/
|
||||
[[nodiscard]] bool is_started() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Subscribe to topic
|
||||
*
|
||||
@@ -245,13 +258,13 @@ protected:
|
||||
*/
|
||||
virtual void on_error(const esp_mqtt_event_handle_t event);
|
||||
/**
|
||||
* @brief Called if there is an disconnection event
|
||||
* @brief Called if there is a disconnection event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*/
|
||||
virtual void on_disconnected(const esp_mqtt_event_handle_t event);
|
||||
/**
|
||||
* @brief Called if there is an subscribed event
|
||||
* @brief Called if there is a subscribed event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*/
|
||||
@@ -263,26 +276,26 @@ protected:
|
||||
*/
|
||||
virtual void on_unsubscribed(const esp_mqtt_event_handle_t event);
|
||||
/**
|
||||
* @brief Called if there is an published event
|
||||
* @brief Called if there is a published event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*/
|
||||
virtual void on_published(const esp_mqtt_event_handle_t event);
|
||||
/**
|
||||
* @brief Called if there is an before connect event
|
||||
* @brief Called if there is a before connect event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*/
|
||||
virtual void on_before_connect(const esp_mqtt_event_handle_t event);
|
||||
/**
|
||||
* @brief Called if there is an connected event
|
||||
* @brief Called if there is a connected event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*
|
||||
*/
|
||||
virtual void on_connected(const esp_mqtt_event_handle_t event) = 0;
|
||||
/**
|
||||
* @brief Called if there is an data event
|
||||
* @brief Called if there is a data event
|
||||
*
|
||||
* @param event mqtt event data
|
||||
*
|
||||
@@ -292,5 +305,6 @@ private:
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id,
|
||||
void *event_data) noexcept;
|
||||
void init(const esp_mqtt_client_config_t &config);
|
||||
bool started{false};
|
||||
};
|
||||
} // namespace idf::mqtt
|
||||
|
||||
10
components/esp_mqtt_cxx/test/unit/CMakeLists.txt
Normal file
10
components/esp_mqtt_cxx/test/unit/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
if(${IDF_TARGET} STREQUAL "linux")
|
||||
set(EXTRA_COMPONENT_DIRS "../../../../common_components/linux_compat")
|
||||
set(COMPONENTS main)
|
||||
endif()
|
||||
|
||||
project(esp_mqtt_cxx_host_test)
|
||||
24
components/esp_mqtt_cxx/test/unit/README.md
Normal file
24
components/esp_mqtt_cxx/test/unit/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Test basic mqtt_cxx wrapper operations
|
||||
|
||||
## Warning: Linux target not supported, this test works only on target
|
||||
|
||||
## Example output
|
||||
```
|
||||
I (588) main_task: Started on CPU0
|
||||
I (598) main_task: Calling app_main()
|
||||
Randomness seeded to: 374196253
|
||||
I (608) mqtt_client_cpp: MQTT_EVENT_BEFORE_CONNECT
|
||||
E (618) esp-tls: [sock=54] delayed connect error: Connection reset by peer
|
||||
E (618) transport_base: Failed to open a new connection: 32772
|
||||
E (618) mqtt_client: Error transport connect
|
||||
I (618) mqtt_client_cpp: MQTT_EVENT_ERROR
|
||||
E (628) mqtt_client_cpp: Last error reported from esp-tls: 0x8004
|
||||
E (628) mqtt_client_cpp: Last error captured as transport's socket errno: 0x68
|
||||
I (638) mqtt_client_cpp: Last errno string (Connection reset by peer)
|
||||
I (648) mqtt_client_cpp: MQTT_EVENT_DISCONNECTED
|
||||
===============================================================================
|
||||
All tests passed (6 assertions in 1 test case)
|
||||
|
||||
Test passed!
|
||||
I (5658) main_task: Returned from app_main()
|
||||
```
|
||||
2
components/esp_mqtt_cxx/test/unit/main/CMakeLists.txt
Normal file
2
components/esp_mqtt_cxx/test/unit/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "test_esp_mqtt_cxx.cpp"
|
||||
WHOLE_ARCHIVE)
|
||||
5
components/esp_mqtt_cxx/test/unit/main/idf_component.yml
Normal file
5
components/esp_mqtt_cxx/test/unit/main/idf_component.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
espressif/catch2: "^3.4.0"
|
||||
esp_mqtt_cxx:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
||||
92
components/esp_mqtt_cxx/test/unit/main/test_esp_mqtt_cxx.cpp
Normal file
92
components/esp_mqtt_cxx/test/unit/main/test_esp_mqtt_cxx.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "catch2/catch_session.hpp"
|
||||
#include "catch2/catch_test_macros.hpp"
|
||||
#include "esp_mqtt.hpp"
|
||||
#include "esp_mqtt_client_config.hpp"
|
||||
#include "esp_netif.h"
|
||||
|
||||
namespace mqtt = idf::mqtt;
|
||||
|
||||
namespace {
|
||||
class TestClient final : public mqtt::Client {
|
||||
public:
|
||||
using mqtt::Client::Client;
|
||||
|
||||
bool constructed{false};
|
||||
bool before_connect{false};
|
||||
bool disconnected{false};
|
||||
|
||||
TestClient(const mqtt::BrokerConfiguration &broker, const mqtt::ClientCredentials &credentials, const mqtt::Configuration &config) :
|
||||
mqtt::Client(broker, credentials, config)
|
||||
{
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
private:
|
||||
void on_connected(esp_mqtt_event_handle_t const event) override
|
||||
{
|
||||
CHECK(constructed);
|
||||
}
|
||||
|
||||
void on_data(esp_mqtt_event_handle_t const event) override
|
||||
{
|
||||
CHECK(constructed);
|
||||
}
|
||||
|
||||
void on_before_connect(esp_mqtt_event_handle_t const event) override
|
||||
{
|
||||
CHECK(constructed);
|
||||
before_connect = true;
|
||||
}
|
||||
|
||||
void on_disconnected(const esp_mqtt_event_handle_t event) override
|
||||
{
|
||||
CHECK(constructed);
|
||||
disconnected = true;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Client does not auto-start and can dispatch events after construction", "[esp_mqtt_cxx]")
|
||||
{
|
||||
mqtt::BrokerConfiguration broker{
|
||||
.address = mqtt::URI{std::string{"mqtt://127.0.0.1:1883"}},
|
||||
.security = mqtt::Insecure{}
|
||||
};
|
||||
mqtt::ClientCredentials credentials{};
|
||||
mqtt::Configuration config{};
|
||||
|
||||
TestClient client{broker, credentials, config};
|
||||
|
||||
REQUIRE(client.is_started() == false);
|
||||
|
||||
// start the client and expect disconnection (reset by peer)
|
||||
// since no server's running on this ESP32
|
||||
client.start();
|
||||
CHECK(client.is_started() == true);
|
||||
|
||||
CHECK(client.before_connect);
|
||||
usleep(10000);
|
||||
CHECK(client.disconnected);
|
||||
}
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
Catch::Session session;
|
||||
|
||||
int failures = session.run();
|
||||
if (failures > 0) {
|
||||
printf("TEST FAILED! number of failures=%d\n", failures);
|
||||
return;
|
||||
}
|
||||
printf("Test passed!\n");
|
||||
}
|
||||
4
components/esp_mqtt_cxx/test/unit/sdkconfig.defaults
Normal file
4
components/esp_mqtt_cxx/test/unit/sdkconfig.defaults
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(websocket): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_websocket_client
|
||||
tag_format: websocket-v$version
|
||||
version: 1.5.0
|
||||
version: 1.6.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## [1.6.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.6.0)
|
||||
|
||||
### Features
|
||||
|
||||
- add WEBSOCKET_EVENT_HEADER_RECEIVED (#827) ([18f0d028](https://github.com/espressif/esp-protocols/commit/18f0d028), [#715](https://github.com/espressif/esp-protocols/issues/715))
|
||||
- enhance example with docs, pytest setup, and standalone test server - Add comprehensive README with TOC and quick start - Add pytest setup and certificate generation scripts - Add standalone WebSocket test server with TLS support - Add troubleshooting and multiple testing approaches ([cad527d2](https://github.com/espressif/esp-protocols/commit/cad527d2))
|
||||
- Add websocket HTTP redirect ([ce1560ac](https://github.com/espressif/esp-protocols/commit/ce1560ac))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- remove redundant timeout check in client task loop ([1e83bee4](https://github.com/espressif/esp-protocols/commit/1e83bee4))
|
||||
- fix PING timing - enable periodic PING during active traffic ([7f424325](https://github.com/espressif/esp-protocols/commit/7f424325))
|
||||
- Update linux build docs on required libs ([e52a5757](https://github.com/espressif/esp-protocols/commit/e52a5757))
|
||||
- clean up component dependencies - Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration - Add cJSON dependency to target example's idf_component.yml ([d665e6f1](https://github.com/espressif/esp-protocols/commit/d665e6f1))
|
||||
- fix relying on asprintf() to NULL strp on failure ([54eb0027](https://github.com/espressif/esp-protocols/commit/54eb0027))
|
||||
- Update Remaining Websocket Echo Server (#893) ([18faeb3d](https://github.com/espressif/esp-protocols/commit/18faeb3d))
|
||||
- avoid long stopping time when waiting to auto-reconnect ([2432e41d](https://github.com/espressif/esp-protocols/commit/2432e41d))
|
||||
- Update Websocket Echo Server ([94bd5b07](https://github.com/espressif/esp-protocols/commit/94bd5b07))
|
||||
|
||||
### Updated
|
||||
|
||||
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
|
||||
|
||||
## [1.5.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.5.0)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -11,7 +11,7 @@ endif()
|
||||
if(${IDF_TARGET} STREQUAL "linux")
|
||||
idf_component_register(SRCS "esp_websocket_client.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp-tls tcp_transport http_parser esp_event nvs_flash esp_stubs json
|
||||
REQUIRES esp-tls tcp_transport http_parser esp_event
|
||||
PRIV_REQUIRES esp_timer)
|
||||
else()
|
||||
idf_component_register(SRCS "esp_websocket_client.c"
|
||||
|
||||
@@ -484,6 +484,14 @@ static esp_err_t stop_wait_task(esp_websocket_client_handle_t client)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
|
||||
static void websocket_header_hook(void * client, const char * line, int line_len)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s header:%.*s", __func__, line_len, line);
|
||||
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_HEADER_RECEIVED, line, line_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_handle_t client, const char *scheme)
|
||||
{
|
||||
esp_transport_handle_t trans = esp_transport_list_get_transport(client->transport_list, scheme);
|
||||
@@ -493,6 +501,10 @@ static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_
|
||||
.sub_protocol = client->config->subprotocol,
|
||||
.user_agent = client->config->user_agent,
|
||||
.headers = client->config->headers,
|
||||
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
|
||||
.header_hook = websocket_header_hook,
|
||||
.header_user_context = client,
|
||||
#endif
|
||||
.auth = client->config->auth,
|
||||
.propagate_control_frames = true
|
||||
};
|
||||
@@ -727,10 +739,14 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail);
|
||||
|
||||
if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) {
|
||||
asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME);
|
||||
if (asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME) < 0) {
|
||||
client->config->scheme = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
|
||||
} else if (config->transport == WEBSOCKET_TRANSPORT_OVER_SSL) {
|
||||
asprintf(&client->config->scheme, WS_OVER_TLS_SCHEME);
|
||||
if (asprintf(&client->config->scheme, WS_OVER_TLS_SCHEME) < 0) {
|
||||
client->config->scheme = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
|
||||
}
|
||||
|
||||
@@ -775,7 +791,9 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
}
|
||||
|
||||
if (client->config->scheme == NULL) {
|
||||
asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME);
|
||||
if (asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME) < 0) {
|
||||
client->config->scheme = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
|
||||
}
|
||||
|
||||
@@ -852,26 +870,34 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
|
||||
}
|
||||
if (puri.field_data[UF_SCHEMA].len) {
|
||||
free(client->config->scheme);
|
||||
asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off);
|
||||
if (asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off) < 0) {
|
||||
client->config->scheme = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, return ESP_ERR_NO_MEM);
|
||||
}
|
||||
|
||||
if (puri.field_data[UF_HOST].len) {
|
||||
free(client->config->host);
|
||||
asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off);
|
||||
if (asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off) < 0) {
|
||||
client->config->host = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->host, return ESP_ERR_NO_MEM);
|
||||
}
|
||||
|
||||
|
||||
if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) {
|
||||
free(client->config->path);
|
||||
int aret = -1;
|
||||
if (puri.field_data[UF_QUERY].len == 0) {
|
||||
asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
|
||||
aret = asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
|
||||
} else if (puri.field_data[UF_PATH].len == 0) {
|
||||
asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
|
||||
aret = asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
|
||||
} else {
|
||||
asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
|
||||
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
|
||||
aret = asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
|
||||
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
|
||||
}
|
||||
if (aret < 0) {
|
||||
client->config->path = NULL;
|
||||
}
|
||||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->path, return ESP_ERR_NO_MEM);
|
||||
}
|
||||
@@ -881,7 +907,9 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
|
||||
|
||||
if (puri.field_data[UF_USERINFO].len) {
|
||||
char *user_info = NULL;
|
||||
asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off);
|
||||
if (asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off) < 0) {
|
||||
user_info = NULL;
|
||||
}
|
||||
if (user_info) {
|
||||
char *pass = strchr(user_info, ':');
|
||||
if (pass) {
|
||||
@@ -1140,13 +1168,6 @@ static void esp_websocket_client_task(void *pv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (read_select == 0) {
|
||||
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
|
||||
break;
|
||||
}
|
||||
client->ping_tick_ms = _tick_get_ms();
|
||||
break;
|
||||
case WEBSOCKET_STATE_WAIT_TIMEOUT:
|
||||
|
||||
@@ -1199,6 +1220,8 @@ static void esp_websocket_client_task(void *pv)
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
|
||||
xSemaphoreGiveRecursive(client->lock);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_poll_read().");
|
||||
}
|
||||
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
|
||||
// waiting for reconnection or a request to stop the client...
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user