Compare commits

..

2 Commits

Author SHA1 Message Date
a66d8a6fc8 bump(modem): 0.1.28 -> 1.0.0 2023-03-16 20:11:02 +01:00
c59e3302b4 fix(common): Changelog to correctly pick references
Also added .cz config for modem components
2023-03-16 20:10:10 +01:00
244 changed files with 1426 additions and 5291 deletions

View File

@ -137,4 +137,4 @@ show_source = True
statistics = True
exclude =
docs/asio/conf_common.py
components/asio/docs/conf_common.py

View File

@ -8,7 +8,7 @@ body:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true

View File

@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: esp-protocols documentation links
url: https://github.com/espressif/esp-protocols#readme
- name: esp-protocols documentation
url: https://espressif.github.io/esp-protocols/
about: Documenation for esp-protocols components.
- name: ESP-IDF Programming Guide
url: https://docs.espressif.com/projects/esp-idf/en/latest/

View File

@ -7,7 +7,7 @@ body:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for esp-protocols [components](https://github.com/espressif/esp-protocols#readme) and the issue is not addressed there.
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true

View File

@ -1,48 +0,0 @@
name: "examples: build/host-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_all_examples:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.1"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Build with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
build_and_run_on_host:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
name: Build and run examples on linux
strategy:
matrix:
idf_ver: ["latest"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Build with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
python ./ci/build_apps.py examples/mqtt -l -t linux
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
grep 'MQTT_EVENT_DATA' test.log

View File

@ -13,33 +13,122 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{ matrix.test.path }}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -m components/mdns/.build-test-rules.yml -d
# Build specific configs for test targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }}
cd components/mdns/${{ matrix.test.path }}
for dir in `ls -d build_esp32_*`; do
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
for dir in `ls -d build_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v3
if: ${{ matrix.idf_target }} == "esp32"
with:
name: mdns_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: components/mdns/${{ matrix.test.path }}/artifacts.zip
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ matrix.test.path }}/artifacts.zip
if-no-files-found: error
build_mdns_app:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Build Test Apps
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Build Environment
working-directory: components/mdns/tests/test_apps
run: |
. ${IDF_PATH}/export.sh
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
working-directory: components/mdns/tests/test_apps
run: |
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --pytest-apps
- name: Merge binaries with IDF-${{ matrix.idf_ver }}
working-directory: components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
- uses: actions/upload-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: |
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/bootloader/bootloader.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/partition_table/partition-table.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.bin
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/*.elf
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/flasher_args.json
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.h
components/mdns/tests/test_apps/build_${{ matrix.idf_target }}_default/config/sdkconfig.json
if-no-files-found: error
target_test_apps_mdns:
# Skip running on forks since it won't have access to secrets
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push' )
name: Target Test Apps
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
needs: build_mdns_app
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
steps:
- name: Clear repository
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
- uses: actions/checkout@v3
- uses: actions/download-artifact@v2
with:
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/mdns/tests/test_apps/build
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Download Test apps to target
run: |
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/mdns/tests/test_apps/build/flash_image.bin
- name: Run Example Test on target
working-directory: components/mdns/tests/test_apps
run: |
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{matrix.idf_target}}
- uses: actions/upload-artifact@v2
if: always()
with:
name: test_apps_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: components/mdns/tests/test_apps/*.xml
target_tests_mdns:
# Skip running on forks since it won't have access to secrets
if: |
@ -50,7 +139,7 @@ jobs:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
needs: build_mdns
runs-on:
- self-hosted
@ -62,14 +151,14 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: components/mdns/${{ matrix.test.path }}/ci/
path: ${{ matrix.test.path }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
working-directory: components/mdns/${{ matrix.test.path }}
working-directory: ${{ matrix.test.path }}
run: |
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
@ -81,4 +170,4 @@ jobs:
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: components/mdns/${{ matrix.test.path }}/*.xml
path: ${{ matrix.test.path }}/*.xml

View File

@ -12,7 +12,7 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Host test
runs-on: ubuntu-20.04
container: espressif/idf:release-v5.1
container: espressif/idf:latest
steps:
- name: Checkout esp-protocols

View File

@ -22,8 +22,6 @@ jobs:
example: modem_tcp_client
- idf_ver: "release-v4.3"
example: modem_tcp_client
- idf_ver: "release-v4.4"
example: modem_tcp_client
include:
- idf_ver: "release-v4.2"
skip_config: usb
@ -50,7 +48,7 @@ jobs:
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
cd $GITHUB_WORKSPACE/protocols
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/examples/.build-test-rules.yml
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }}
host_test_esp_modem:
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
@ -64,7 +62,7 @@ jobs:
COMP_DIR: esp-protocols/components/esp_modem
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
uses: actions/checkout@master
with:
path: esp-protocols
@ -93,8 +91,6 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
name: Run gcovr on esp modem host test
runs-on: ubuntu-22.04
permissions:
contents: write
container: espressif/idf:release-v4.3
env:
lwip: lwip-2.1.2
@ -106,11 +102,11 @@ jobs:
uses: actions/checkout@v3
with:
path: esp-protocols
repository: ''
persist-credentials: false
- name: Build and Test
shell: bash
run: |
apt-get update
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip
apt-get install -y rsync
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
@ -130,9 +126,10 @@ jobs:
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/*
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml
mkdir modem_coverage_report
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report
cp -rf modem_coverage_report $GITHUB_WORKSPACE
mkdir docs_gcovr
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr
touch docs_gcovr/.nojekyll
- name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
@ -145,6 +142,7 @@ jobs:
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
@ -152,7 +150,28 @@ jobs:
uses: actions/upload-artifact@v3
if: always()
with:
name: modem_coverage_report
name: docs_gcovr
path: |
${{ env.COMP_DIR }}/modem_coverage_report
${{ env.COMP_DIR }}/docs_gcovr
if-no-files-found: error
# show_report_data:
# name: Publish-Results
# if: github.ref == 'refs/heads/master' || github.repository != 'espressif/esp-protocols'
# runs-on: ubuntu-22.04
# needs: gcovr_analyzer_esp_modem
# steps:
# - name: Checkout 🛎️
# uses: actions/checkout@v3
# with:
# persist-credentials: false
# - name: Download Artifacts
# uses: actions/download-artifact@v1
# with:
# name: docs_gcovr
#
# - name: Deploy generated docs
# uses: JamesIves/github-pages-deploy-action@v4
# with:
# branch: gh-pages
# folder: 'docs_gcovr'

View File

@ -58,7 +58,7 @@ jobs:
needs: build_esp_modem_tests
runs-on:
- self-hosted
- modem
- BrnoRPI-GH006
env:
TEST_DIR: components/esp_modem/${{ matrix.test.path }}
steps:

View File

@ -12,17 +12,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.base_ref }}
fetch-depth: 20
uses: actions/checkout@v2
- name: Fetch head and base refs
# This is necessary for pre-commit to check the changes in the PR branch (and to set origin/HEAD and HEAD refs)
# This is necessary for pre-commit to check the changes in the PR branch
run: |
git fetch origin ${{ github.event.pull_request.head.sha }}:pr_ref
git checkout pr_ref
git remote set-head origin --auto
git merge-base origin/HEAD HEAD || ( echo "Your PR is far behind origin/HEAD, please rebase" && exit 1 )
git fetch origin ${{ github.base_ref }}:base_ref
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_ref
- name: Set up Python environment
uses: actions/setup-python@master
with:
@ -31,12 +26,13 @@ jobs:
run: |
pip install pre-commit
pre-commit install-hooks
pre-commit install --hook-type commit-msg --hook-type pre-push
- name: Run pre-commit and check for any changes
run: |
echo "Commits being checked:"
git log --oneline --no-decorate origin/HEAD..HEAD
git log --oneline --no-decorate base_ref..pr_ref
echo ""
if ! pre-commit run --from-ref origin/HEAD --to-ref HEAD --hook-stage manual --show-diff-on-failure ; then
if ! pre-commit run --from-ref base_ref --to-ref pr_ref --show-diff-on-failure ; then
echo ""
echo "::notice::It looks like the commits in this PR have been made without having pre-commit hooks installed."
echo "::notice::Please see https://github.com/espressif/esp-protocols/CONTRIBUTING.md for instructions."

View File

@ -1,45 +0,0 @@
name: Publish coverage report to Github Pages
on:
workflow_run:
workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"]
types:
- completed
branches:
- master
jobs:
publish_github_pages:
runs-on: ubuntu-latest
if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Download Websocket Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: websocket__build-host-tests.yml
workflow_conclusion: success
name: websocket_coverage_report
path: websocket_coverage_report_artifact
- name: Download Modem Artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: modem__build-host-tests.yml
workflow_conclusion: success
name: modem_coverage_report
path: modem_coverage_report_artifact
- name: Merge HTML files
run: |
echo "<html><body>" > index.html
cat modem_coverage_report_artifact/index.html >> index.html
cat websocket_coverage_report_artifact/index.html >> index.html
echo "</body></html>" >> index.html
mkdir coverage_report
mv index.html coverage_report
- name: Deploy generated docs
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: coverage_report

View File

@ -5,89 +5,66 @@ on:
branches:
- master
env:
DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_DEPLOY_URL_BASE }}
DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_DEPLOY_SERVER }}
DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_DEPLOY_SERVER_USER }}
DOCS_DEPLOY_KEY: ${{ secrets.DOCS_DEPLOY_PRIVATEKEY }}
DOCS_DEPLOY_PATH_ORIG : ${{ secrets.DOCS_DEPLOY_PATH }}
jobs:
publish:
name: Publish Tag, Release, Docs, Component
docs_build:
name: Docs-Build-And-Upload
runs-on: ubuntu-latest
# Skip running on forks since it won't have access to secrets
if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
uses: actions/checkout@master
with:
persist-credentials: false
fetch-depth: 0
submodules: recursive
token: "${{ secrets.GITHUB_TOKEN }}"
- name: Check for version update
shell: bash
run: ./ci/detect_component_bump
- name: Tag merge commit
if: env.BUMP_VERSION != ''
uses: anothrNick/github-tag-action@1.61.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CUSTOM_TAG: ${{ env.BUMP_TAG }}
- name: Create Release
if: env.BUMP_VERSION != ''
uses: softprops/action-gh-release@v1
with:
body_path: "release_notes.md"
tag_name: ${{ env.BUMP_TAG }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate docs
shell: bash
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs==1.4.1
for comp in `ls components`; do
cd $GITHUB_WORKSPACE/docs/${comp}
if [[ "${{ env.BUMP_COMPONENT }}" == "${comp}" ]]; then
echo "Building specific version of ${comp} (${{ env.BUMP_VERSION }})"
./generate_docs ${{ env.BUMP_VERSION }}
else
echo "Building latest version of ${comp}"
./generate_docs
fi
done
- name: Deploying generated docs
shell: bash
run: |
source $GITHUB_WORKSPACE/docs/utils.sh
add_doc_server_ssh_keys $DOCS_DEPLOY_KEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
export GIT_VER=$(git describe --always)
export GITHUB_REF_NAME=latest
for comp in `ls components`; do
echo "Deploying latest of ${comp}"
export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs/${comp}
export DOCS_DEPLOY_PATH=$DOCS_DEPLOY_PATH_ORIG/${comp}
cd $GITHUB_WORKSPACE/docs/${comp}
deploy-docs
done;
# Deploy docs with version path
if [[ "${{ env.BUMP_VERSION }}" != "" ]]; then
echo "Deploying specific version of ${comp} (${{ env.BUMP_VERSION }})"
cd $GITHUB_WORKSPACE/docs/${{ env.BUMP_COMPONENT }}
export GITHUB_REF_NAME=${{ env.BUMP_VERSION }}
deploy-docs
fi
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs
cd $GITHUB_WORKSPACE/components/esp_modem/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
cd $GITHUB_WORKSPACE/components/mdns/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
cd $GITHUB_WORKSPACE/components/asio/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/asio
cp -r html/. $GITHUB_WORKSPACE/docs/asio
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
echo '<a href="asio/index.html">ASIO</a><br>' >> index.html
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
- name: Upload components to component service
uses: espressif/upload-components-ci-action@v1
uses: espressif/github-actions/upload_components@master
with:
directories: >
components/asio;
components/esp_modem;
components/esp_mqtt_cxx;
components/esp_websocket_client;
components/mdns;
directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio"
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
- name: Deploy generated docs
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: docs

View File

@ -1,86 +0,0 @@
name: Run on host
on:
workflow_call:
inputs:
idf_version:
required: true
type: string
app_name:
type: string
required: true
app_path:
type: string
required: true
component_path:
type: string
required: true
upload_artifacts:
type: boolean
required: true
jobs:
build:
name: Build App
runs-on: ubuntu-20.04
permissions:
contents: write
container: espressif/idf:${{inputs.idf_version}}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
path: esp-protocols
- name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
rm -rf sdkconfig sdkconfig.defaults build
cp sdkconfig.ci.linux sdkconfig.defaults
idf.py build
./build/${{inputs.app_name}}.elf
- name: Build with Coverage Enabled
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
rm -rf build sdkconfig sdkconfig.defaults
cp sdkconfig.ci.coverage sdkconfig.defaults
idf.py fullclean
idf.py build
./build/${{inputs.app_name}}.elf
- name: Run Coverage
shell: bash
run: |
apt-get update && apt-get install -y python3-pip rsync
python -m pip install gcovr
cd $GITHUB_WORKSPACE/${{inputs.component_path}}
gcov `find . -name "*gcda"`
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x ${{inputs.app_name}}_coverage.xml
mkdir ${{inputs.app_name}}_coverage_report
touch ${{inputs.app_name}}_coverage_report/.nojekyll
cp index.html ${{inputs.app_name}}_coverage_report
cp -rf ${{inputs.app_name}}_coverage_report ${{inputs.app_name}}_coverage.xml $GITHUB_WORKSPACE
- name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: esp-protocols/**/${{inputs.app_name}}_coverage.xml
badge: true
fail_below_min: false
format: markdown
hide_branch_rate: false
hide_complexity: false
indicators: true
output: both
thresholds: '60 80'
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
- name: Upload files to artifacts for run-target job
uses: actions/upload-artifact@v3
if: ${{inputs.upload_artifacts}}
with:
name: ${{inputs.app_name}}_coverage_report
path: |
${{inputs.component_path}}/${{inputs.app_name}}_coverage_report
if-no-files-found: error

View File

@ -1,20 +0,0 @@
name: "websocket: build/host-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
host_test_websocket:
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
uses: "./.github/workflows/run-host-tests.yml"
with:
idf_version: "latest"
app_name: "websocket"
app_path: "esp-protocols/components/esp_websocket_client/examples/linux"
component_path: "esp-protocols/components/esp_websocket_client"
upload_artifacts: true

View File

@ -13,32 +13,45 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
idf_ver: ["release-v5.0", "latest"]
idf_target: ["esp32"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
TEST_DIR: components/esp_websocket_client/examples
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
working-directory: ${{ env.TEST_DIR }}
run: |
. ${IDF_PATH}/export.sh
cat sdkconfig.ci >> sdkconfig.defaults
idf.py build
- name: Merge binaries
working-directory: ${{ env.TEST_DIR }}/build
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
python ./ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
- uses: actions/upload-artifact@v3
with:
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/artifacts.zip
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: |
${{ env.TEST_DIR }}/build/bootloader/bootloader.bin
${{ env.TEST_DIR }}/build/partition_table/partition-table.bin
${{ env.TEST_DIR }}/build/*.bin
${{ env.TEST_DIR }}/build/*.elf
${{ env.TEST_DIR }}/build/flasher_args.json
${{ env.TEST_DIR }}/build/config/sdkconfig.h
${{ env.TEST_DIR }}/build/config/sdkconfig.json
if-no-files-found: error
run-target-websocket:
@ -51,36 +64,32 @@ jobs:
strategy:
fail-fast: false
matrix:
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
idf_ver: ["release-v5.0", "latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: components/esp_websocket_client/${{ matrix.test.path }}
TEST_DIR: components/esp_websocket_client/examples
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/build/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Download Example Test to target
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
run: |
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
done
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: components/esp_websocket_client/${{ matrix.test.path }}/*.xml
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/*.xml

View File

@ -43,7 +43,7 @@ repos:
hooks:
- id: eradicate
- repo: https://github.com/espressif/check-copyright/
rev: v1.0.3
rev: v1.0.1
hooks:
- id: check-copyright
args: ['--ignore', 'ci/check_copyright_ignore.txt', '--config', 'ci/check_copyright_config.yaml']
@ -57,12 +57,12 @@ repos:
hooks:
- id: commitizen
- id: commitizen-branch
stages: [push, manual]
stages: [push]
- repo: local
hooks:
- id: commit message scopes
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, common"
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx|examples)\)\:)'
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|websocket|asio|mqtt_cxx)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

View File

@ -24,8 +24,8 @@ When releasing a new component version we have to:
* Deploy the component to component registry
* Update the documentation
This process is not fully automated, the first step needs to be performed manually by project maintainers running the `bump` command. Release procedure is as follows:
* Run `ci/bump [component] [version]` (version number is optional, `cz` would automatically increment it if not present)
* Check the updated `CHANGELOG.md` and the generated bump commit message
* Create a PR
Once the PR is merged, the CI job tags the merge commit, creates a new release, builds and deploys documentation and the new component to the component registry
This process is not fully automated, the first three steps need to be performed manually by project maintainers running the `bump` command (from within this repository, rather than forks, to publish the release `tag`). Release procedure is as follows:
* Create a branch in this repository (not from fork)
* Run `cz bump [version]` (version number is optional, `cz` would automatically increment it if not present)
* Check the updated `CHANGELOG.md`
* Create and merge the branch to master

View File

@ -1,35 +1,26 @@
# Collection of protocol components for ESP-IDF
## How to use
The [ESP-Protocols](https://github.com/espressif/esp-protocols) repository contains a collection of protocol components for [ESP-IDF](https://github.com/espressif/esp-idf).
Additionally, each component is available in [IDF Component Registry](https://components.espressif.com).
Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
[Documentation of esp-protocol](https://espressif.github.io/esp-protocols)
## Components
### esp_modem
* Brief introduction [README](components/esp_modem/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
### mDNS
* Brief introduction [README](components/mdns/README.md)
* Full html [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
* Full html [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html)
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
### esp_websocket_client
* Brief introduction [README](components/esp_websocket_client/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_websocket_client/docs/latest/index.html)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
### ASIO port
* Brief introduction [README](components/asio/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
### esp_mqtt_cxx
* Brief introduction [README](components/esp_mqtt_cxx/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_mqtt_cxx/docs/latest/index.html)
* Full html [documentation](https://espressif.github.io/esp-protocols/asio/index.html)

View File

@ -7,6 +7,7 @@ This file is used in CI for esp-protocols build tests
import argparse
import os
import sys
from pathlib import Path
from idf_build_apps import build_apps, find_apps, setup_logging
from idf_build_apps.constants import SUPPORTED_TARGETS
@ -23,35 +24,27 @@ if __name__ == '__main__':
default='all',
help='Build apps for given target',
)
parser.add_argument('-r', '--rules', nargs='*', default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'], help='Rules how to treat configs')
parser.add_argument('-m', '--manifests', nargs='*', default=[], help='list of manifest files')
parser.add_argument('-d', '--delete', action='store_true', help='Delete build artifacts')
parser.add_argument('-c', '--recursive', action='store_true', help='Build recursively')
parser.add_argument('-l', '--linux', action='store_true', help='Include linux build (dont check warnings)')
args = parser.parse_args()
IDF_PATH = os.environ['IDF_PATH']
# Compose the ignore warning strings from the global list and from the environment
ignore_warning_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),'ignore_build_warnings.txt')
ignore_warning = open(ignore_warning_file).read().rstrip('\n').split('\n')
if 'EXPECTED_WARNING' in os.environ:
ignore_warning += os.environ['EXPECTED_WARNING'].split('\n')
if args.linux:
SUPPORTED_TARGETS.append('linux')
ignore_warning = 'warning: ' # Ignore all common warnings on linux builds
print(args.paths)
setup_logging(2)
apps = find_apps(
args.paths,
recursive=args.recursive,
recursive=True,
target=args.target,
build_dir='build_@t_@w',
config_rules_str=args.rules,
config_rules_str=[
'sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'
],
build_log_path='build_log.txt',
size_json_path='size.json' if not args.linux else None,
size_json_path='size.json',
check_warnings=True,
preserve=not args.delete,
manifest_files=args.manifests,
preserve=True,
manifest_files=[
str(p) for p in Path('.').glob('**/.build-test-rules.yml')
],
default_build_targets=SUPPORTED_TARGETS,
manifest_rootpath='.',
)
@ -63,5 +56,5 @@ if __name__ == '__main__':
build_apps(apps,
dry_run=False,
keep_going=False,
ignore_warning_strs=ignore_warning)
)
ignore_warning_strs=os.environ['EXPECTED_WARNING']
if 'EXPECTED_WARNING' in os.environ else None))

23
ci/bump
View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
if [ -z "$1" ]; then
echo "Usage: bump component [version]"
exit 1;
fi
comp=$1; shift;
cd components/${comp}
if ! cz bump --dry-run; then
echo "Commitizen bump commad failed!"
exit 1;
fi
cz_bump_out=`cz bump --files-only "$@"`
commit_title=`echo "${cz_bump_out}" | head -1`
commit_body=`cat ../../release_notes.txt`
git add -u .
git commit -m $"${commit_title}
${commit_body}"

View File

@ -93,13 +93,6 @@ def main():
updated_changelog.write(orig_items)
git.add(filename)
# write the current changelog entry to a local text file (removing links, captions and extra newlines)
changelog = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', changelog)
changelog = re.sub(r'\#\#[\#\s]*(.+)', r'\1', changelog)
changelog = re.sub(r'\n\n', '\n', changelog)
with open(os.path.join(root_path, 'release_notes.txt'), 'w') as release_notes:
release_notes.write(changelog)
if __name__ == '__main__':
main()

View File

@ -29,7 +29,6 @@ DEFAULT:
# this section sets the default license for examples and unit tests of components
examples_and_unit_tests:
include:
- 'examples/**'
- 'components/**/examples/**'
- 'components/**/test/**'
- 'components/**/tests/**'
@ -58,7 +57,7 @@ slim_modem_examples:
ignore:
perform_check: no
include:
- '**/docs/**'
- 'components/**/docs/**'
- 'components/esp_modem/port/linux/**'
- 'components/asio/examples/**'
- 'components/mdns/**/esp_system_protocols_linux/**'

View File

@ -1,41 +0,0 @@
#!/usr/bin/env bash
set -e
if ! git show -s | grep -q '^Merge'; then
echo "Not a merge commit"
exit 0;
fi
for comp in `ls components`; do
if git log -1 -m --name-only --pretty="" | grep -q components/${comp}/idf_component.yml; then
echo "${comp}: Component version file has changed"
version=`grep version: components/${comp}/.cz.yaml`
version=${version#*version: }
tag_format=`grep tag_format: components/${comp}/.cz.yaml`
tag_format=${tag_format#*tag_format: }
eval tag=$tag_format
# check if the tag is already created
if [ $(git tag -l "$tag") ]; then
echo "${comp}: version (${tag}) already exits"
else
echo "${comp}: Component version has been updated to ${version}"
# creates release notes from the last entry (between first two "## sections")
awk '/^## \[/{a++};{if(a==1){print}}' components/${comp}/CHANGELOG.md > release_notes.md
echo "BUMP_VERSION=${version}"
echo "BUMP_COMPONENT=${comp}"
echo "BUMP_TAG=${tag}"
# export the findings to github env, so it could be used in other jobs
echo "BUMP_VERSION=${version}" >> "$GITHUB_ENV"
echo "BUMP_COMPONENT=${comp}" >> "$GITHUB_ENV"
echo "BUMP_TAG=${tag}" >> "$GITHUB_ENV"
exit 0;
fi
fi
done
echo "No changes in component version file"

View File

@ -1 +0,0 @@
DeprecationWarning: pkg_resources is deprecated as an API

View File

@ -1,272 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "osal/osal_api.h"
static uint64_t s_semaphore_data = 0;
typedef enum queue_type_tag {
MUTEX_REC,
MUTEX,
SEMA,
QUEUE,
} queue_type_t;
struct generic_queue_handle {
queue_type_t type;
size_t item_size;
void *q;
};
static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint32_t len, uint32_t item_size)
{
struct generic_queue_handle *h = calloc(1, sizeof(struct generic_queue_handle));
h->item_size = len;
h->type = type;
switch (type) {
default:
case QUEUE:
case SEMA:
h->q = osal_queue_create();
break;
case MUTEX:
case MUTEX_REC:
h->q = osal_mutex_create();
break;
}
return h;
}
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
{
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
}
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
{
struct generic_queue_handle *h = xQueue;
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
}
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
{
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
}
uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait)
{
struct generic_queue_handle *h = xQueue;
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
}
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
osal_mutex_give(h->q);
return pdTRUE;
}
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
osal_mutex_give(h->q);
return pdTRUE;
}
return pdFALSE;
}
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
osal_mutex_take(h->q);
return pdTRUE;
}
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
osal_mutex_take(h->q);
return pdTRUE;
}
return pdFALSE;
}
void vQueueDelete( QueueHandle_t xQueue )
{
struct generic_queue_handle *h = xQueue;
if (h->q) {
if (h->type == MUTEX || h->type == MUTEX_REC) {
osal_mutex_delete(h->q);
} else {
osal_queue_delete(h->q);
}
}
free(xQueue);
}
QueueHandle_t xSemaphoreCreateBinary(void)
{
QueueHandle_t sempaphore = xQueueCreate(1, 1);
return sempaphore;
}
QueueHandle_t xSemaphoreCreateMutex(void)
{
return (QueueHandle_t)create_generic_queue(MUTEX, 1, 1);
}
QueueHandle_t xSemaphoreCreateRecursiveMutex(void)
{
return (QueueHandle_t)create_generic_queue(MUTEX_REC, 1, 1);
}
void vTaskDelete(TaskHandle_t *task)
{
if (task == NULL) {
pthread_exit(0);
}
void *thread_rval = NULL;
pthread_join((pthread_t)task, &thread_rval);
}
void vTaskSuspend(void *task)
{
vTaskDelete(task);
}
TickType_t xTaskGetTickCount( void )
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
}
void vTaskDelay( const TickType_t xTicksToDelay )
{
usleep(xTicksToDelay * 1000);
}
void *pthread_task(void *params)
{
struct {
void *const param;
TaskFunction_t task;
bool started;
} *pthread_params = params;
void *const param = pthread_params->param;
TaskFunction_t task = pthread_params->task;
pthread_params->started = true;
task(param);
return NULL;
}
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
{
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
return pdTRUE;
}
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask)
{
pthread_t new_thread = (pthread_t)NULL;
pthread_attr_t attr;
struct {
void *const param;
TaskFunction_t task;
bool started;
} pthread_params = { .param = pvParameters, .task = pvTaskCode};
int res = pthread_attr_init(&attr);
assert(res == 0);
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
assert(res == 0);
res = pthread_create(&new_thread, &attr, pthread_task, &pthread_params);
assert(res == 0);
if (pvCreatedTask) {
*pvCreatedTask = (void *)new_thread;
}
// just wait till the task started so we can unwind params from the stack
while (pthread_params.started == false) {
usleep(1000);
}
return pdTRUE;
}
void xTaskNotifyGive(TaskHandle_t task)
{
}
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
{
return true;
}
TaskHandle_t xTaskGetCurrentTaskHandle(void)
{
return NULL;
}
EventGroupHandle_t xEventGroupCreate( void )
{
return osal_signal_create();
}
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
{
osal_signal_delete(xEventGroup);
}
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
return osal_signal_clear(xEventGroup, uxBitsToClear);
}
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
{
return osal_signal_get(xEventGroup);
}
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
{
return osal_signal_set(xEventGroup, uxBitsToSet);
}
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
{
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
}

View File

@ -1,6 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@ -1,109 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <mutex>
#include <condition_variable>
#include "osal_api.h"
class SignalGroup {
struct SignalGroupInternal {
std::condition_variable notify;
std::mutex m;
uint32_t flags{ 0 };
};
using SignalT = std::unique_ptr<SignalGroupInternal>;
public:
void set(uint32_t bits)
{
std::unique_lock<std::mutex> lock(event_group->m);
event_group->flags |= bits;
event_group->notify.notify_all();
}
uint32_t get()
{
return event_group->flags;
}
void clear(uint32_t bits)
{
std::unique_lock<std::mutex> lock(event_group->m);
event_group->flags &= ~bits;
event_group->notify.notify_all();
}
// waiting for all and clearing if set
bool wait(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] {
if ((flags & event_group->flags) == flags)
{
event_group->flags &= ~flags;
return true;
}
return false;
});
}
// waiting for any bit, not clearing them
bool wait_any(uint32_t flags, uint32_t time_ms)
{
std::unique_lock<std::mutex> lock(event_group->m);
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; });
}
private:
SignalT event_group{std::make_unique<SignalGroupInternal>()};
};
void *osal_signal_create()
{
auto signal = new SignalGroup;
return signal;
}
void osal_signal_delete(void *s)
{
delete static_cast<SignalGroup *>(s);
}
uint32_t osal_signal_clear(void *s, uint32_t bits)
{
auto signal = static_cast<SignalGroup *>(s);
signal->clear(bits);
return signal->get();
}
uint32_t osal_signal_set(void *s, uint32_t bits)
{
auto signal = static_cast<SignalGroup *>(s);
signal->set(bits);
return signal->get();
}
uint32_t osal_signal_get(void *s)
{
auto signal = static_cast<SignalGroup *>(s);
return signal->get();
}
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout)
{
auto signal = static_cast<SignalGroup *>(s);
if (all) {
signal->wait(flags, timeout);
} else {
signal->wait_any(flags, timeout);
}
return signal->get();
}

View File

@ -1,31 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <queue>
#include <mutex>
#include "osal_api.h"
void *osal_mutex_create()
{
auto mut = new std::recursive_mutex();
return mut;
}
void osal_mutex_delete(void *mut)
{
delete static_cast<std::recursive_mutex *>(mut);
}
void osal_mutex_take(void *m)
{
auto mut = static_cast<std::recursive_mutex *>(m);
mut->lock();
}
void osal_mutex_give(void *m)
{
auto mut = static_cast<std::recursive_mutex *>(m);
mut->unlock();
}

View File

@ -1,34 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// queue api
void *osal_queue_create(void);
void osal_queue_delete(void *q);
bool osal_queue_send(void *q, uint8_t *data, size_t len);
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms);
// mutex api
void *osal_mutex_create(void);
void osal_mutex_delete(void *m);
void osal_mutex_take(void *m);
void osal_mutex_give(void *m);
// event groups
void *osal_signal_create(void);
void osal_signal_delete(void *s);
uint32_t osal_signal_clear(void *s, uint32_t bits);
uint32_t osal_signal_set(void *s, uint32_t bits);
uint32_t osal_signal_get(void *s);
uint32_t osal_signal_wait(void *s, uint32_t flags, bool all, uint32_t timeout);
#ifdef __cplusplus
}
#endif

View File

@ -1,78 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cstdint>
#include <vector>
#include <cstring>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "osal_api.h"
template <class T>
class Queue {
public:
void send(std::unique_ptr<T> t)
{
std::lock_guard<std::mutex> lock(m);
q.push(std::move(t));
c.notify_one();
}
std::unique_ptr<T> receive(std::chrono::milliseconds ms)
{
std::unique_lock<std::mutex> lock(m);
while (q.empty()) {
if (c.wait_for(lock, ms) == std::cv_status::timeout) {
return nullptr;
}
}
std::unique_ptr<T> val = std::move(q.front());
q.pop();
return val;
}
private:
std::queue<std::unique_ptr<T>> q{};
mutable std::mutex m{};
std::condition_variable c{};
};
using item_t = std::vector<uint8_t>;
void *osal_queue_create(void)
{
auto *q = new Queue<item_t>();
return q;
}
void osal_queue_delete(void *q)
{
auto *queue = static_cast<Queue<item_t> *>(q);
delete (queue);
}
bool osal_queue_send(void *q, uint8_t *data, size_t len)
{
auto v = std::make_unique<item_t>(len);
v->assign(data, data + len);
auto queue = static_cast<Queue<item_t> *>(q);
queue->send(std::move(v));
return true;
}
bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms)
{
auto queue = static_cast<Queue<item_t> *>(q);
auto v = queue->receive(std::chrono::milliseconds(ms));
if (v != nullptr) {
memcpy(data, (void *)v->data(), len);
return true;
}
return false;
}

View File

@ -27,6 +27,7 @@ idf_component_register(SRCS ${asio_sources}
INCLUDE_DIRS "asio/asio/include" "port/include"
PRIV_INCLUDE_DIRS ${asio_priv_includes}
REQUIRES lwip)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
if(CONFIG_ASIO_SSL_SUPPORT)
if(CONFIG_ASIO_USE_ESP_WOLFSSL)

View File

@ -6,8 +6,8 @@ Asio is a cross-platform C++ library, see https://think-async.com/Asio/. It prov
## Examples
Get started with examples [examples](https://github.com/espressif/esp-protocols/tree/master/components/asio/examples)
Get started with example test :example:`examples <examples/..>`:
## Documentation
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
* View the full [html documentation](https://espressif.github.io/esp-protocols/asio/index.html)

View File

@ -0,0 +1,26 @@
build-docs --target esp32 --language en
cp -rf _build/en/esp32/html .
rm -rf _build __pycache__
# Modifes some version and target fields of index.html
echo "<script type="text/javascript">
window.onload =(function() {
var myAnchor = document.getElementById('version-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'latest';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>" >> html/index.html

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -1,4 +0,0 @@
dependencies:
## Required IDF version
idf: ">=5.0"
espressif/asio: ">=1.0.1"

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(modem): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_modem
tag_format: modem-v$version
version: 1.0.2
version: 1.0.0
version_files:
- idf_component.yml

View File

@ -1,37 +1,5 @@
# Changelog
## [1.0.2](https://github.com/espressif/esp-protocols/commits/modem-v1.0.2)
### Features
- Add factory method for simple creation of custom DCEs ([4cf9e50](https://github.com/espressif/esp-protocols/commit/4cf9e50))
- Support custom transport in AT TCP client example ([ae629ed](https://github.com/espressif/esp-protocols/commit/ae629ed))
### Bug Fixes
- Fix host test race with async read and d'structor ([c89d42f](https://github.com/espressif/esp-protocols/commit/c89d42f))
- More error handling in cmux protocol ([8edbac6](https://github.com/espressif/esp-protocols/commit/8edbac6))
- Fix netif to set PPP config in C++ way ([5287432](https://github.com/espressif/esp-protocols/commit/5287432))
- Fix vfs terminal not to reset read_cb() automatically ([25a35e2](https://github.com/espressif/esp-protocols/commit/25a35e2))
- Fix netif data race causing PPP startup delays ([c8c0507](https://github.com/espressif/esp-protocols/commit/c8c0507), [#308](https://github.com/espressif/esp-protocols/issues/308))
- Added support for inflatable buffer ([cb6e03a](https://github.com/espressif/esp-protocols/commit/cb6e03a), [#272](https://github.com/espressif/esp-protocols/issues/272))
- Fix LoadProhibited after failed CMUX initialization (IDFGH-10845) ([60c87dd](https://github.com/espressif/esp-protocols/commit/60c87dd))
## [1.0.1](https://github.com/espressif/esp-protocols/commits/modem-v1.0.1)
### Bug Fixes
- Support UART clean build with IDF v5.2 ([e620eb5](https://github.com/espressif/esp-protocols/commit/e620eb5))
- enable code coverage publishing to github pages ([4910e89](https://github.com/espressif/esp-protocols/commit/4910e89))
- fix esp_modem build issue ([ab94566](https://github.com/espressif/esp-protocols/commit/ab94566))
- Example to use 1.0.0 ([afb6930](https://github.com/espressif/esp-protocols/commit/afb6930))
- Changelog to correctly pick references ([423e965](https://github.com/espressif/esp-protocols/commit/423e965))
### Updated
- docs(esp_modem): updated documents to show missed topics ([0534853](https://github.com/espressif/esp-protocols/commit/0534853))
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
## [1.0.0](https://github.com/espressif/esp-protocols/commits/modem-v1.0.0)
### Major changes

View File

@ -47,3 +47,15 @@ if(${target} STREQUAL "linux")
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
set_target_properties(${COMPONENT_LIB} PROPERTIES COMPILE_FLAGS -Wno-format)
endif()
if(CONFIG_GCOV_ENABLED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
endif()

View File

@ -16,17 +16,6 @@ menu "esp-modem"
in command mode might come fragmented in rare cases so might need to retry
AT commands.
config ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
bool "Use inflatable buffer in DCE"
default n
help
If enabled we will process the ongoing AT command by growing the current
buffer (if we've run out the preconfigured buffer).
If disabled, we simply report a failure.
Use this if additional allocation is not a problem and you need to reliably process
all commands, usually with sporadically longer responses than the configured buffer.
Could be also used to defragment AT replies in CMUX mode if CMUX_DEFRAGMENT_PAYLOAD=n
config ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP
int "Delay in ms to wait before creating another virtual terminal"
default 0
@ -36,13 +25,4 @@ menu "esp-modem"
The typical reason for failing SABM request without a delay is that
some devices (SIM800) send MSC requests just after opening a new DLCI.
config ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
bool "CMUX to support only short payloads (<128 bytes)"
default n
help
If enabled, the CMUX protocol would only use 1 byte size field.
You can use this option for devices that support only short CMUX payloads
to make the protocol more robust on noisy environments or when underlying
transport gets corrupted often (for example by Rx buffer overflows)
endmenu

View File

@ -17,5 +17,5 @@ Get started with one of the examples:
## Documentation
* Continue with esp-modem [brief overview](https://github.com/espressif/esp-protocols/tree/master/docs/esp_modem/en/README.rst)
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
* Continue with esp-modem [brief overview](docs/README.md)
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all generated output in the proper direction.
# Possible values are: None, LTR, RTL and Context.
# The default value is: None.
OUTPUT_TEXT_DIRECTION = None
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@ -259,6 +267,13 @@ ALIASES =
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
@ -814,25 +829,22 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = \
$(PROJECT_PATH)/../components/esp_modem/include/esp_modem_api.h \
$(PROJECT_PATH)/../components/esp_modem/include/esp_modem_c_api_types.h \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_api.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/esp_modem_config.h \
$(PROJECT_PATH)/../components/esp_modem/include/esp_modem_dce_config.h \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce_factory.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_command_library.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce_module.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dte.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_netif.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_types.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_terminal.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_cmux.hpp \
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce.hpp \
$(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_api_commands.h \
$(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_dce.hpp
INPUT = ../include/esp_modem_api.h \
../include/esp_modem_c_api_types.h \
../include/cxx_include/esp_modem_api.hpp \
../include/cxx_include/esp_modem_dce.hpp \
../include/esp_modem_config.h \
../include/esp_modem_dce_config.h \
../include/cxx_include/esp_modem_dce_factory.hpp \
../include/cxx_include/esp_modem_command_library.hpp \
../include/cxx_include/esp_modem_dce_module.hpp \
../include/cxx_include/esp_modem_dte.hpp \
../include/cxx_include/esp_modem_netif.hpp \
../include/cxx_include/esp_modem_types.hpp \
../include/cxx_include/esp_modem_terminal.hpp \
../include/cxx_include/esp_modem_cmux.hpp \
esp_modem_api_commands.h \
esp_modem_dce.hpp
# The last two are generated
# This tag can be used to specify the character encoding of the source files
@ -1082,6 +1094,12 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
@ -1500,6 +1518,16 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side Javascript for the rendering
@ -1793,6 +1821,15 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
@ -1874,6 +1911,15 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
@ -1971,6 +2017,14 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
@ -2150,6 +2204,14 @@ EXTERNAL_PAGES = YES
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = YES
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
@ -2183,7 +2245,22 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
@ -2397,6 +2474,17 @@ DOT_GRAPH_MAX_NODES = 250
MAX_DOT_GRAPH_DEPTH = 3
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This

View File

@ -0,0 +1,92 @@
# ESP MODEM
This component is used to communicate with modems in the command mode (using AT commands), as well as the data mode
(over PPPoS protocol).
The modem device is modeled with a DCE (Data Communication Equipment) object, which is composed of:
* DTE (Data Terminal Equipment), which abstracts the terminal (currently only UART implemented).
* PPP Netif representing a network interface communicating with the DTE using PPP protocol.
* Module abstracting the specific device model and its commands.
```
+-----+
| DTE |--+
+-----+ | +-------+
+-->| DCE |
+-------+ | |o--- set_mode(command/data)
| Module|--->| |
+-------+ | |o--- send_commands
+->| |
+------+ | +-------+
| PPP |--+
| netif|------------------> network events
+------+
```
## Modem components
### DCE
This is the basic operational unit of the esp_modem component, abstracting a specific module in software,
which is basically configured by
* the I/O communication media (UART), defined by the DTE configuration
* the specific command library supported by the device model, defined with the module type
* network interface configuration (PPPoS config in lwip)
After the object is created, the application interaction with the DCE is in
* issuing specific commands to the modem
* switching between data and command mode
### DTE
Is an abstraction of the physical interface connected to the modem. Current implementation supports only UART
### PPP netif
Is used to attach the specific network interface to a network communication protocol used by the modem. Currently implementation supports only PPPoS protocol.
### Module
Abstraction of the specific modem device. Currently the component supports SIM800, BG96, SIM7600.
## Use cases
Users interact with the esp-modem using the DCE's interface, to basically
* Switch between command and data mode to connect to the internet via cellular network.
* Send various commands to the device (e.g. send SMS)
The applications typically register handlers for network events to receive notification on the network availability and
IP address changes.
Common use cases of the esp-modem are also listed as the examples:
* `examples/pppos_client` -- simple client which reads some module properties and switches to the data mode to connect to a public mqtt broker.
* `examples/modem_console` -- is an example to exercise all possible module commands in a console application.
* `examples/ap_to_pppos` -- this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
## Extensibility
### CMUX
Implementation of virtual terminals is an experimental feature, which allows users to also issue commands in the data mode,
after creating multiple virtual terminals, designating some of them solely to data mode, others solely to command mode.
### DTE's
Currently, we support only UART (and USB as a preview feature), but modern modules support other communication interfaces, such as USB, SPI.
### Other devices
Adding a new device is a must-have requirement for the esp-modem component. Different modules support different commands,
or some commands might have a different implementation. Adding a new device means to provide a new implementation
as a class derived from `GenericModule`, where we could add new commands or modify the existing ones.
## Configuration
Modem abstraction is configurable both compile-time and run-time.
### Component Kconfig
Compile-time configuration is provided using menuconfig. Please check the description for the CMUX mode configuration options.
### Runtime configuration
Is defined using standard configuration structures for `DTE` and `DCE` objects separately. Please find documentation of
* :cpp:class:`esp_modem_dte_config_t`
* :cpp:class:`esp_modem_dce_config_t`

View File

@ -0,0 +1,23 @@
# Cleanup the generated html
rm -rf html
# Cleans example and test build dirs (to reduce the component size before upload)
rm -rf ../examples/ap_to_pppos/build/ ../examples/simple_cmux_client/build/ ../examples/pppos_client/build/ ../examples/linux_modem/build/ ../examples/modem_console/build ../test/host_test/build/ ../test/target/build/
# Generate C++ API header of the DCE
cat ../include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_dce.hpp
# Generate C API header of the modem_api.h
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > esp_modem_api_commands.h
# RST with links to C++ API
cat ../include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > cxx_api_links.rst
# Run doxygen
doxygen
# Generate the docs
python -u -m sphinx.cmd.build -b html . html
# Cleanup the doxygen xml's and temporary headers
rm -rf xml esp_modem_api_commands.h esp_modem_dce.hpp cxx_api_links.rst

View File

@ -0,0 +1,36 @@
# Internal design
## Design decisions
* Use C++ with additional C API
* Use exceptions
- Use macro wrapper over `try-catch` blocks when exceptions off (use `abort()` if `THROW()`)
* Initializes and allocates in the constructor (might throw)
- easier code with exceptions ON, with exceptions OFF alloc/init failures are not treated as runtime error (program aborts)
- break down long initialization in constructor into more private methods
* Implements different devices using inheritance from `GenericModule`, which is the most general implementation of a common modem
- Internally uses templates with device specialization (modeled as `DCE<SpecificModule>`) which could be used as well for some special cases,
such as implantation of a minimal device (ModuleIf), add new AT commands (oOnly in compile time), or using the Module with DTE only (no DCE, no Netif) for sending AT commands without network
## DCE collaboration model
The diagram describes how the DCE class collaborates with DTE, PPP and the device abstraction
![DCE_architecture](DCE_DTE_collaboration.png)
## Terminal inheritance
Terminal is a class which can read or write data, and can handle callbacks when data are available. UART specialization
is provided implementing these method using the uart driver.
## CMUX terminal
The below diagram depicts the idea of using CMUX terminal mode using the CMuxInstance class which is a terminal
(it implements the basic read/write methods) interfacing arbitrary number of virtual terminals,
but at the same time it is also composed of CMux class, which consumes the original terminal and uses its read/write methods
to multiplex the terminal.
![CMUX Terminal](CMux_collaboration.png)

View File

@ -5,3 +5,4 @@ idf_component_register(SRCS "modem_console_main.cpp"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -3,7 +3,7 @@ dependencies:
## Required IDF version
idf: ">=4.1.0"
espressif/esp_modem:
version: "^1.0.0"
version: "^0.1.20"
override_path: "../../../"
espressif/esp_modem_usb_dte:
version: "^1.1.0"

View File

@ -173,7 +173,7 @@ extern "C" void app_main(void)
ESP_LOGI(TAG, "Waiting for USB device connection...");
auto dte = create_usb_dte(&dte_config);
dte->set_error_cb([&](terminal_error err) {
ESP_LOGI(TAG, "error handler %d", (int)err);
ESP_LOGI(TAG, "error handler %d", err);
if (err == terminal_error::DEVICE_GONE) {
exit_signal.set(1);
}
@ -482,7 +482,7 @@ extern "C" void app_main(void)
// wait for exit
exit_signal.wait_any(1, UINT32_MAX);
s_repl->del(s_repl);
ESP_LOGI(TAG, "Exiting...%" PRIu32, esp_get_free_heap_size());
ESP_LOGI(TAG, "Exiting...%d", esp_get_free_heap_size());
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
// USB example runs in a loop to demonstrate hot-plugging and sudden disconnection features.
} // while (1)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -28,7 +28,7 @@ static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "%" PRIu32 " bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 " ms\n",
ESP_LOGI(TAG, "%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
@ -57,7 +57,7 @@ static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
} else {
ESP_LOGI(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 " packet loss, time %" PRIu32 "ms\n",
ESP_LOGI(TAG, "%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks

View File

@ -1,2 +1,3 @@
idf_component_register(SRCS "modem_psm.c"
INCLUDE_DIRS ".")

View File

@ -1,6 +1,7 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
set(EXTRA_COMPONENT_DIRS "../..")

View File

@ -5,10 +5,6 @@
## Overview
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)
This example could be used in two different configurations:
1) Custom TCP transport: Implements a TCP transport in form of AT commands and uses it as custom transport for mqtt client.
2) Localhost listener: Uses standard transports to connect and forwards socket layer data from the client to the modem using AT commands.
### Supported IDF versions
This example is supported from IDF `v5.0`.
This example is supported from IDF `v4.4`.

View File

@ -1,4 +0,0 @@
idf_component_register(SRCS mbedtls_wrap.cpp
tls_transport.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)

View File

@ -1,9 +0,0 @@
# Custom transports
This component is a placeholder of custom transports. It contains mbedTLS cxx wrapper which could be used to create a custom TLS layer on any kind of IO function.
# List of Transports
## TLS Transport
TLS layer on top of any custom transport. Very similar to `ssl_transport` (standard IDF transport), but this is customizable and could work on any kind of transport, not only the BSD socket based tcp transport (like `ssl_transport`).

View File

@ -1,52 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <utility>
#include <span>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
using const_buf = std::span<const unsigned char>;
class Tls {
public:
enum class is_server : bool {};
enum class do_verify : bool {};
Tls();
virtual ~Tls();
bool init(is_server server, do_verify verify);
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
[[nodiscard]] bool set_ca_cert(const_buf crt);
virtual int send(const unsigned char *buf, size_t len) = 0;
virtual int recv(unsigned char *buf, size_t len) = 0;
size_t get_available_bytes();
protected:
mbedtls_ssl_context ssl_{};
mbedtls_x509_crt public_cert_{};
mbedtls_pk_context pk_key_{};
mbedtls_x509_crt ca_cert_{};
mbedtls_ssl_config conf_{};
mbedtls_ctr_drbg_context ctr_drbg_{};
mbedtls_entropy_context entropy_{};
virtual void delay() {}
private:
static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
static int bio_read(void *ctx, unsigned char *buf, size_t len);
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
};

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_transport.h"
/**
* @brief Initializes TLS transport based on mbetls wrapper
*
* @param parent Transport on top of which we run TLS layer
* @return Transport handle on success
*/
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent);

View File

@ -1,134 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ssl.h"
#include "mbedtls_wrap.hpp"
bool Tls::init(is_server server, do_verify verify)
{
const char pers[] = "mbedtls_wrapper";
mbedtls_entropy_init(&entropy_);
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
int ret = mbedtls_ssl_config_defaults(&conf_, server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
if (ret) {
print_error("mbedtls_ssl_config_defaults", ret);
return false;
}
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
mbedtls_ssl_conf_authmode(&conf_, verify == do_verify{true} ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
if (ret) {
print_error("mbedtls_ssl_conf_own_cert", ret);
return false;
}
if (verify == do_verify{true}) {
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
}
ret = mbedtls_ssl_setup(&ssl_, &conf_);
if (ret) {
print_error("mbedtls_ssl_setup", ret);
return false;
}
return true;
}
void Tls::print_error(const char *function, int error_code)
{
static char error_buf[100];
mbedtls_strerror(error_code, error_buf, sizeof(error_buf));
printf("%s() returned -0x%04X\n", function, -error_code);
printf("-0x%04X: %s\n", -error_code, error_buf);
}
int Tls::handshake()
{
int ret = 0;
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, nullptr);
while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
print_error( "mbedtls_ssl_handshake returned", ret );
return -1;
}
delay();
}
return ret;
}
int Tls::bio_write(void *ctx, const unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->send(buf, len);
}
int Tls::bio_read(void *ctx, unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->recv(buf, len);
}
int Tls::write(const unsigned char *buf, size_t len)
{
return mbedtls_ssl_write( &ssl_, buf, len );
}
int Tls::read(unsigned char *buf, size_t len)
{
return mbedtls_ssl_read( &ssl_, buf, len );
}
bool Tls::set_own_cert(const_buf crt, const_buf key)
{
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.data(), crt.size());
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
ret = mbedtls_pk_parse_key(&pk_key_, key.data(), key.size(), nullptr, 0);
if (ret < 0) {
print_error("mbedtls_pk_parse_keyfile", ret);
return false;
}
return true;
}
bool Tls::set_ca_cert(const_buf crt)
{
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.data(), crt.size());
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
return true;
}
Tls::Tls()
{
mbedtls_x509_crt_init(&public_cert_);
mbedtls_pk_init(&pk_key_);
mbedtls_x509_crt_init(&ca_cert_);
}
int Tls::mbedtls_pk_parse_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen, const unsigned char *pwd, size_t pwdlen)
{
return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen, nullptr, nullptr);
}
size_t Tls::get_available_bytes()
{
return ::mbedtls_ssl_get_bytes_avail(&ssl_);
}
Tls::~Tls()
{
::mbedtls_ssl_config_free(&conf_);
::mbedtls_ssl_free(&ssl_);
::mbedtls_pk_free(&pk_key_);
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
}

View File

@ -1,137 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_transport.h"
#include "mbedtls_wrap.hpp"
static const char *TAG = "tls_transport";
class TlsTransport: public Tls {
public:
explicit TlsTransport(esp_transport_handle_t parent) : Tls(), transport_(parent) {}
int send(const unsigned char *buf, size_t len) override;
int recv(unsigned char *buf, size_t len) override;
static bool set_func(esp_transport_handle_t tls_transport);
private:
esp_transport_handle_t transport_{};
int connect(const char *host, int port, int timeout_ms);
void delay() override;
struct transport {
static int connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms);
static int read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms);
static int write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms);
static int close(esp_transport_handle_t t);
static int poll_read(esp_transport_handle_t t, int timeout_ms);
static int poll_write(esp_transport_handle_t t, int timeout_ms);
static int destroy(esp_transport_handle_t t);
};
};
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent)
{
esp_transport_handle_t ssl = esp_transport_init();
auto *tls = new TlsTransport(parent);
esp_transport_set_context_data(ssl, tls);
TlsTransport::set_func(ssl);
return ssl;
}
int TlsTransport::send(const unsigned char *buf, size_t len)
{
return esp_transport_write(transport_, reinterpret_cast<const char *>(buf), len, 0);
}
int TlsTransport::recv(unsigned char *buf, size_t len)
{
int ret = esp_transport_read(transport_, reinterpret_cast<char *>(buf), len, 0);
if (ret == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return ret == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN ? 0 : ret;
}
bool TlsTransport::set_func(esp_transport_handle_t tls_transport)
{
return esp_transport_set_func(tls_transport, TlsTransport::transport::connect, TlsTransport::transport::read, TlsTransport::transport::write, TlsTransport::transport::close, TlsTransport::transport::poll_read, TlsTransport::transport::poll_write, TlsTransport::transport::destroy) == ESP_OK;
}
int TlsTransport::connect(const char *host, int port, int timeout_ms)
{
return esp_transport_connect(transport_, host, port, timeout_ms);
}
void TlsTransport::delay()
{
vTaskDelay(pdMS_TO_TICKS(500));
}
int TlsTransport::transport::connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
tls->init(is_server{false}, do_verify{false});
auto ret = tls->connect(host, port, timeout_ms);
if (ret < 0) {
return ret;
}
return tls->handshake();
}
int TlsTransport::transport::read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
if (tls->get_available_bytes() <= 0) {
int poll = esp_transport_poll_read(t, timeout_ms);
if (poll == -1) {
return ERR_TCP_TRANSPORT_CONNECTION_FAILED;
}
if (poll == 0) {
return ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
}
}
return tls->read(reinterpret_cast<unsigned char *>(buffer), len);
}
int TlsTransport::transport::write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll;
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
ESP_LOGW(TAG, "Poll timeout or error timeout_ms=%d", timeout_ms);
return poll;
}
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return tls->write(reinterpret_cast<const unsigned char *>(buffer), len);
}
int TlsTransport::transport::close(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_close(tls->transport_);
}
int TlsTransport::transport::poll_read(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_read(tls->transport_, timeout_ms);
}
int TlsTransport::transport::poll_write(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_write(tls->transport_, timeout_ms);
}
int TlsTransport::transport::destroy(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_destroy(tls->transport_);
}

View File

@ -6,6 +6,6 @@ endif()
idf_component_register(SRCS "modem_client.cpp"
"sock_dce.cpp"
"tcp_transport_at.cpp"
"${device_srcs}"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -1,10 +1,5 @@
menu "Example Configuration"
config EXAMPLE_CUSTOM_TCP_TRANSPORT
bool "Custom TCP transport"
help
Use custom TCP transport to connect to MQTT broker
choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96

View File

@ -15,17 +15,14 @@
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "sock_dce.hpp"
#include "esp_log.h"
#include "tcp_transport_mbedtls.h"
#include "tcp_transport_at.h"
#define BROKER_URL "mqtt.eclipseprojects.io"
#define BROKER_PORT 8883
static const char *TAG = "modem_client";
static EventGroupHandle_t event_group = NULL;
@ -34,7 +31,7 @@ static const int GOT_DATA_BIT = BIT2;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRId32, base, event_id);
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
@ -73,7 +70,6 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
}
}
extern "C" void app_main(void)
{
@ -105,29 +101,24 @@ extern "C" void app_main(void)
/* create the DCE and initialize network manually (using AT commands) */
auto dce = sock_dce::create(&dce_config, std::move(dte));
if (!dce->init()) {
if (!dce->init_network()) {
ESP_LOGE(TAG, "Failed to setup network");
return;
}
dce->init_sock(8883);
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
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
dce->start_listening(BROKER_PORT);
mqtt_config.session.message_retransmit_timeout = 10000;
#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);
mqtt_config.network.transport = ssl;
mqtt_config.uri = "mqtt://127.0.0.1";
mqtt_config.message_retransmit_timeout = 10000;
#endif
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
if (!dce->start(BROKER_URL, 8883)) {
ESP_LOGE(TAG, "Failed to start DCE");
return;
}
@ -137,16 +128,13 @@ extern "C" void app_main(void)
}
ESP_LOGE(TAG, "Loop exit.. retrying");
// handle disconnections errors
if (!dce->init()) {
if (!dce->init_network()) {
ESP_LOGE(TAG, "Failed to reinit network");
return;
}
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
if (!dce->start("test.mosquitto.org", 1883)) {
ESP_LOGI(TAG, "Network reinitialized, retrying");
}
}
#else
vTaskDelay(portMAX_DELAY);
#endif
}

View File

@ -13,6 +13,8 @@
namespace sock_commands {
//using namespace esp_modem;
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);

View File

@ -6,6 +6,7 @@
#include <charconv>
#include <cstring>
#include <sys/socket.h>
#include "sock_commands.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "sock_dce.hpp"
@ -131,7 +132,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
if (data_to_recv == 0) {
const std::string_view head = "+QIRD: ";
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
if (head_pos == recv_data + len) {
if (head_pos == nullptr) {
return ret::FAIL;
}
@ -159,17 +160,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
recv_data = next_nl + 1;
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
if (actual_len > first_data_len) {
on_read(recv_data, first_data_len);
::send(sock, recv_data, first_data_len, 0);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
on_read(recv_data, actual_len);
::send(sock, recv_data, actual_len, 0);
} else if (data_to_recv > len) { // continue sending
on_read(recv_data, len);
::send(sock, recv_data, len, 0);
data_to_recv -= len;
return ret::NEED_MORE_DATA;
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
on_read(recv_data, data_to_recv);
::send(sock, recv_data, data_to_recv, 0);
actual_len = data_to_recv;
}

View File

@ -6,6 +6,7 @@
#include <charconv>
#include <cstring>
#include <sys/socket.h>
#include "sock_commands.hpp"
#include "cxx_include/esp_modem_command_library_utils.hpp"
#include "sock_dce.hpp"
@ -217,7 +218,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
if (data_to_recv == 0) {
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
if (head_pos == recv_data + len) {
if (head_pos == nullptr) {
return ret::FAIL;
}
@ -245,7 +246,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
ESP_LOGE(TAG, "TOO BIG");
return ret::FAIL;
}
total_len = 0;
size_t total_len = 0;
if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) {
ESP_LOGE(TAG, "cannot convert");
return ret::FAIL;
@ -254,17 +255,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
recv_data = next_nl + 1;
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
if (actual_len > first_data_len) {
on_read(recv_data, first_data_len);
::send(sock, recv_data, first_data_len, 0);
data_to_recv = actual_len - first_data_len;
return ret::NEED_MORE_DATA;
}
on_read(recv_data, actual_len);
::send(sock, recv_data, actual_len, 0);
} else if (data_to_recv > len) { // continue sending
on_read(recv_data, len);
::send(sock, recv_data, len, 0);
data_to_recv -= len;
return ret::NEED_MORE_DATA;
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
on_read(recv_data, data_to_recv);
::send(sock, recv_data, data_to_recv, 0);
actual_len = data_to_recv;
}

View File

@ -120,14 +120,14 @@ 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);
ESP_LOGD(TAG, "select read: modem data available %x", data);
if (!signal.wait(IDLE, 1000)) {
ESP_LOGE(TAG, "Failed to get idle");
close_sock();
return false;
}
if (state != status::IDLE) {
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
ESP_LOGE(TAG, "Unexpected state %d", state);
close_sock();
return false;
}
@ -145,7 +145,7 @@ bool DCE::sock_to_at()
return false;
}
if (state != status::IDLE) {
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
ESP_LOGE(TAG, "Unexpected state %d", state);
close_sock();
return false;
}
@ -192,8 +192,14 @@ bool DCE::accept_sock()
return false;
}
void DCE::start_listening(int port)
void DCE::init_sock(int port)
{
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
esp_vfs_eventfd_register(&config);
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
assert(data_ready_fd > 0);
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
@ -222,7 +228,7 @@ void DCE::start_listening(int port)
}
bool DCE::connect(std::string host, int port)
bool DCE::start(std::string host, int port)
{
dte->on_read(nullptr);
tcp_close();
@ -239,14 +245,8 @@ bool DCE::connect(std::string host, int port)
return true;
}
bool DCE::init()
bool DCE::init_network()
{
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;

View File

@ -9,7 +9,6 @@
#include <cxx_include/esp_modem_dce_factory.hpp>
#include "socket_commands.inc"
#include "sock_commands.hpp"
#include <sys/socket.h>
#pragma once
@ -47,36 +46,9 @@ public:
{
return buffer_size;
}
void clear_offsets()
{
actual_read = 0;
}
size_t data_available()
{
return actual_read;
}
size_t has_data()
{
return total_len;
}
private:
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);
#else
::memcpy(&buffer[actual_read], data, len);
actual_read += len;
#endif
return true;
}
ret recv(uint8_t *data, size_t len);
ret send(uint8_t *data, size_t len);
ret send(std::string_view response);
@ -87,9 +59,6 @@ private:
}
std::array<uint8_t, buffer_size> buffer;
size_t data_to_recv = 0;
size_t actual_read = 0;
size_t total_len = 0;
bool read_again = false;
int &sock;
int &data_ready_fd;
@ -109,101 +78,13 @@ esp_modem::return_type name(__VA_ARGS__);
#undef ESP_MODEM_DECLARE_DCE_COMMAND
bool init();
bool connect(std::string host, int port);
bool init_network();
bool start(std::string host, int port);
void start_listening(int port);
void init_sock(int port);
bool perform_sock();
void set_idle()
{
signal.set(IDLE);
}
bool wait_to_idle(uint32_t ms)
{
if (!signal.wait(IDLE, ms)) {
ESP_LOGE("dce", "Failed to get idle");
return false;
}
if (state != status::IDLE) {
ESP_LOGE("dce", "Unexpected state %d", static_cast<int>(state));
return false;
}
return true;
}
int sync_recv(char *buffer, int len, int timeout_ms)
{
if (!wait_to_idle(timeout_ms)) {
return 0;
}
at.clear_offsets();
state = status::RECEIVING;
uint64_t data;
read(data_ready_fd, &data, sizeof(data));
int max_len = std::min(len, (int)at.get_buf_len());
at.start_receiving(max_len);
if (!signal.wait(IDLE, 500 + timeout_ms)) {
return 0;
}
int ret = at.data_available();
if (ret > 0) {
memcpy(buffer, at.get_buf(), ret);
}
set_idle();
return ret;
}
int sync_send(const char *buffer, size_t len, int timeout_ms)
{
int len_to_send = std::min(len, at.get_buf_len());
if (!wait_to_idle(timeout_ms)) {
return -1;
}
state = status::SENDING;
memcpy(at.get_buf(), buffer, len_to_send);
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
at.start_sending(len_to_send);
if (!signal.wait(IDLE, timeout_ms + 1000)) {
if (state == status::PENDING) {
state = status::IDLE;
} else {
return -1;
}
}
set_idle();
return len_to_send;
}
int wait_to_read(uint32_t ms)
{
if (at.has_data() > 0) {
ESP_LOGD("dce", "Data buffered in modem (len=%d)", at.has_data());
return 1;
}
struct timeval tv = {
.tv_sec = static_cast<time_t>(ms / 1000),
.tv_usec = 0,
};
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(data_ready_fd, &fdset);
int s = select(data_ready_fd + 1, &fdset, nullptr, nullptr, &tv);
if (s == 0) {
return 0;
} else if (s < 0) {
ESP_LOGE("dce", "select error %d", errno);
return -1;
}
if (FD_ISSET(data_ready_fd, &fdset)) {
ESP_LOGD("dce", "select read: modem data available");
return 1;
}
return -1;
}
private:
esp_modem::SignalGroup signal;

View File

@ -1,74 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "sock_dce.hpp"
#include "esp_log.h"
#include "esp_transport.h"
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
if (!dce->connect(host, port)) {
return -1;
}
if (dce->wait_to_idle(timeout_ms)) {
dce->set_idle();
return 1;
}
return -1;
}
static int tcp_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
auto ret = dce->wait_to_read(1000);
if (ret > 0) {
return dce->sync_recv(buffer, len, timeout_ms);
}
return ret;
}
static int tcp_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->sync_send(buffer, len, timeout_ms);
}
static int base_close(esp_transport_handle_t t)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->tcp_close() == esp_modem::command_result::OK ? 0 : -1;
}
static int base_poll_read(esp_transport_handle_t t, int timeout_ms)
{
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
return dce->wait_to_read(timeout_ms);
}
static int base_poll_write(esp_transport_handle_t t, int timeout_ms)
{
// expect the socket is always writable
return 1;
}
static int base_destroy(esp_transport_handle_t transport)
{
return 0;
}
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce)
{
esp_transport_handle_t at = esp_transport_init();
if (!at) {
return nullptr;
}
esp_transport_set_context_data(at, (void *)dce);
esp_transport_set_func(at, tcp_connect, tcp_read, tcp_write, base_close, base_poll_read, base_poll_write, base_destroy);
return at;
}

View File

@ -1,17 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_transport.h"
#include "sock_dce.hpp"
/**
* @brief Initializes TCP transport based on AT commands
*
* @param DCE
* @return Transport handle on success
*/
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce);

View File

@ -1,2 +1,3 @@
idf_component_register(SRCS "pppos_client_main.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -3,7 +3,7 @@ dependencies:
## Required IDF version
idf: ">=4.1.0"
espressif/esp_modem:
version: "^1.0.0"
version: "^0.1.23"
override_path: "../../../"
espressif/esp_modem_usb_dte:
version: "^1.1.0"

View File

@ -60,7 +60,7 @@ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED
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_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
@ -102,7 +102,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %" PRIu32, event_id);
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = event_data;
@ -114,7 +114,7 @@ static void on_ppp_changed(void *arg, esp_event_base_t event_base,
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "IP event! %" PRIu32, event_id);
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;

View File

@ -175,9 +175,6 @@ extern "C" void app_main(void)
#endif
assert(dce);
/* Try to connect to the network and publish an mqtt topic */
StatusHandler handler;
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
if (command_result::OK != dce->set_flow_control(2, 2)) {
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
@ -218,6 +215,8 @@ extern "C" void app_main(void)
}
#endif
/* Try to connect to the network and publish an mqtt topic */
StatusHandler handler;
if (!handler.wait_for(StatusHandler::IP_Event, 60000)) {
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
return;

View File

@ -1,4 +1,4 @@
version: "1.0.2"
version: "1.0.0"
description: esp modem
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
dependencies:

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -86,30 +86,9 @@ public:
*/
int write(int i, uint8_t *data, size_t len);
/**
* @brief Recovers the protocol
*
* This restarts the CMUX state machine, which could have been in a wrong state due to communication
* issue on a lower layer.
*
* @return true on success
*/
bool recover();
private:
enum class protocol_mismatch_reason {
MISSED_LEAD_SOF,
MISSED_TRAIL_SOF,
WRONG_CRC,
UNEXPECTED_HEADER,
UNEXPECTED_DATA,
READ_BEHIND_BUFFER,
UNKNOWN
};
static uint8_t fcs_crc(const uint8_t frame[6]); /*!< Utility to calculate FCS CRC */
bool data_available(uint8_t *data, size_t len); /*!< Called when valid data available (returns false on unexpected data format) */
void data_available(uint8_t *data, size_t len); /*!< Called when valid data available */
void send_sabm(size_t i); /*!< Sending initial SABM */
void send_disconnect(size_t i); /*!< Sending closing request for each virtual or control terminal */
bool on_cmux_data(uint8_t *data, size_t len); /*!< Called from terminal layer when raw CMUX protocol data available */
@ -126,7 +105,6 @@ private:
bool on_header(CMuxFrame &frame);
bool on_payload(CMuxFrame &frame);
bool on_footer(CMuxFrame &frame);
void recover_protocol(protocol_mismatch_reason reason);
std::function<bool(uint8_t *data, size_t len)> read_cb[MAX_TERMINALS_NUM]; /*!< Function pointers to read callbacks */
std::shared_ptr<Terminal> term; /*!< The original terminal */

View File

@ -84,11 +84,6 @@ public:
return mode.set(dte.get(), device.get(), netif, m);
}
bool recover()
{
return dte->recover();
}
protected:
std::shared_ptr<DTE> dte;
std::shared_ptr<SpecificModule> device;

View File

@ -236,14 +236,6 @@ public:
return nullptr;
}
template <typename T_Module>
static std::unique_ptr<DCE> create_unique_dce_from(const esp_modem::dce_config *config,
std::shared_ptr<esp_modem::DTE> dte,
esp_netif_t *netif)
{
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
}
private:
ModemType m;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -79,13 +79,6 @@ public:
*/
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
/**
* @brief Sets read callback for manual command processing
* Note that this API also locks the command API, which can only be used
* after you remove the callback by dte->on_read(nullptr)
*
* @param on_data Function to be called when a command response is available
*/
void on_read(got_line_cb on_data) override;
/**
@ -115,13 +108,6 @@ public:
*/
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
/**
* @brief Allows this DTE to recover from a generic connection issue
*
* @return true if success
*/
bool recover();
protected:
/**
* @brief Allows for locking the DTE
@ -136,11 +122,10 @@ protected:
}
friend class Scoped<DTE>; /*!< Declaring "Scoped<DTE> lock(dte)" locks this instance */
private:
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
void handle_error(terminal_error err); /*!< Performs internal error handling */
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
void exit_cmux_internal(); /*!< Cleanup CMUX */
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */
Lock internal_lock{}; /*!< Locks DTE operations */
unique_buffer buffer; /*!< DTE buffer */
@ -148,82 +133,9 @@ private:
std::shared_ptr<Terminal> primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */
std::shared_ptr<Terminal> secondary_term; /*!< Secondary terminal for this DTE */
modem_mode mode; /*!< DTE operation mode */
SignalGroup signal; /*!< Event group used to signal request-response operations */
command_result result; /*!< Command result of the currently exectuted command */
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
std::function<void(terminal_error err)> user_error_cb; /*!< user callback on error event from attached terminals */
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
/**
* @brief Implements an extra buffer that is used to capture partial reads from underlying terminals
* when we run out of the standard buffer
*/
struct extra_buffer {
extra_buffer(): buffer(nullptr) {}
~extra_buffer()
{
delete buffer;
}
std::vector<uint8_t> *buffer;
size_t consumed{0};
void grow(size_t need_size);
void deflate()
{
grow(0);
consumed = 0;
}
[[nodiscard]] uint8_t *begin() const
{
return &buffer->at(0);
}
[[nodiscard]] uint8_t *current() const
{
return &buffer->at(0) + consumed;
}
} inflatable;
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
/**
* @brief Set internal command callbacks to the underlying terminal.
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
*/
void set_command_callbacks();
/**
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
*/
struct command_cb {
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
got_line_cb got_line; /*!< Supplied command callback */
Lock line_lock{}; /*!< Command callback locking mechanism */
char separator{}; /*!< Command reply separator (end of line/processing unit) */
command_result result{}; /*!< Command return code */
SignalGroup signal; /*!< Event group used to signal request-response operations */
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
bool wait_for_line(uint32_t time_ms) /*!< Waiting for command processing */
{
return signal.wait_any(command_cb::GOT_LINE, time_ms);
}
void set(got_line_cb l, char s = '\n') /*!< Sets the command callback atomically */
{
Scoped<Lock> lock(line_lock);
if (l) {
// if we set the line callback, we have to reset the signal and the result
signal.clear(GOT_LINE);
result = command_result::TIMEOUT;
} else {
// if we clear the line callback, we check consistency (since we've locked the line processing)
if (signal.is_any(command_cb::GOT_LINE) && result == command_result::TIMEOUT) {
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
}
}
got_line = std::move(l);
separator = s;
}
void give_up() /*!< Reports other than timeout error when processing replies (out of buffer) */
{
result = command_result::FAIL;
signal.set(GOT_LINE);
}
} command_cb; /*!< Command callback utility class */
};
/**

View File

@ -54,9 +54,8 @@ public:
*/
void stop();
void receive(uint8_t *data, size_t len);
private:
void receive(uint8_t *data, size_t len);
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len);

View File

@ -13,7 +13,7 @@
// limitations under the License.
#pragma once
#include "generate/esp_modem_command_declare_helper.inc"
#include "esp_modem_command_declare_helper.inc"
#define DECLARE_ALL_COMMAND_APIS(...) \

View File

@ -14,7 +14,7 @@
.stop_bits = UART_STOP_BITS_1, \
.parity = UART_PARITY_DISABLE, \
.flow_control = ESP_MODEM_FLOW_CONTROL_NONE,\
.source_clk = ESP_MODEM_DEFAULT_UART_CLK, \
.source_clk = UART_SCLK_APB, \
.baud_rate = 115200, \
.tx_io_num = 25, \
.rx_io_num = 26, \

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -113,7 +113,7 @@ struct CMux::CMuxFrame {
}
};
bool CMux::data_available(uint8_t *data, size_t len)
void CMux::data_available(uint8_t *data, size_t len)
{
if (data && (type & FT_UIH) == FT_UIH && len > 0 && dlci > 0) { // valid payload on a virtual term
int virtual_term = dlci - 1;
@ -128,38 +128,32 @@ bool CMux::data_available(uint8_t *data, size_t len)
#else
read_cb[virtual_term](data, len);
#endif
} else {
return false;
}
} else if (data == nullptr && type == (FT_UA | PF) && len == 0) { // notify the initial SABM command
} else if (data == nullptr && type == 0x73 && len == 0) { // notify the initial SABM command
Scoped<Lock> l(lock);
sabm_ack = dlci;
} else if (data == nullptr && dlci > 0) {
} else if (data == nullptr) {
int virtual_term = dlci - 1;
if (virtual_term < MAX_TERMINALS_NUM && read_cb[virtual_term]) {
#ifdef DEFRAGMENT_CMUX_PAYLOAD
read_cb[virtual_term](payload_start, total_payload_size);
#endif
} else {
return false;
}
} else if ((type & FT_UIH) == FT_UIH && dlci == 0) { // notify the internal DISC command
if (len > 0 && (data[0] & 0xE1) == 0xE1) {
// Not a DISC, ignore (MSC frame)
return true;
return;
}
Scoped<Lock> l(lock);
sabm_ack = dlci;
} else {
return false;
}
return true;
}
bool CMux::on_init(CMuxFrame &frame)
{
if (frame.ptr[0] != SOF_MARKER) {
recover_protocol(protocol_mismatch_reason::MISSED_LEAD_SOF);
ESP_LOGW("CMUX", "Protocol mismatch: Missed leading SOF, recovering...");
state = cmux_state::RECOVER;
return true;
}
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
@ -212,7 +206,6 @@ bool CMux::on_header(CMuxFrame &frame)
}
size_t payload_offset = std::min(frame.len, 4 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
#ifndef ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
if ((frame_header[3] & 1) == 0) {
if (frame_header_offset + frame.len <= 4) {
frame_header_offset += frame.len;
@ -222,21 +215,12 @@ bool CMux::on_header(CMuxFrame &frame)
memcpy(frame_header + frame_header_offset, frame.ptr, payload_offset);
payload_len = frame_header[4] << 7;
frame_header_offset += payload_offset - 1; // rewind frame_header back to hold only 6 bytes size
} else
#endif // ! ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
{
} else {
payload_len = 0;
frame_header_offset += payload_offset;
}
dlci = frame_header[1] >> 2;
type = frame_header[2];
// Sanity check for expected values of DLCI and type,
// since CRC could be evaluated after the frame payload gets received
if (dlci > MAX_TERMINALS_NUM || (frame_header[1] & 0x01) == 0 ||
(((type & FT_UIH) != FT_UIH) && type != (FT_UA | PF) ) ) {
recover_protocol(protocol_mismatch_reason::UNEXPECTED_HEADER);
return true;
}
payload_len += (frame_header[3] >> 1);
frame.advance(payload_offset);
state = cmux_state::PAYLOAD;
@ -248,18 +232,12 @@ bool CMux::on_payload(CMuxFrame &frame)
ESP_LOGD("CMUX", "Payload frame: dlci:%02x type:%02x payload:%d available:%d", dlci, type, payload_len, frame.len);
if (frame.len < payload_len) { // payload
state = cmux_state::PAYLOAD;
if (!data_available(frame.ptr, frame.len)) { // partial read
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
data_available(frame.ptr, frame.len); // partial read
payload_len -= frame.len;
return false;
} else { // complete
if (payload_len > 0) {
if (!data_available(&frame.ptr[0], payload_len)) { // rest read
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
data_available(&frame.ptr[0], payload_len); // rest read
}
frame.advance((payload_len));
state = cmux_state::FOOTER;
@ -279,23 +257,16 @@ bool CMux::on_footer(CMuxFrame &frame)
footer_offset = std::min(frame.len, 6 - frame_header_offset);
memcpy(frame_header + frame_header_offset, frame.ptr, footer_offset);
if (frame_header[5] != SOF_MARKER) {
recover_protocol(protocol_mismatch_reason::MISSED_TRAIL_SOF);
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
payload_start = nullptr;
total_payload_size = 0;
state = cmux_state::RECOVER;
return true;
}
#ifdef ESP_MODEM_CMUX_USE_SHORT_PAYLOADS_ONLY
uint8_t crc = 0xFF - fcs_crc(frame_header);
if (crc != frame_header[4]) {
recover_protocol(protocol_mismatch_reason::WRONG_CRC);
return true;
}
#endif
frame.advance(footer_offset);
state = cmux_state::INIT;
frame_header_offset = 0;
if (!data_available(nullptr, 0)) {
recover_protocol(protocol_mismatch_reason::UNEXPECTED_DATA);
return true;
}
data_available(nullptr, 0);
payload_start = nullptr;
total_payload_size = 0;
}
@ -309,28 +280,7 @@ bool CMux::on_cmux_data(uint8_t *data, size_t actual_len)
auto data_to_read = buffer.size - 128; // keep 128 (max CMUX payload) backup buffer)
if (payload_start) {
data = payload_start + total_payload_size;
auto data_end = buffer.get() + buffer.size;
data_to_read = payload_len + 2; // 2 -- CMUX protocol footer
if (data + data_to_read >= data_end) {
ESP_LOGW("CUMX", "Failed to defragment longer payload (payload=%d)", payload_len);
// If you experience this error, your device uses longer payloads while
// the configured buffer is too small to defragment the payload properly.
// To resolve this issue you can:
// * Either increase `dte_buffer_size`
// * Or disable `ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD` in menuconfig
// Attempts to process the data accumulated so far (rely on upper layers to process correctly)
data_available(nullptr, 0);
if (payload_len > total_payload_size) {
payload_start = nullptr;
total_payload_size = 0;
} else {
// cannot continue with this payload, give-up and recover the protocol
recover_protocol(protocol_mismatch_reason::READ_BEHIND_BUFFER);
}
data_to_read = buffer.size;
data = buffer.get();
}
data_to_read = payload_len + 2;
} else {
data = buffer.get();
}
@ -485,19 +435,3 @@ std::pair<std::shared_ptr<Terminal>, unique_buffer> CMux::detach()
{
return std::make_pair(std::move(term), std::move(buffer));
}
void esp_modem::CMux::recover_protocol(protocol_mismatch_reason reason)
{
ESP_LOGW("CMUX", "Restarting CMUX state machine (reason: %d)", static_cast<int>(reason));
payload_start = nullptr;
total_payload_size = 0;
frame_header_offset = 0;
state = cmux_state::RECOVER;
}
bool CMux::recover()
{
Scoped<Lock> l(lock);
recover_protocol(protocol_mismatch_reason::UNKNOWN);
return true;
}

View File

@ -21,17 +21,10 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
netif.stop();
auto signal = std::make_shared<SignalGroup>();
std::weak_ptr<SignalGroup> weak_signal = signal;
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
// post the transitioning data to the network layers if it contains PPP SOF marker
if (memchr(data, 0x7E, len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (PPP)", data, len, ESP_LOG_DEBUG);
netif.receive(data, len);
}
// treat the transitioning data as a textual message if it contains a newline char
dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
if (memchr(data, '\n', len))
{
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
std::string_view response((char *) data, len);
for (auto &it : pass)
@ -46,7 +39,6 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
});
netif.wait_until_ppp_exits();
if (!signal->wait(1, 2000)) {
dte.set_read_cb(nullptr);
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
return false;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,131 +17,53 @@ static const size_t dte_default_buffer_size = 1000;
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
mode(modem_mode::UNDEF) {}
DTE::DTE(std::unique_ptr<Terminal> terminal):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
mode(modem_mode::UNDEF) {}
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
mode(modem_mode::DUAL_MODE) {}
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::UNDEF)
{
set_command_callbacks();
}
void DTE::set_command_callbacks()
{
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
Scoped<Lock> l(command_cb.line_lock);
if (command_cb.got_line == nullptr) {
return false;
}
if (data) {
// For terminals which post data directly with the callback (CMUX)
// we cannot defragment unless we allocate, but
// we'll try to process the data on the actual buffer
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
if (inflatable.consumed != 0) {
inflatable.grow(inflatable.consumed + len);
std::memcpy(inflatable.current(), data, len);
data = inflatable.begin();
}
if (command_cb.process_line(data, inflatable.consumed, len)) {
return true;
}
// at this point we're sure that the data processing hasn't finished,
// and we have to grow the inflatable buffer (if enabled) or give up
if (inflatable.consumed == 0) {
inflatable.grow(len);
std::memcpy(inflatable.begin(), data, len);
}
inflatable.consumed += len;
return false;
#else
if (command_cb.process_line(data, 0, len)) {
return true;
}
// cannot inflate and the processing hasn't finishes in the first iteration -> report a failure
command_cb.give_up();
return true;
#endif
}
// data == nullptr: Terminals which request users to read current data
// we're able to use DTE's buffer to defragment it; as long as we consume less that the buffer size
if (buffer.size > buffer.consumed) {
data = buffer.get();
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
if (command_cb.process_line(data, buffer.consumed, len)) {
return true;
}
buffer.consumed += len;
return false;
}
// we have used the entire DTE's buffer, need to use the inflatable buffer to continue
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
if (inflatable.consumed == 0) {
inflatable.grow(buffer.size + len);
std::memcpy(inflatable.begin(), buffer.get(), buffer.size);
inflatable.consumed = buffer.size;
} else {
inflatable.grow(inflatable.consumed + len);
}
len = primary_term->read(inflatable.current(), len);
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
return true;
}
inflatable.consumed += len;
return false;
#else
// cannot inflate -> report a failure
command_cb.give_up();
return true;
#endif
});
primary_term->set_error_cb([this](terminal_error err) {
if (user_error_cb) {
user_error_cb(err);
}
handle_error(err);
});
secondary_term->set_error_cb([this](terminal_error err) {
if (user_error_cb) {
user_error_cb(err);
}
handle_error(err);
});
}
mode(modem_mode::DUAL_MODE) {}
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
{
Scoped<Lock> l1(internal_lock);
command_cb.set(got_line, separator);
Scoped<Lock> l(internal_lock);
result = command_result::TIMEOUT;
signal.clear(GOT_LINE);
primary_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) {
if (!data) {
data = buffer.get();
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
} else {
buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment
}
if (memchr(data + buffer.consumed, separator, len)) {
result = got_line(data, buffer.consumed + len);
if (result == command_result::OK || result == command_result::FAIL) {
signal.set(GOT_LINE);
return true;
}
}
buffer.consumed += len;
return false;
});
primary_term->write((uint8_t *)command.c_str(), command.length());
command_cb.wait_for_line(time_ms);
command_cb.set(nullptr);
auto got_lf = signal.wait(GOT_LINE, time_ms);
if (got_lf && result == command_result::TIMEOUT) {
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
}
buffer.consumed = 0;
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
inflatable.deflate();
#endif
return command_cb.result;
primary_term->set_read_cb(nullptr);
return result;
}
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
@ -154,22 +76,12 @@ bool DTE::exit_cmux()
if (!cmux_term->deinit()) {
return false;
}
exit_cmux_internal();
return true;
}
void DTE::exit_cmux_internal()
{
if (!cmux_term) {
return;
}
auto ejected = cmux_term->detach();
// return the ejected terminal and buffer back to this DTE
primary_term = std::move(ejected.first);
buffer = std::move(ejected.second);
secondary_term = primary_term;
set_command_callbacks();
return true;
}
bool DTE::setup_cmux()
@ -178,21 +90,14 @@ bool DTE::setup_cmux()
if (cmux_term == nullptr) {
return false;
}
if (!cmux_term->init()) {
exit_cmux_internal();
cmux_term = nullptr;
return false;
}
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
if (primary_term == nullptr) {
return false;
}
secondary_term = std::make_unique<CMuxInstance>(cmux_term, 1);
if (primary_term == nullptr || secondary_term == nullptr) {
exit_cmux_internal();
cmux_term = nullptr;
return false;
}
set_command_callbacks();
return true;
}
@ -214,7 +119,6 @@ bool DTE::set_mode(modem_mode m)
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
// mode stays the same, but need to swap terminals (as command has been switched)
secondary_term.swap(primary_term);
set_command_callbacks();
} else {
mode = m;
}
@ -257,7 +161,6 @@ bool DTE::set_mode(modem_mode m)
// manual CMUX transitions: Swap terminals
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
secondary_term.swap(primary_term);
set_command_callbacks();
return true;
}
mode = modem_mode::UNDEF;
@ -266,10 +169,6 @@ bool DTE::set_mode(modem_mode m)
void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
{
if (f == nullptr) {
set_command_callbacks();
return;
}
on_data = std::move(f);
secondary_term->set_read_cb([this](uint8_t *data, size_t len) {
if (!data) { // if no data available from terminal callback -> need to explicitly read some
@ -285,8 +184,8 @@ void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
void DTE::set_error_cb(std::function<void(terminal_error err)> f)
{
user_error_cb = std::move(f);
set_command_callbacks();
secondary_term->set_error_cb(f);
primary_term->set_error_cb(f);
}
int DTE::read(uint8_t **d, size_t len)
@ -331,49 +230,6 @@ void DTE::on_read(got_line_cb on_read_cb)
});
}
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
{
if (memchr(data + consumed, separator, len)) {
result = got_line(data, consumed + len);
if (result == command_result::OK || result == command_result::FAIL) {
signal.set(GOT_LINE);
return true;
}
}
return false;
}
bool DTE::recover()
{
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
return cmux_term->recover();
}
return false;
}
void DTE::handle_error(terminal_error err)
{
if (err == terminal_error::BUFFER_OVERFLOW ||
err == terminal_error::CHECKSUM_ERROR ||
err == terminal_error::UNEXPECTED_CONTROL_FLOW) {
recover();
}
}
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
void DTE::extra_buffer::grow(size_t need_size)
{
if (need_size == 0) {
delete buffer;
buffer = nullptr;
} else if (buffer == nullptr) {
buffer = new std::vector<uint8_t>(need_size);
} else {
buffer->resize(need_size);
}
}
#endif
/**
* Implemented here to keep all headers C++11 compliant
*/

Some files were not shown because too many files have changed in this diff Show More