mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-23 07:12:32 +01:00
Compare commits
177 Commits
eppp-v1.0.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
767a090dc5 | ||
|
|
23ca97d5ec | ||
|
|
77abff48e1 | ||
|
|
7f8494a4b1 | ||
|
|
fdec046428 | ||
|
|
25d54efd3a | ||
|
|
7f4e3690db | ||
|
|
ebc1258ea6 | ||
|
|
dc68bf87cc | ||
|
|
853e8e2810 | ||
|
|
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 | ||
|
|
ae052e5507 | ||
|
|
44524f5de0 | ||
|
|
1ace92c279 | ||
|
|
7a6cf0f9c0 | ||
|
|
54eb002758 | ||
|
|
318e41b3c3 | ||
|
|
6f6237a0cc | ||
|
|
18faeb3dfa | ||
|
|
296123c14e | ||
|
|
4e178f06bd | ||
|
|
5ab7e8327e | ||
|
|
91e7e9fa08 | ||
|
|
ff5d6021be | ||
|
|
2432e41dcb | ||
|
|
870ac91db7 | ||
|
|
94bd5b074a | ||
|
|
db7baaffba | ||
|
|
1ea93a866b | ||
|
|
92e1460721 | ||
|
|
858d38b55f | ||
|
|
6428e68c8e | ||
|
|
94563cdc1f | ||
|
|
e0b8de8f38 | ||
|
|
34b6681576 | ||
|
|
1f7828f629 | ||
|
|
e74db36ebb | ||
|
|
f8d2ed2eed | ||
|
|
8bba3a9734 | ||
|
|
9fbb6e6d0a | ||
|
|
e599cd826b | ||
|
|
e2d36b4fbd | ||
|
|
82a784baf4 | ||
|
|
15140e04c6 | ||
|
|
ce1560acb1 | ||
|
|
e1be830fb7 | ||
|
|
b7cfa31a0b | ||
|
|
845a1e2ef8 | ||
|
|
6ae7a4d2ba | ||
|
|
4d52982a69 | ||
|
|
6318022cda | ||
|
|
13591ade3d | ||
|
|
d09b302b9e | ||
|
|
39e2333adb | ||
|
|
cd57f1bb13 | ||
|
|
497ee2d6d4 | ||
|
|
b2568a3d83 | ||
|
|
58a21e39d0 | ||
|
|
9c7ee07755 | ||
|
|
14d3cb6bd1 | ||
|
|
ccdb45ee94 | ||
|
|
18418c83ff | ||
|
|
ecb7dae502 | ||
|
|
958ff6a584 | ||
|
|
5ea83be7ce | ||
|
|
858f85706d | ||
|
|
b7b8c5dbd7 |
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 }}
|
||||
|
||||
3
.github/workflows/esp_dns__build.yml
vendored
3
.github/workflows/esp_dns__build.yml
vendored
@@ -33,6 +33,9 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: ${{matrix.test.path}}
|
||||
run: |
|
||||
if [[ "${{ matrix.idf_ver }}" == "release-v5.3" || "${{ matrix.idf_ver }}" == "release-v5.4" ]]; then
|
||||
export EXPECTED_WARNING="unknown kconfig symbol 'LWIP_USE_ESP_GETADDRINFO'"
|
||||
fi
|
||||
. ${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
|
||||
|
||||
@@ -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:
|
||||
|
||||
14
.github/workflows/modem_sim__build.yml
vendored
14
.github/workflows/modem_sim__build.yml
vendored
@@ -11,17 +11,21 @@ jobs:
|
||||
build_modem_sim:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem_sim') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout idf
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: espressif/esp-idf
|
||||
ref: 8ad0d3d8f2faab752635bee36070313c47c07a13
|
||||
path: idf
|
||||
- name: Build ESP-AT with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
export IDF_PATH=$GITHUB_WORKSPACE/idf
|
||||
${IDF_PATH}/install.sh
|
||||
cd common_components/modem_sim
|
||||
./install.sh
|
||||
source export.sh
|
||||
|
||||
51
.github/workflows/mosq__build.yml
vendored
51
.github/workflows/mosq__build.yml
vendored
@@ -13,15 +13,16 @@ 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:
|
||||
- idf_ver: "release-v5.1"
|
||||
example: "serverless_mqtt" # serverless_mqtt is not supported due to esp-peer
|
||||
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker"]
|
||||
include:
|
||||
- idf_ver: "latest"
|
||||
# serverless_mqtt is not supported on >=v6.0 (esp-peer dependency)
|
||||
- idf_ver: "release-v5.3"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "release-v5.4"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "release-v5.5"
|
||||
example: "serverless_mqtt"
|
||||
warning: "'octal_mode' is deprecated"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
@@ -35,8 +36,6 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
@@ -64,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
|
||||
@@ -80,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
|
||||
@@ -123,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
|
||||
@@ -168,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
|
||||
@@ -183,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
|
||||
|
||||
5
.github/workflows/tls_cxx__build.yml
vendored
5
.github/workflows/tls_cxx__build.yml
vendored
@@ -25,9 +25,6 @@ jobs:
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ matrix.idf_ver }}" != "latest" ]]; then
|
||||
export EXPECTED_WARNING="unknown kconfig symbol 'MBEDTLS_SSL_COOKIE_C'"
|
||||
fi
|
||||
. ${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,
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
DeprecationWarning: pkg_resources is deprecated as an API
|
||||
Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead.
|
||||
Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead.
|
||||
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
|
||||
warning: unknown kconfig symbol 'LIBC_NEWLIB'
|
||||
|
||||
@@ -6,3 +6,4 @@ dpkt
|
||||
pytest
|
||||
idf_build_apps
|
||||
netifaces
|
||||
esptool>=5.1.0
|
||||
|
||||
51
common_components/modem_sim/README.md
Normal file
51
common_components/modem_sim/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Modem Simulator Component
|
||||
|
||||
A Wi-Fi modem simulator that extends ESP-AT with PPP server capabilities, turning ESP32 into a fully functional Wi-Fi modem. Perfect for testing AT commands and PPP connections without real hardware dependencies.
|
||||
|
||||
## What it does
|
||||
|
||||
- Extends ESP-AT firmware with PPP server functionality
|
||||
- Provides DATA mode for raw IP communication
|
||||
- Enables existing communication stacks (MQTT, HTTP, custom protocols) to work over Wi-Fi
|
||||
- Ideal for testing ESP-Modem library and CI reliability
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cd common_components/modem_sim
|
||||
./install.sh
|
||||
source export.sh
|
||||
idf.py build
|
||||
```
|
||||
|
||||
## Custom Platform/Module
|
||||
|
||||
```bash
|
||||
./install.sh PLATFORM_ESP32S3 WROOM-32
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The `sdkconfig.defaults` includes:
|
||||
- Wi-Fi and Bluetooth enabled
|
||||
- PPP server support
|
||||
- AT commands for HTTP/MQTT
|
||||
- 4MB flash configuration
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
modem_sim/
|
||||
├── install.sh # Installation script
|
||||
├── export.sh # Environment setup
|
||||
├── sdkconfig.defaults # Default configuration
|
||||
├── pppd_cmd/ # Custom PPP commands
|
||||
└── modem_sim_esp32/ # Generated ESP-AT build
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Testing ESP-Modem library without real hardware
|
||||
- Quick Wi-Fi connectivity for existing communication stacks
|
||||
- CI/CD testing with reliable modem simulation
|
||||
- Development and debugging of AT command implementations
|
||||
@@ -75,3 +75,5 @@ CONFIG_AT_HTTP_COMMAND_SUPPORT=y
|
||||
CONFIG_AT_BLE_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLE_HID_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLUFI_COMMAND_SUPPORT=n
|
||||
CONFIG_LWIP_IP_FORWARD=y
|
||||
CONFIG_LWIP_IPV4_NAPT=y
|
||||
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())
|
||||
@@ -7,5 +7,6 @@
|
||||
|
||||
#include "sys/socket.h"
|
||||
#include "socketpair.h"
|
||||
#include "asio_stub.hpp"
|
||||
|
||||
#include_next "asio/detail/config.hpp"
|
||||
|
||||
9
components/asio/port/include/asio_stub.hpp
Normal file
9
components/asio/port/include/asio_stub.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include <signal.h>
|
||||
|
||||
extern "C" int pthread_sigmask(int, const sigset_t *, sigset_t *);
|
||||
@@ -28,7 +28,7 @@ posix_event::posix_event()
|
||||
}
|
||||
} // namespace asio::detail
|
||||
|
||||
extern "C" int pause (void)
|
||||
extern "C" int pause(void)
|
||||
{
|
||||
while (true) {
|
||||
::sleep(UINT_MAX);
|
||||
|
||||
@@ -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.0.1
|
||||
version: 1.1.4
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
# 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
|
||||
|
||||
- Fix test dependency issue on driver ([1ace92c2](https://github.com/espressif/esp-protocols/commit/1ace92c2))
|
||||
- Fix tun netif to (optionally) return errors ([7a6cf0f9](https://github.com/espressif/esp-protocols/commit/7a6cf0f9))
|
||||
|
||||
## [1.1.2](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.2)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Update uart driver deps per IDF > v5.3 ([92e14607](https://github.com/espressif/esp-protocols/commit/92e14607))
|
||||
|
||||
## [1.1.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix getting context for channel API ([94563cdc](https://github.com/espressif/esp-protocols/commit/94563cdc))
|
||||
- Cover more combinations in build tests ([e0b8de8f](https://github.com/espressif/esp-protocols/commit/e0b8de8f))
|
||||
|
||||
## [1.1.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for UART flow control ([cd57f1bb](https://github.com/espressif/esp-protocols/commit/cd57f1bb), [#870](https://github.com/espressif/esp-protocols/issues/870))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix SPI transport to allow already init GPIO ISR ([497ee2d6](https://github.com/espressif/esp-protocols/commit/497ee2d6), [#868](https://github.com/espressif/esp-protocols/issues/868))
|
||||
- Fix stack-overflow in ping task for TUN netif ([b2568a3d](https://github.com/espressif/esp-protocols/commit/b2568a3d), [#867](https://github.com/espressif/esp-protocols/issues/867))
|
||||
|
||||
### Updated
|
||||
|
||||
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi)
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
|
||||
else()
|
||||
set(driver_deps driver)
|
||||
endif()
|
||||
|
||||
@@ -17,9 +17,17 @@
|
||||
#include "esp_check.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if defined(CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS)
|
||||
typedef esp_err_t esp_netif_recv_ret_t;
|
||||
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr) expr
|
||||
#else
|
||||
typedef void esp_netif_recv_ret_t;
|
||||
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr)
|
||||
#endif // CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS
|
||||
|
||||
static const char *TAG = "eppp_tun_netif";
|
||||
|
||||
static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
|
||||
static esp_netif_recv_ret_t tun_input(void *h, void *buffer, unsigned int len, void *eb)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
struct netif *netif = h;
|
||||
@@ -31,11 +39,12 @@ static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
|
||||
ESP_GOTO_ON_FALSE(pbuf_remove_header(p, SIZEOF_ETH_HDR) == 0, ESP_FAIL, err, TAG, "pbuf_remove_header failed");
|
||||
memcpy(p->payload, buffer, len);
|
||||
ESP_GOTO_ON_FALSE(netif->input(p, netif) == ERR_OK, ESP_FAIL, err, TAG, "failed to input packet to lwip");
|
||||
return;
|
||||
return ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_OK);
|
||||
err:
|
||||
if (p) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return ESP_NETIF_OPTIONAL_RETURN_CODE(ret);
|
||||
}
|
||||
|
||||
static err_t tun_output(struct netif *netif, struct pbuf *p)
|
||||
@@ -167,6 +176,9 @@ esp_err_t eppp_check_connection(esp_netif_t *netif)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
|
||||
#if CONFIG_LOG_MAXIMUM_LEVEL > 3
|
||||
config.task_stack_size += 1024; // Some additional stack needed for verbose logs
|
||||
#endif
|
||||
config.count = 100;
|
||||
ESP_LOGI(TAG, "Checking connection on EPPP interface #%" PRIu32, config.interface);
|
||||
ip_addr_t target_addr = {0};
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -208,7 +208,9 @@ static esp_err_t init_master(struct eppp_config_spi_s *config, struct eppp_spi *
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err_dev, TAG, "Failed to config interrupt GPIO");
|
||||
ESP_GOTO_ON_ERROR(gpio_install_isr_service(0), err_dev, TAG, "Failed to install GPIO ISR");
|
||||
ret = gpio_install_isr_service(0);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK || ret == ESP_ERR_INVALID_STATE /* In case the GPIO ISR already installed */,
|
||||
ret, err_dev, TAG, "Failed to install GPIO ISR");
|
||||
ESP_GOTO_ON_ERROR(gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE), err_dev, TAG, "Failed to set ISR type");
|
||||
ESP_GOTO_ON_ERROR(gpio_isr_handler_add(config->intr, gpio_isr_handler, h), err_dev, TAG, "Failed to add ISR handler");
|
||||
return ESP_OK;
|
||||
@@ -468,9 +470,6 @@ err:
|
||||
if (h->ready_semaphore) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
if (h->out_queue) {
|
||||
vQueueDelete(h->out_queue);
|
||||
}
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -80,12 +80,12 @@ static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *confi
|
||||
uart_config.data_bits = UART_DATA_8_BITS;
|
||||
uart_config.parity = UART_PARITY_DISABLE;
|
||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.flow_ctrl = config->flow_control;
|
||||
uart_config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->rx_buffer_size, 0, config->queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART");
|
||||
ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, config->rts_io, config->cts_io), TAG, "Failed to set UART pins");
|
||||
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout");
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -157,6 +157,8 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava
|
||||
esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL);
|
||||
} else {
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent);
|
||||
if (h->parent.channel_rx) {
|
||||
h->parent.channel_rx(netif, channel, in_buf + buf_start + sizeof(struct header), payload_size);
|
||||
}
|
||||
@@ -269,6 +271,7 @@ eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config)
|
||||
ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART");
|
||||
return &h->parent;
|
||||
err:
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,3 +5,7 @@ dependencies:
|
||||
override_path: ../../..
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
4
components/eppp_link/examples/host/sdkconfig.ci.1
Normal file
4
components/eppp_link/examples/host/sdkconfig.ci.1
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
CONFIG_EPPP_LINK_USES_PPP=y
|
||||
3
components/eppp_link/examples/host/sdkconfig.ci.2
Normal file
3
components/eppp_link/examples/host/sdkconfig.ci.2
Normal file
@@ -0,0 +1,3 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
@@ -1,4 +1 @@
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
CONFIG_LWIP_PPP_DEBUG_ON=y
|
||||
|
||||
4
components/eppp_link/examples/slave/sdkconfig.ci.1
Normal file
4
components/eppp_link/examples/slave/sdkconfig.ci.1
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s2"
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
4
components/eppp_link/examples/slave/sdkconfig.ci.2
Normal file
4
components/eppp_link/examples/slave/sdkconfig.ci.2
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
CONFIG_EPPP_LINK_USES_PPP=y
|
||||
@@ -1,6 +1,3 @@
|
||||
CONFIG_LWIP_IP_FORWARD=y
|
||||
CONFIG_LWIP_IPV4_NAPT=y
|
||||
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 1.0.1
|
||||
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:
|
||||
|
||||
@@ -34,6 +34,9 @@ extern "C" {
|
||||
.rx_io = 26, \
|
||||
.queue_size = 16, \
|
||||
.rx_buffer_size = 1024, \
|
||||
.rts_io = -1, \
|
||||
.cts_io = -1, \
|
||||
.flow_control = 0, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_SDIO_CONFIG() \
|
||||
@@ -135,6 +138,9 @@ typedef struct eppp_config_t {
|
||||
int rx_io;
|
||||
int queue_size;
|
||||
int rx_buffer_size;
|
||||
int rts_io;
|
||||
int cts_io;
|
||||
int flow_control;
|
||||
} uart;
|
||||
|
||||
struct eppp_config_sdio_s {
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# The following four lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "6.0")
|
||||
set(test_component_dir $ENV{IDF_PATH}/tools/test_apps/components)
|
||||
else()
|
||||
set(test_component_dir $ENV{IDF_PATH}/tools/unit-test-app/components)
|
||||
endif()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS ${test_component_dir})
|
||||
|
||||
project(test_app)
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
|
||||
else()
|
||||
set(driver_deps driver)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS app_main.c
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES test_utils
|
||||
PRIV_REQUIRES unity nvs_flash esp_netif driver esp_event)
|
||||
PRIV_REQUIRES unity nvs_flash esp_netif esp_event ${driver_deps})
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -103,4 +103,14 @@ menu "esp-modem"
|
||||
Set this to 'y' if you're making changes to the actual sources of
|
||||
the AT command definitions (typically in esp_modem_command_declare.inc)
|
||||
|
||||
config ESP_MODEM_USE_PPP_MODE
|
||||
bool "Use PPP mode"
|
||||
default y
|
||||
select LWIP_PPP_SUPPORT
|
||||
help
|
||||
If enabled, the library can use PPP netif from lwip.
|
||||
This is the default and most common setting.
|
||||
But it's possible to disable it and use only AT commands,
|
||||
in this case it's not required to enable LWIP_PPP_SUPPORT.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -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: "../../../"
|
||||
|
||||
@@ -198,7 +198,7 @@ extern "C" bool modem_stop_network()
|
||||
|
||||
extern "C" void modem_deinit_network()
|
||||
{
|
||||
free(dce);
|
||||
delete dce;
|
||||
dce = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -63,13 +63,12 @@ void ConsoleCommand::RegisterCommand(const char *command, const char *help, cons
|
||||
|
||||
arg_type end = { .end = arg_end(1) };
|
||||
arg_table.emplace_back(end);
|
||||
const esp_console_cmd_t command_def = {
|
||||
.command = command,
|
||||
.help = help,
|
||||
.hint = nullptr,
|
||||
.func = command_func_pts[last_command],
|
||||
.argtable = &arg_table[0]
|
||||
};
|
||||
esp_console_cmd_t command_def = { };
|
||||
command_def.command = command;
|
||||
command_def.help = help;
|
||||
command_def.hint = nullptr;
|
||||
command_def.func = command_func_pts[last_command];
|
||||
command_def.argtable = &arg_table[0];
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&command_def));
|
||||
last_command++;
|
||||
console_commands.emplace_back(this);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "esp_idf_version.h"
|
||||
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
|
||||
#include "esp_modem_usb_config.h"
|
||||
#include "cxx_include/esp_modem_usb_api.hpp"
|
||||
@@ -29,6 +30,12 @@
|
||||
#include "console_helper.hpp"
|
||||
#include "my_module_dce.hpp"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#define GET_WAKEUP_CAUSE() esp_sleep_get_wakeup_causes()
|
||||
#else
|
||||
#define GET_WAKEUP_CAUSE() esp_sleep_get_wakeup_cause()
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
|
||||
#elif defined(CONFIG_EXAMPLE_FLOW_CONTROL_SW)
|
||||
@@ -90,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
|
||||
|
||||
@@ -208,7 +226,7 @@ extern "C" void app_main(void)
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
|
||||
|
||||
switch (esp_sleep_get_wakeup_cause()) {
|
||||
switch (GET_WAKEUP_CAUSE()) {
|
||||
case ESP_SLEEP_WAKEUP_TIMER:
|
||||
if (esp_modem::modem_mode::CMUX_MODE == mode_rtc) {
|
||||
ESP_LOGI(TAG, "Deep sleep reset\n");
|
||||
@@ -374,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;
|
||||
});
|
||||
@@ -428,7 +446,7 @@ extern "C" void app_main(void)
|
||||
const ConsoleCommand SetDeepSleep("set_deep_sleep", "Put esp32 to deep sleep", &deep_sleep_args, sizeof(deep_sleep_args), [&](ConsoleCommand * c) {
|
||||
int tout = c->get_int_of(&DeepSleepArgs::timeout);
|
||||
ESP_LOGI(TAG, "Entering deep sleep for %d sec", tout);
|
||||
ESP_LOGI(TAG, "Wakeup Cause: %d ", esp_sleep_get_wakeup_cause());
|
||||
ESP_LOGI(TAG, "Wakeup Cause: %d ", GET_WAKEUP_CAUSE());
|
||||
esp_deep_sleep(tout * 1000000);
|
||||
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`).
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
set(module_dir "generic_module")
|
||||
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||
set(device_srcs sock_commands_bg96.cpp)
|
||||
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
||||
set(device_srcs sock_commands_sim7600.cpp)
|
||||
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT)
|
||||
set(device_srcs sock_commands_espat.cpp)
|
||||
set(module_dir "espat_module")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
|
||||
@@ -14,4 +18,4 @@ idf_component_register(SRCS "modem_client.cpp"
|
||||
"${command_dir}/sock_dce.cpp"
|
||||
"tcp_transport_at.cpp"
|
||||
"${device_srcs}"
|
||||
INCLUDE_DIRS "." "${command_dir}")
|
||||
INCLUDE_DIRS "." "${command_dir}" "${module_dir}")
|
||||
|
||||
@@ -18,14 +18,44 @@ menu "Example Configuration"
|
||||
bool "SIM7600"
|
||||
help
|
||||
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
|
||||
config EXAMPLE_MODEM_DEVICE_ESPAT
|
||||
bool "ESP-AT"
|
||||
help
|
||||
ESP-AT firmware for ESP32 modules with WiFi connectivity
|
||||
endchoice
|
||||
|
||||
if EXAMPLE_MODEM_DEVICE_ESPAT
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) to connect to.
|
||||
|
||||
config EXAMPLE_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2).
|
||||
endif
|
||||
|
||||
config EXAMPLE_MODEM_APN
|
||||
string "Set MODEM APN"
|
||||
default "internet"
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "sock_commands.hpp"
|
||||
#include "sock_module.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#pragma once
|
||||
@@ -33,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);
|
||||
@@ -62,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;
|
||||
@@ -97,9 +105,11 @@ private:
|
||||
std::shared_ptr<esp_modem::DTE> &dte;
|
||||
};
|
||||
|
||||
class DCE : public ::esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
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
|
||||
@@ -162,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));
|
||||
@@ -183,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);
|
||||
@@ -223,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();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
class Module: public esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
public:
|
||||
|
||||
esp_modem::command_result sync() override;
|
||||
esp_modem::command_result set_echo(bool on) override;
|
||||
esp_modem::command_result set_pdp_context(esp_modem::PdpContext &pdp) override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "sock_commands.hpp"
|
||||
#include "sock_module.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#pragma once
|
||||
@@ -33,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);
|
||||
@@ -62,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;
|
||||
@@ -97,9 +105,11 @@ private:
|
||||
std::shared_ptr<esp_modem::DTE> &dte;
|
||||
};
|
||||
|
||||
class DCE : public ::esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
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"
|
||||
@@ -140,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));
|
||||
@@ -162,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);
|
||||
@@ -203,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;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
using Module = esp_modem::GenericModule;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.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 "mqtt.eclipseprojects.io"
|
||||
#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,49 +117,78 @@ 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 = "mqtts://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, NULL);
|
||||
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;
|
||||
}
|
||||
while (1) {
|
||||
while (true) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include "sock_commands.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
#include "sock_module.hpp"
|
||||
|
||||
static const char *TAG = "sock_commands_espat";
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
command_result Module::sync()
|
||||
{
|
||||
return dce_commands::generic_command_common(dte.get(), "AT\r\n");
|
||||
}
|
||||
|
||||
command_result Module::set_echo(bool on)
|
||||
{
|
||||
return dce_commands::generic_command_common(dte.get(), "ATE0\r\n");
|
||||
}
|
||||
|
||||
command_result Module::set_pdp_context(PdpContext &pdp)
|
||||
{
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
command_result net_open(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
|
||||
// Set WiFi mode to station
|
||||
auto ret = dce_commands::generic_command(t, "AT+CWMODE=1\r\n", "OK", "ERROR", 5000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to set WiFi mode");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Connect to WiFi network
|
||||
std::string wifi_cmd = "AT+CWJAP=\"" CONFIG_EXAMPLE_WIFI_SSID "\",\"" CONFIG_EXAMPLE_WIFI_PASSWORD "\"\r\n";
|
||||
ret = dce_commands::generic_command(t, wifi_cmd, "OK", "ERROR", 15000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to connect to WiFi");
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(TAG, "WiFi connected successfully");
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
command_result net_close(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// Disconnect from WiFi
|
||||
auto ret = dce_commands::generic_command(t, "AT+CWQAP\r\n", "OK", "ERROR", 5000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGW(TAG, "Failed to disconnect WiFi (may already be disconnected)");
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_close(CommandableIf *t)
|
||||
{
|
||||
return command_result::OK;
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// 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 get_ip(CommandableIf *t, std::string &ip)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
std::string out;
|
||||
auto ret = dce_commands::at_raw(t, "AT+CIFSR\r\n", out, "OK", "ERROR", 5000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Parse station IP from response
|
||||
// Expected format: +CIFSR:STAIP,"192.168.1.100"
|
||||
auto pos = out.find("+CIFSR:STAIP,\"");
|
||||
if (pos != std::string::npos) {
|
||||
pos += 14; // Move past "+CIFSR:STAIP,\""
|
||||
auto end_pos = out.find("\"", pos);
|
||||
if (end_pos != std::string::npos) {
|
||||
ip = out.substr(pos, end_pos - pos);
|
||||
ESP_LOGI(TAG, "Got IP address: %s", ip.c_str());
|
||||
return command_result::OK;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Failed to parse IP address from response");
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result set_rx_mode(CommandableIf *t, int mode)
|
||||
{
|
||||
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(link_id) + "," + std::to_string(mode) + "\r\n";
|
||||
return dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||
}
|
||||
|
||||
} // sock_commands
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
{
|
||||
const int MIN_MESSAGE = 6;
|
||||
size_t actual_len = 0;
|
||||
auto *recv_data = (char *)data;
|
||||
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+CIPRECVDATA:";
|
||||
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) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
// Parse the actual length
|
||||
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "Cannot convert length");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Received: actual len=%zu", actual_len);
|
||||
if (actual_len == 0) {
|
||||
ESP_LOGD(TAG, "No data received");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (actual_len > buffer_size) {
|
||||
ESP_LOGE(TAG, "Data too large: %zu > %zu", actual_len, buffer_size);
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
// Move to the actual data after the comma
|
||||
recv_data = next_comma + 1;
|
||||
auto first_data_len = len - (recv_data - (char *)data);
|
||||
|
||||
if (actual_len > first_data_len) {
|
||||
on_read(recv_data, first_data_len);
|
||||
data_to_recv = actual_len - first_data_len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
on_read(recv_data, actual_len);
|
||||
|
||||
} else if (data_to_recv > len) { // Continue receiving
|
||||
on_read(recv_data, len);
|
||||
data_to_recv -= len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
|
||||
} else if (data_to_recv <= len) { // Last chunk
|
||||
on_read(recv_data, data_to_recv);
|
||||
actual_len = data_to_recv;
|
||||
}
|
||||
|
||||
// Look for "OK" marker after the data
|
||||
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') {
|
||||
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;
|
||||
}
|
||||
|
||||
Responder::ret Responder::send(uint8_t *data, size_t len)
|
||||
{
|
||||
if (send_stat < 3) {
|
||||
// Look for the '>' prompt
|
||||
if (memchr(data, '>', len) == NULL) {
|
||||
if (send_stat++ < 2) {
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
ESP_LOGE(TAG, "Missed '>' prompt");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
// Send the actual data
|
||||
auto written = dte->write(&buffer[0], data_to_send);
|
||||
if (written != data_to_send) {
|
||||
ESP_LOGE(TAG, "Failed to write data: %d/%zu", written, data_to_send);
|
||||
return ret::FAIL;
|
||||
}
|
||||
data_to_send = 0;
|
||||
send_stat = 3;
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::send(std::string_view response)
|
||||
{
|
||||
if (send_stat == 3) {
|
||||
if (response.find("SEND OK") != std::string::npos) {
|
||||
send_stat = 0;
|
||||
return ret::OK;
|
||||
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Send failed");
|
||||
return ret::FAIL;
|
||||
} else if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Send error");
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
if (response.find("ERROR") != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Failed to connect");
|
||||
return ret::FAIL;
|
||||
}
|
||||
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)
|
||||
{
|
||||
ESP_LOGD(TAG, "Response: %.*s", static_cast<int>(response.size()), response.data());
|
||||
|
||||
// Handle WiFi status messages
|
||||
if (response.find("WIFI CONNECTED") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "WiFi connected");
|
||||
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "WiFi disconnected");
|
||||
}
|
||||
|
||||
// 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_LOGD(TAG, "TCP connection closed");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
}
|
||||
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||
{
|
||||
if (state == status::SENDING) {
|
||||
return send(data, len);
|
||||
}
|
||||
if (state == status::RECEIVING) {
|
||||
return recv(data, len);
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
status Responder::pending()
|
||||
{
|
||||
// For ESP-AT, we don't need a pending check like BG96
|
||||
// Just return current status
|
||||
return status::SENDING;
|
||||
}
|
||||
|
||||
} // sock_dce
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT=y
|
||||
@@ -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
|
||||
|
||||
@@ -62,12 +62,16 @@ class Creator {
|
||||
public:
|
||||
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif): dte(std::move(dte)), device(nullptr), netif(esp_netif)
|
||||
{
|
||||
#ifdef CONFIG_ESP_MODEM_USE_PPP_MODE
|
||||
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
|
||||
#endif
|
||||
}
|
||||
|
||||
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif, std::shared_ptr<T_Module> dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif)
|
||||
{
|
||||
#ifdef CONFIG_ESP_MODEM_USE_PPP_MODE
|
||||
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
|
||||
#endif
|
||||
}
|
||||
|
||||
explicit Creator(std::shared_ptr<DTE> dte): dte(std::move(dte)), device(nullptr), netif(nullptr)
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user