Compare commits

..

44 Commits

Author SHA1 Message Date
6a94e61a7e Merge pull request #288 from david-cermak/bump/mdns
bump(mdns): 1.0.9 -> 1.1.0
2023-04-28 20:36:35 +02:00
c8fe4092c4 bump(mdns): 1.0.9 -> 1.1.0
1.1.0
Features
- Decouple main module from mdns-networking (d238e93)
Bug Fixes
- Use idf-build-apps package for building mdns (1a0a41f)
- socket networking to init interfaces properly (ee9b04f)
- Removed unused internal lock from mdns_server struct (a06fb77)
- Resolve conflicts only on self hosted items (e69a9eb, #185)
- Fix memory issues reported by valgrind (0a682e7)
Updated
- docs(common): updated component and example links (f48d9b2)
- Add APIs to look up delegated services (87dcd7d)
- Fix deadly mdns crash (4fa3023)
- docs(common): improving documentation (ca3fce0)
- append all ipv6 address in mdns answer (5ed3e9a)
- test(mdns): Host tests to use IDF's esp_event_stub (537d170)
2023-04-28 19:32:47 +02:00
31187b7d17 Merge pull request #283 from espressif/esp_modem/fix_build
fix(esp_modem): fix esp_modem build issue
2023-04-27 20:31:19 +04:00
ab94566995 fix(esp_modem): fix esp_modem build issue 2023-04-27 12:25:59 +04:00
f67511c2c1 Merge pull request #275 from gabsuren/docs/esp_modem_docs
docs(esp_modem): updated documents to show missed topics
2023-04-27 10:56:12 +04:00
05348534a6 docs(esp_modem): updated documents to show missed topics 2023-04-26 16:24:30 +04:00
18ea910f02 Merge pull request #279 from david-cermak/feat/auto_tag
ci(common): Support for tagging and releasing in CI
2023-04-25 16:31:11 +02:00
62be981c92 ci(common): Fail CI if the PR to too much behind origin
And removes the docs generation/deploy part
2023-04-25 16:27:05 +02:00
6daf6c6ba6 ci(common): Support for tagging and releasing in CI 2023-04-25 13:54:44 +02:00
b0d3b41aae Merge pull request #277 from gabsuren/docs/update_links
docs(common): updated component and example links
2023-04-20 12:01:22 +04:00
f48d9b2fcf docs(common): updated component and example links 2023-04-19 11:58:05 +04:00
6c299c068b Merge pull request #274 from david-cermak/bugfix/ci_mdns_all_targets
mdns: Build all targets and releases
2023-04-18 10:38:46 +02:00
ea5d3cee29 Merge pull request #268 from wqx6/search_delegated_service
mdns: Add APIs to lookup delegated services (IDFGH-9790)
2023-04-18 10:38:27 +02:00
7330597586 Merge pull request #270 from 0xFEEDC0DE64/fix_mdns_crash
Fix deadly mdns crash (IDFGH-9800)
2023-04-18 10:37:11 +02:00
1a0a41fa2d fix(mdns): Use idf-build-apps package for building mdns 2023-04-17 13:44:18 +02:00
2f7cbd16db ci(common): Fix pre-commit manual run 2023-04-17 13:43:14 +02:00
87dcd7dc7d mdns: Add APIs to look up delegated services 2023-04-17 19:35:41 +08:00
8e5a27f4b7 docs: fixed broken link in README.md 2023-04-10 18:38:23 +04:00
6528f446bc docs: fixed broken link on README.md 2023-04-07 16:43:43 +04:00
4fa3023ea2 Fix deadly mdns crash 2023-04-05 16:13:07 +02:00
10f8200564 Merge pull request #267 from gabsuren/esp_mqtt_cxx/updated_docs_and_yml_file
docs(esp_mqtt_cxx): updated documentation and deployment *yml file
2023-04-05 11:51:28 +04:00
a547ec8147 docs(esp_mqtt_cxx): updated documentation and deployment file 2023-04-05 11:42:59 +04:00
db0e20f446 Merge pull request #256 from gabsuren/docs/documentation_improving
docs: Improving documentation (IDF-7064)
2023-03-31 12:07:09 +04:00
ca3fce003e docs(common): improving documentation
- update esp_modem to use esp_docs
- migrated docs from github pages to docs.espressif.com
2023-03-31 12:04:46 +04:00
f6ff132eb1 Merge pull request #259 from 0xFEEDC0DE64/fix_websocket_error_spacings
Fix weird error message spacings (IDFGH-9704)
2023-03-23 17:57:12 +01:00
8bb207e9bb Fix weird error message spacings 2023-03-23 11:44:51 +01:00
76047a4cc6 Merge pull request #137 from david-cermak/mdns/isolate_networking
mdns: Decouple networking from the main mdns module
2023-03-22 14:40:49 +01:00
ee9b04f598 fix(mdns): socket networking to init interfaces properly 2023-03-22 12:17:09 +01:00
d238e9311b feat(mdns): Decouple main module from mdns-networking
Removed sharing the data via the global variable `_mdns_server`, added
API `mdns_is_netif_ready()`
2023-03-22 12:16:28 +01:00
a838af46a6 Merge pull request #251 from xieqinan/feature/append-all-ipv6-address-in-mdns-answer
mdns: append all ipv6 address in mdns answer (IDFGH-9646)
2023-03-22 08:16:38 +01:00
5ed3e9a6a1 mdns: append all ipv6 address in mdns answer 2023-03-22 12:00:38 +08:00
5b3346f5c5 Merge pull request #225 from david-cermak/bugfix/mdns_wrong_name_collision
fix(mdns): Incorrect name collision
2023-03-20 17:24:47 +01:00
a06fb7714b fix(mdns): Removed unused internal lock from mdns_server struct 2023-03-20 16:55:49 +01:00
e69a9ebb3d fix(mdns): Resolve conflicts only on self hosted items
Skip solving conflicts for delegated names and delegated services

Closes https://github.com/espressif/esp-protocols/issues/185
2023-03-20 16:55:49 +01:00
2e607e8ffb Merge pull request #257 from david-cermak/fix/mdns_valgrind
fix(mdns): Fix memory issues reported by valgrind
2023-03-20 16:49:36 +01:00
0a9765626b Merge pull request #255 from david-cermak/feat/modem_cz_bump
esp_modem: version 1.0.0 (plus cz-bump config)
2023-03-20 12:33:32 +01:00
537d170f1d test(mdns): Host tests to use IDF's esp_event_stub
Also fixes one minor from esp_netif_linux implementation
2023-03-20 12:17:36 +01:00
0a682e7f12 fix(mdns): Fix memory issues reported by valgrind
* Read after the allocated area (which may lead to subsequent
mdns name corruption)
* Potentially uninit variable used in condition
2023-03-19 20:58:10 +01:00
afb69308df fix(modem): Example to use 1.0.0 2023-03-16 20:47:59 +01:00
284cdeb2ed Merge pull request #254 from lhauswald/feature/mqtt_client_certificate
MQTT: complete client authentication configuration (IDFGH-9656)
2023-03-16 20:35:23 +01:00
97d52495d4 bump(modem): 0.1.28 -> 1.0.0 2023-03-16 20:19:00 +01:00
423e965c88 fix(common): Changelog to correctly pick references
Also added .cz config for modem components
2023-03-16 20:19:00 +01:00
e762ada551 Merge pull request #156 from david-cermak/feature/modem_example_at_mqtt
feat(esp_modem): Add mqtt example in  AT-only mode
2023-03-16 20:17:32 +01:00
ee09ff45e5 feat(mqtt_cxx): configure client authentication via certificate/key or secure element 2023-03-16 09:42:43 +01:00
90 changed files with 1041 additions and 686 deletions

View File

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

View File

@ -13,122 +13,33 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32", "esp32s2", "esp32c3"]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
idf_ver: ["latest", "release-v5.0"]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
shell: bash
working-directory: ${{ matrix.test.path }}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
for dir in `ls -d build_*`; do
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -m components/mdns/.build-test-rules.yml -d
# Build specific configs for test targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }}
cd components/mdns/${{ matrix.test.path }}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v3
if: ${{ matrix.idf_target }} == "esp32"
with:
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ matrix.test.path }}/artifacts.zip
name: mdns_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: components/mdns/${{ matrix.test.path }}/artifacts.zip
if-no-files-found: error
build_mdns_app:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || 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: |
@ -139,7 +50,7 @@ jobs:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/mdns/examples" }, { app: unit_test, path: "components/mdns/tests/unit_test" } ]
test: [ { app: example, path: "examples" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
needs: build_mdns
runs-on:
- self-hosted
@ -151,14 +62,14 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ matrix.test.path }}/ci/
path: components/mdns/${{ matrix.test.path }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
sudo apt-get install -y dnsutils
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
working-directory: ${{ matrix.test.path }}
working-directory: components/mdns/${{ matrix.test.path }}
run: |
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
@ -170,4 +81,4 @@ jobs:
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: ${{ matrix.test.path }}/*.xml
path: components/mdns/${{ matrix.test.path }}/*.xml

View File

@ -48,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 }}
python ./ci/build_apps.py components/esp_modem/examples/${{ matrix.example }} -m components/esp_modem/examples/.build-test-rules.yml
host_test_esp_modem:
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'

View File

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

View File

@ -5,66 +5,65 @@ 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 : ${{ secrets.DOCS_DEPLOY_PATH }}
jobs:
docs_build:
name: Docs-Build-And-Upload
publish:
name: Publish Tag, Release, Docs, Component
runs-on: ubuntu-latest
# Skip running on forks since it won't have access to secrets
if: github.repository == 'espressif/esp-protocols'
steps:
- name: Checkout esp-protocols
uses: actions/checkout@master
uses: actions/checkout@v3
with:
persist-credentials: false
fetch-depth: 0
submodules: recursive
- name: Generate docs
run: |
sudo apt-get update
sudo apt-get -y install doxygen clang python3-pip
python -m pip install breathe recommonmark esp-docs
cd $GITHUB_WORKSPACE/components/esp_modem/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
cd $GITHUB_WORKSPACE/components/mdns/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
cd $GITHUB_WORKSPACE/components/asio/docs
./generate_docs
mkdir -p $GITHUB_WORKSPACE/docs/asio
cp -r html/. $GITHUB_WORKSPACE/docs/asio
cd $GITHUB_WORKSPACE/docs
touch .nojekyll
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
echo '<a href="asio/index.html">ASIO</a><br>' >> index.html
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
- name: Upload components to component service
uses: espressif/github-actions/upload_components@master
token: "${{ secrets.GITHUB_TOKEN }}"
- name: Check for version update
shell: bash
run: ./ci/detect_component_bump
- name: Tag merge commit
if: env.BUMP_VERSION != ''
uses: anothrNick/github-tag-action@1.61.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CUSTOM_TAG: ${{ env.BUMP_TAG }}
- name: Create Release
if: env.BUMP_VERSION != ''
uses: softprops/action-gh-release@v1
with:
directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio"
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
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
# cd $GITHUB_WORKSPACE/docs
# ./generate_docs
# - name: Deploying generated docs
# if: always()
# 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
# export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs
# deploy-docs
# - name: Upload components to component service
# uses: espressif/github-actions/upload_components@master
# with:
# directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio;components/esp_mqtt_cxx"
# namespace: "espressif"
# api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

View File

@ -57,7 +57,7 @@ repos:
hooks:
- id: commitizen
- id: commitizen-branch
stages: [push]
stages: [push, manual]
- repo: local
hooks:
- id: commit message scopes

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 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
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

View File

@ -1,26 +1,29 @@
# Collection of protocol components for 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://espressif.github.io/esp-protocols/esp_modem/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/latest/index.html)
### mDNS
* Brief introduction [README](components/mdns/README.md)
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
* Full html [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
* Full html [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html)
### esp_websocket_client
* Brief introduction [README](components/esp_websocket_client/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_websocket_client/docs/latest/index.html)
### ASIO port
* Brief introduction [README](components/asio/README.md)
* Full html [documentation](https://espressif.github.io/esp-protocols/asio/index.html)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)
### esp_mqtt_cxx
* Brief introduction [README](components/esp_mqtt_cxx/README.md)
* Full html [documentation](https://docs.espressif.com/projects/esp-protocols/esp_mqtt_cxx/docs/latest/index.html)

View File

@ -7,7 +7,6 @@ This file is used in CI for esp-protocols build tests
import argparse
import os
import sys
from pathlib import Path
from idf_build_apps import build_apps, find_apps, setup_logging
from idf_build_apps.constants import SUPPORTED_TARGETS
@ -24,6 +23,9 @@ 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')
args = parser.parse_args()
IDF_PATH = os.environ['IDF_PATH']
@ -32,19 +34,15 @@ if __name__ == '__main__':
setup_logging(2)
apps = find_apps(
args.paths,
recursive=True,
recursive=False,
target=args.target,
build_dir='build_@t_@w',
config_rules_str=[
'sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'
],
config_rules_str=args.rules,
build_log_path='build_log.txt',
size_json_path='size.json',
check_warnings=True,
preserve=True,
manifest_files=[
str(p) for p in Path('.').glob('**/.build-test-rules.yml')
],
preserve=not args.delete,
manifest_files=args.manifests,
default_build_targets=SUPPORTED_TARGETS,
manifest_rootpath='.',
)

23
ci/bump Executable file
View File

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

View File

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

View File

@ -57,7 +57,7 @@ slim_modem_examples:
ignore:
perform_check: no
include:
- 'components/**/docs/**'
- '**/docs/**'
- 'components/esp_modem/port/linux/**'
- 'components/asio/examples/**'
- 'components/mdns/**/esp_system_protocols_linux/**'

36
ci/detect_component_bump Executable file
View File

@ -0,0 +1,36 @@
#!/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 has been updated"
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
# 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
done
echo "No changes in component version file"

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 example test :example:`examples <examples/..>`:
Get started with examples [examples](https://github.com/espressif/esp-protocols/tree/master/components/asio/examples)
## Documentation
* View the full [html documentation](https://espressif.github.io/esp-protocols/asio/index.html)
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/asio/docs/latest/index.html)

View File

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

View File

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

View File

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

View File

@ -1,36 +0,0 @@
# Internal design
## Design decisions
* Use C++ with additional C API
* Use exceptions
- Use macro wrapper over `try-catch` blocks when exceptions off (use `abort()` if `THROW()`)
* Initializes and allocates in the constructor (might throw)
- easier code with exceptions ON, with exceptions OFF alloc/init failures are not treated as runtime error (program aborts)
- break down long initialization in constructor into more private methods
* Implements different devices using inheritance from `GenericModule`, which is the most general implementation of a common modem
- Internally uses templates with device specialization (modeled as `DCE<SpecificModule>`) which could be used as well for some special cases,
such as implantation of a minimal device (ModuleIf), add new AT commands (oOnly in compile time), or using the Module with DTE only (no DCE, no Netif) for sending AT commands without network
## DCE collaboration model
The diagram describes how the DCE class collaborates with DTE, PPP and the device abstraction
![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

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

View File

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

View File

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

View File

@ -0,0 +1,13 @@
# esp_mqtt_cxx
[![Component Registry](https://components.espressif.com/components/espressif/esp_mqtt_cxx/badge.svg)](https://components.espressif.com/components/espressif/esp_mqtt_cxx)
The ESP MQTT client is a wrapper over the esp_mqtt client with the goal of providing a higher level API.
## Examples
Get started with [examples](https://github.com/espressif/esp-protocols/tree/master/components/esp_mqtt_cxx/examples)
## Documentation
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/docs/latest/esp_mqtt_cxx/index.html)

View File

@ -92,8 +92,35 @@ void config_client_credentials(esp_mqtt_client_config_t &mqtt_client_cfg, Client
{
mqtt_client_cfg.credentials.authentication.password = password.data.c_str();
},
[](ClientCertificate const & certificate) {},
[](SecureElement const & enable_secure_element) {},
[&mqtt_client_cfg](ClientCertificate const & certificate)
{
std::visit(overloaded{
[&mqtt_client_cfg](PEM const & pem)
{
mqtt_client_cfg.credentials.authentication.certificate = pem.data;
}, [&mqtt_client_cfg](DER const & der)
{
mqtt_client_cfg.credentials.authentication.certificate = der.data;
mqtt_client_cfg.credentials.authentication.certificate_len = der.len;
}}, certificate.certificate);
std::visit(overloaded{
[&mqtt_client_cfg](PEM const & pem)
{
mqtt_client_cfg.credentials.authentication.key = pem.data;
}, [&mqtt_client_cfg](DER const & der)
{
mqtt_client_cfg.credentials.authentication.key = der.data;
mqtt_client_cfg.credentials.authentication.key_len = der.len;
}}, certificate.key);
if (certificate.key_password.has_value()) {
mqtt_client_cfg.credentials.authentication.key_password = certificate.key_password.value().data.c_str();
mqtt_client_cfg.credentials.authentication.key_password_len = static_cast<int>(certificate.key_password.value().data.size());
}
},
[&mqtt_client_cfg](SecureElement const & enable_secure_element)
{
mqtt_client_cfg.credentials.authentication.use_secure_element = true;
},
[]([[maybe_unused ]]auto & unknown)
{
static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");

View File

@ -6,8 +6,8 @@ The `esp-websocket_client` component is a managed component for `esp-idf` that c
## Examples
Get started with example test [Example](examples/README.md):
Get started with example test [example](https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client/examples):
## Documentation
* View the full [html documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
* View the full [html documentation](https://docs.espressif.com/projects/esp-protocols/esp_websocket_client/docs/latest/index.html)

View File

@ -848,8 +848,8 @@ static void esp_websocket_client_task(void *pv)
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
client->error_handle.esp_ws_handshake_status_code = esp_transport_ws_get_upgrade_request_status(client->transport);
if (error_handle) {
esp_websocket_client_error(client, "esp_transport_connect() failed with %d,\
transport_error=%s, tls_error_code=%i, tls_flags=%i, esp_ws_handshake_status_code=%d, errno=%d",
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, "
"transport_error=%s, tls_error_code=%i, tls_flags=%i, esp_ws_handshake_status_code=%d, errno=%d",
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, client->error_handle.esp_ws_handshake_status_code, errno);
} else {

View File

@ -0,0 +1,11 @@
components/mdns/examples:
disable:
- if: IDF_TARGET in ["esp32h2"]
components/mdns/tests/unit_test:
disable:
- if: IDF_TARGET in ["esp32h2"]
components/mdns/tests/test_apps:
disable:
- if: IDF_TARGET in ["esp32h2"]

View File

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

View File

@ -1,5 +1,28 @@
# Changelog
## [1.1.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.1.0)
### Features
- Decouple main module from mdns-networking ([d238e93](https://github.com/espressif/esp-protocols/commit/d238e93))
### Bug Fixes
- Use idf-build-apps package for building mdns ([1a0a41f](https://github.com/espressif/esp-protocols/commit/1a0a41f))
- socket networking to init interfaces properly ([ee9b04f](https://github.com/espressif/esp-protocols/commit/ee9b04f))
- Removed unused internal lock from mdns_server struct ([a06fb77](https://github.com/espressif/esp-protocols/commit/a06fb77))
- Resolve conflicts only on self hosted items ([e69a9eb](https://github.com/espressif/esp-protocols/commit/e69a9eb), [#185](https://github.com/espressif/esp-protocols/issues/185))
- Fix memory issues reported by valgrind ([0a682e7](https://github.com/espressif/esp-protocols/commit/0a682e7))
### Updated
- docs(common): updated component and example links ([f48d9b2](https://github.com/espressif/esp-protocols/commit/f48d9b2))
- Add APIs to look up delegated services ([87dcd7d](https://github.com/espressif/esp-protocols/commit/87dcd7d))
- Fix deadly mdns crash ([4fa3023](https://github.com/espressif/esp-protocols/commit/4fa3023))
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
- append all ipv6 address in mdns answer ([5ed3e9a](https://github.com/espressif/esp-protocols/commit/5ed3e9a))
- test(mdns): Host tests to use IDF's esp_event_stub ([537d170](https://github.com/espressif/esp-protocols/commit/537d170))
## [1.0.9](https://github.com/espressif/esp-protocols/commits/mdns-v1.0.9)
### Features

View File

@ -6,7 +6,7 @@ endif()
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
set(dependencies esp_event esp_netif_linux esp_timer_linux esp_system)
set(dependencies esp_netif_linux esp_timer_linux esp_system)
set(srcs "mdns.c" ${MDNS_NETWORKING})
else()
set(dependencies lwip console esp_netif)

View File

@ -6,9 +6,9 @@ mDNS is a multicast UDP service that is used to provide local network service an
## Examples
Get started with example test [Example](examples/README.md):
Get started with example test [Example](https://github.com/espressif/esp-protocols/tree/master/components/mdns/examples):
## Documentation
* View the full [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
* View the full [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
* View the full [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html)
* View the full [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/latest/zh_CN/index.html)

View File

@ -93,8 +93,10 @@ static void mdns_print_results(mdns_result_t *results)
mdns_ip_addr_t *a = NULL;
int i = 1, t;
while (r) {
printf("%d: Interface: %s, Type: %s, TTL: %u\n", i++, esp_netif_get_ifkey(r->esp_netif), ip_protocol_str[r->ip_protocol],
r->ttl);
if (r->esp_netif) {
printf("%d: Interface: %s, Type: %s, TTL: %u\n", i++, esp_netif_get_ifkey(r->esp_netif),
ip_protocol_str[r->ip_protocol], r->ttl);
}
if (r->instance_name) {
printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto);
}
@ -140,6 +142,27 @@ static void query_mdns_service(const char *service_name, const char *proto)
mdns_query_results_free(results);
}
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
static void lookup_mdns_delegated_service(const char *service_name, const char *proto)
{
ESP_LOGI(TAG, "Lookup delegated service: %s.%s.local", service_name, proto);
mdns_result_t *results = NULL;
esp_err_t err = mdns_lookup_delegated_service(NULL, service_name, proto, 20, &results);
if (err) {
ESP_LOGE(TAG, "Lookup Failed: %s", esp_err_to_name(err));
return;
}
if (!results) {
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
static bool check_and_print_result(mdns_search_once_t *search)
{
// Check if any result is available
@ -234,6 +257,9 @@ static void check_button(void)
query_mdns_service("_smb", "_tcp");
query_mdns_service("_ftp", "_tcp");
query_mdns_service("_nfs", "_tcp");
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
lookup_mdns_delegated_service("_http", "_tcp");
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
}
old_level = new_level;
}

View File

@ -1,4 +1,4 @@
version: "1.0.9"
version: "1.1.0"
description: mDNS
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
dependencies:

View File

@ -26,7 +26,6 @@ extern "C" {
*/
typedef struct mdns_search_once_s mdns_search_once_t;
typedef enum {
MDNS_EVENT_ENABLE_IP4 = 1 << 1,
MDNS_EVENT_ENABLE_IP6 = 1 << 2,
@ -248,7 +247,6 @@ esp_err_t mdns_service_add_for_host(const char *instance_name, const char *servi
*/
bool mdns_service_exists(const char *service_type, const char *proto, const char *hostname);
/**
* @brief Check whether a service has been added.
*
@ -687,6 +685,24 @@ esp_err_t mdns_query_srv(const char *instance_name, const char *service_type, co
*/
esp_err_t mdns_query_txt(const char *instance_name, const char *service_type, const char *proto, uint32_t timeout, mdns_result_t **result);
/**
* @brief Look up delegated services.
*
* @param instance instance name (NULL for uncertain instance)
* @param service_type service type (_http, _ftp, etc)
* @param proto service protocol (_tcp, _udp)
* @param max_results maximum results to be collected
* @param result pointer to the result of the search
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_STATE mDNS is not running
* - ESP_ERR_NO_MEM memory error
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_lookup_delegated_service(const char *instance, const char *service_type, const char *proto, size_t max_results,
mdns_result_t **result);
/**
* @brief Query mDNS for A record
*

View File

@ -678,7 +678,7 @@ static uint16_t append_fqdn_dots(uint8_t *packet, uint16_t *index, const char *n
char *end = host;
char *start = host;
do {
end = memchr(start, '.', len);
end = memchr(start, '.', host + len - start);
end = end ? end : host + len;
int part_len = end - start;
if (!append_single_str(packet, index, start, part_len)) {
@ -733,6 +733,7 @@ search_next:
//read the destination into name and compare
name.parts = 0;
name.sub = 0;
name.invalid = false;
name.host[0] = 0;
name.service[0] = 0;
name.proto[0] = 0;
@ -1357,7 +1358,7 @@ static uint8_t _mdns_append_answer(uint8_t *packet, uint16_t *index, mdns_out_an
} else if (answer->type == MDNS_TYPE_A) {
if (answer->host == &_mdns_self_host) {
esp_netif_ip_info_t if_ip_info;
if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP) {
if (!mdns_is_netif_ready(tcpip_if, MDNS_IP_PROTOCOL_V4) && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP) {
return 0;
}
if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) {
@ -1384,32 +1385,39 @@ static uint8_t _mdns_append_answer(uint8_t *packet, uint16_t *index, mdns_out_an
#if CONFIG_LWIP_IPV6
else if (answer->type == MDNS_TYPE_AAAA) {
if (answer->host == &_mdns_self_host) {
struct esp_ip6_addr if_ip6;
if (!_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP) {
struct esp_ip6_addr if_ip6s[NETIF_IPV6_MAX_NUMS];
uint8_t count = 0;
if (!mdns_is_netif_ready(tcpip_if, MDNS_IP_PROTOCOL_V6) && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP) {
return 0;
}
if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) {
return 0;
}
if (_ipv6_address_is_zero(if_ip6)) {
return 0;
}
if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)if_ip6.addr, answer->flush, answer->bye) <= 0) {
return 0;
count = esp_netif_get_all_ip6(_mdns_get_esp_netif(tcpip_if), if_ip6s);
assert(count <= NETIF_IPV6_MAX_NUMS);
for (int i = 0; i < count; i++) {
if (_ipv6_address_is_zero(if_ip6s[i])) {
return 0;
}
if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)if_ip6s[i].addr,
answer->flush, answer->bye) <= 0) {
return 0;
}
}
if (!_mdns_if_is_dup(tcpip_if)) {
return 1;
return count;
}
mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &if_ip6)) {
return 1;
mdns_if_t other_if = _mdns_get_other_if(tcpip_if);
struct esp_ip6_addr other_ip6;
if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &other_ip6)) {
return count;
}
if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)if_ip6.addr, answer->flush, answer->bye) > 0) {
return 2;
if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)other_ip6.addr,
answer->flush, answer->bye) > 0) {
return 1 + count;
}
return 1;
return count;
} else if (answer->host != NULL) {
return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V6, answer->flush, answer->bye);
return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V6, answer->flush,
answer->bye);
}
}
#endif
@ -1606,18 +1614,20 @@ static void _mdns_remove_scheduled_answer(mdns_if_t tcpip_if, mdns_ip_protocol_t
while (q) {
if (q->tcpip_if == tcpip_if && q->ip_protocol == ip_protocol && q->distributed) {
mdns_out_answer_t *a = q->answers;
if (a->type == type && a->service == service->service) {
q->answers = q->answers->next;
free(a);
} else {
while (a->next) {
if (a->next->type == type && a->next->service == service->service) {
mdns_out_answer_t *b = a->next;
a->next = b->next;
free(b);
break;
if (a) {
if (a->type == type && a->service == service->service) {
q->answers = q->answers->next;
free(a);
} else {
while (a->next) {
if (a->next->type == type && a->next->service == service->service) {
mdns_out_answer_t *b = a->next;
a->next = b->next;
free(b);
break;
}
a = a->next;
}
a = a->next;
}
}
}
@ -2266,7 +2276,7 @@ static void _mdns_send_bye(mdns_srv_item_t **services, size_t len, bool include_
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
if (_mdns_server->interfaces[i].pcbs[j].pcb && _mdns_server->interfaces[i].pcbs[j].state == PCB_RUNNING) {
if (mdns_is_netif_ready(i, j) && _mdns_server->interfaces[i].pcbs[j].state == PCB_RUNNING) {
_mdns_pcb_send_bye((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, include_ip);
}
}
@ -2280,7 +2290,7 @@ static void _mdns_announce_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco
{
mdns_pcb_t *_pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
size_t i;
if (_pcb->pcb) {
if (mdns_is_netif_ready(tcpip_if, ip_protocol)) {
if (PCB_STATE_IS_PROBING(_pcb)) {
_mdns_init_pcb_probe(tcpip_if, ip_protocol, services, len, include_ip);
} else if (PCB_STATE_IS_ANNOUNCING(_pcb)) {
@ -2324,7 +2334,7 @@ static void _mdns_probe_all_pcbs(mdns_srv_item_t **services, size_t len, bool pr
uint8_t i, j;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
if (_mdns_server->interfaces[i].pcbs[j].pcb) {
if (mdns_is_netif_ready(i, j)) {
mdns_pcb_t *_pcb = &_mdns_server->interfaces[i].pcbs[j];
if (clear_old_probe) {
free(_pcb->probe_services);
@ -2619,7 +2629,7 @@ static void _mdns_remove_scheduled_service_packets(mdns_service_t *service)
mdns_pcb_t *_pcb = &_mdns_server->interfaces[q->tcpip_if].pcbs[q->ip_protocol];
if (_pcb->pcb) {
if (mdns_is_netif_ready(q->tcpip_if, q->ip_protocol)) {
if (PCB_STATE_IS_PROBING(_pcb)) {
uint8_t i;
//check if we are probing this service
@ -2820,6 +2830,22 @@ static int _mdns_check_txt_collision(mdns_service_t *service, const uint8_t *dat
return 0;//same
}
static esp_err_t mdns_pcb_deinit_local(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_proto)
{
esp_err_t err = _mdns_pcb_deinit(tcpip_if, ip_proto);
mdns_pcb_t *_pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_proto];
if (_pcb == NULL || err != ESP_OK) {
return err;
}
free(_pcb->probe_services);
_pcb->state = PCB_OFF;
_pcb->probe_ip = false;
_pcb->probe_services = NULL;
_pcb->probe_services_len = 0;
_pcb->probe_running = false;
_pcb->failed_probes = 0;
return ESP_OK;
}
/**
* @brief Set interface as duplicate if another is found on the same subnet
*/
@ -2831,11 +2857,11 @@ static void _mdns_dup_interface(mdns_if_t tcpip_if)
return; // no other interface found
}
for (i = 0; i < MDNS_IP_PROTOCOL_MAX; i++) {
if (_mdns_server->interfaces[other_if].pcbs[i].pcb) {
if (mdns_is_netif_ready(other_if, i)) {
//stop this interface and mark as dup
if (_mdns_server->interfaces[tcpip_if].pcbs[i].pcb) {
if (mdns_is_netif_ready(tcpip_if, i)) {
_mdns_clear_pcb_tx_queue_head(tcpip_if, i);
_mdns_pcb_deinit(tcpip_if, i);
mdns_pcb_deinit_local(tcpip_if, i);
}
_mdns_server->interfaces[tcpip_if].pcbs[i].state = PCB_DUP;
_mdns_announce_pcb(other_if, i, NULL, 0, true);
@ -3058,6 +3084,29 @@ static bool _mdns_name_is_discovery(mdns_name_t *name, uint16_t type)
);
}
/**
* @brief Check if the parsed name is self-hosted, i.e. we should resolve conflicts
*/
static bool _mdns_name_is_selfhosted(mdns_name_t *name)
{
if (_str_null_or_empty(_mdns_server->hostname)) { // self-hostname needs to be defined
return false;
}
// hostname only -- check if selfhosted name
if (_str_null_or_empty(name->service) && _str_null_or_empty(name->proto) &&
strcasecmp(name->host, _mdns_server->hostname) == 0 ) {
return true;
}
// service -- check if selfhosted service
mdns_srv_item_t *srv = _mdns_get_service_item(name->service, name->proto, NULL);
if (srv && strcasecmp(_mdns_server->hostname, srv->service->hostname) == 0) {
return true;
}
return false;
}
/**
* @brief Check if the parsed name is ours (matches service or host name)
*/
@ -3666,7 +3715,7 @@ void mdns_parse_packet(mdns_rx_packet_t *packet)
}
}
}
bool is_selfhosted = _mdns_name_is_selfhosted(name);
if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) {
continue;//error
}
@ -3694,6 +3743,9 @@ void mdns_parse_packet(mdns_rx_packet_t *packet)
_mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
continue;
}
if (!is_selfhosted) {
continue;
}
//detect collision (-1=won, 0=none, 1=lost)
int col = 0;
if (mdns_class > 1) {
@ -3784,6 +3836,9 @@ void mdns_parse_packet(mdns_rx_packet_t *packet)
_mdns_remove_parsed_question(parsed_packet, type, service);
continue;
}
if (!_mdns_name_is_selfhosted(name)) {
continue;
}
//detect collision (-1=won, 0=none, 1=lost)
int col = 0;
if (mdns_class > 1) {
@ -3818,6 +3873,9 @@ void mdns_parse_packet(mdns_rx_packet_t *packet)
_mdns_remove_parsed_question(parsed_packet, type, NULL);
continue;
}
if (!_mdns_name_is_selfhosted(name)) {
continue;
}
//detect collision (-1=won, 0=none, 1=lost)
int col = 0;
if (mdns_class > 1) {
@ -3868,6 +3926,9 @@ void mdns_parse_packet(mdns_rx_packet_t *packet)
_mdns_remove_parsed_question(parsed_packet, type, NULL);
continue;
}
if (!_mdns_name_is_selfhosted(name)) {
continue;
}
//detect collision (-1=won, 0=none, 1=lost)
int col = 0;
if (mdns_class > 1) {
@ -3940,8 +4001,9 @@ clear_rx_packet:
*/
void _mdns_enable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (!_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
if (!mdns_is_netif_ready(tcpip_if, ip_protocol)) {
if (_mdns_pcb_init(tcpip_if, ip_protocol)) {
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
return;
}
}
@ -3955,9 +4017,9 @@ void _mdns_disable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
_mdns_clean_netif_ptr(tcpip_if);
if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
if (mdns_is_netif_ready(tcpip_if, ip_protocol)) {
_mdns_clear_pcb_tx_queue_head(tcpip_if, ip_protocol);
_mdns_pcb_deinit(tcpip_if, ip_protocol);
mdns_pcb_deinit_local(tcpip_if, ip_protocol);
mdns_if_t other_if = _mdns_get_other_if (tcpip_if);
if (other_if != MDNS_MAX_INTERFACES && _mdns_server->interfaces[other_if].pcbs[ip_protocol].state == PCB_DUP) {
_mdns_server->interfaces[other_if].pcbs[ip_protocol].state = PCB_OFF;
@ -4625,7 +4687,7 @@ static mdns_tx_packet_t *_mdns_create_search_packet(mdns_search_once_t *search,
static void _mdns_search_send_pcb(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
mdns_tx_packet_t *packet = NULL;
if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT) {
if (mdns_is_netif_ready(tcpip_if, ip_protocol) && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT) {
packet = _mdns_create_search_packet(search, tcpip_if, ip_protocol);
if (!packet) {
return;
@ -5348,16 +5410,10 @@ esp_err_t mdns_init(void)
s_esp_netifs[i].netif = NULL;
}
_mdns_server->lock = xSemaphoreCreateMutex();
if (!_mdns_server->lock) {
err = ESP_ERR_NO_MEM;
goto free_server;
}
_mdns_server->action_queue = xQueueCreate(MDNS_ACTION_QUEUE_LEN, sizeof(mdns_action_t *));
if (!_mdns_server->action_queue) {
err = ESP_ERR_NO_MEM;
goto free_lock;
goto free_server;
}
_mdns_server->action_sema = xSemaphoreCreateBinary();
@ -5424,8 +5480,6 @@ free_event_handlers:
vSemaphoreDelete(_mdns_server->action_sema);
free_queue:
vQueueDelete(_mdns_server->action_queue);
free_lock:
vSemaphoreDelete(_mdns_server->lock);
free_server:
free(_mdns_server);
_mdns_server = NULL;
@ -5447,7 +5501,7 @@ void mdns_free(void)
_mdns_service_task_stop();
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
_mdns_pcb_deinit(i, j);
mdns_pcb_deinit_local(i, j);
}
}
free((char *)_mdns_server->hostname);
@ -5473,7 +5527,6 @@ void mdns_free(void)
free(h);
}
vSemaphoreDelete(_mdns_server->action_sema);
vSemaphoreDelete(_mdns_server->lock);
free(_mdns_server);
_mdns_server = NULL;
}
@ -5685,6 +5738,131 @@ bool mdns_service_exists_with_instance(const char *instance, const char *service
return _mdns_get_service_item_instance(instance, service_type, proto, hostname) != NULL;
}
static mdns_txt_item_t *_copy_mdns_txt_items(mdns_txt_linked_item_t *items, uint8_t **txt_value_len, size_t *txt_count)
{
mdns_txt_item_t *ret = NULL;
size_t ret_index = 0;
for (mdns_txt_linked_item_t *tmp = items; tmp != NULL; tmp = tmp->next) {
ret_index++;
}
*txt_count = ret_index;
ret = (mdns_txt_item_t *)calloc(ret_index, sizeof(mdns_txt_item_t));
*txt_value_len = (uint8_t *)calloc(ret_index, sizeof(uint8_t));
if (!ret || !(*txt_value_len)) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
ret_index = 0;
for (mdns_txt_linked_item_t *tmp = items; tmp != NULL; tmp = tmp->next) {
size_t key_len = strlen(tmp->key);
char *key = (char *)malloc(key_len + 1);
if (!key) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
memcpy(key, tmp->key, key_len);
key[key_len] = 0;
ret[ret_index].key = key;
char *value = (char *)malloc(tmp->value_len + 1);
if (!value) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
memcpy(value, tmp->value, tmp->value_len);
value[tmp->value_len] = 0;
ret[ret_index].value = value;
(*txt_value_len)[ret_index] = tmp->value_len;
ret_index++;
}
return ret;
handle_error:
for (size_t y = 0; y < ret_index + 1; y++) {
mdns_txt_item_t *t = &ret[y];
free((char *)t->key);
free((char *)t->value);
}
free(*txt_value_len);
free(ret);
return NULL;
}
static mdns_ip_addr_t *_copy_delegated_host_address_list(char *hostname)
{
mdns_host_item_t *host = _mdns_host_list;
while (host) {
if (strcasecmp(host->hostname, hostname) == 0) {
return copy_address_list(host->address_list);
}
}
return NULL;
}
static mdns_result_t *_mdns_lookup_delegated_service(const char *instance, const char *service, const char *proto, size_t max_results)
{
if (_str_null_or_empty(service) || _str_null_or_empty(proto)) {
return NULL;
}
mdns_result_t *results = NULL;
size_t num_results = 0;
mdns_srv_item_t *s = _mdns_server->services;
while (s) {
mdns_service_t *srv = s->service;
if (srv && srv->hostname && (_str_null_or_empty(_mdns_server->hostname) || strcmp(_mdns_server->hostname, srv->hostname) != 0)) {
if (!strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) &&
(_str_null_or_empty(instance) || _mdns_instance_name_match(srv->instance, instance))) {
mdns_result_t *item = (mdns_result_t *)malloc(sizeof(mdns_result_t));
if (!item) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
item->next = results;
results = item;
item->esp_netif = NULL;
item->ttl = UINT32_MAX;
item->ip_protocol = MDNS_IP_PROTOCOL_MAX;
item->instance_name = strndup(srv->instance, MDNS_NAME_BUF_LEN - 1);
if (!item->instance_name) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
item->service_type = strndup(srv->service, MDNS_NAME_BUF_LEN - 1);
if (!item->service_type) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
item->proto = strndup(srv->proto, MDNS_NAME_BUF_LEN - 1);
if (!item->proto) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
item->hostname = strndup(srv->hostname, MDNS_NAME_BUF_LEN - 1);
if (!item->hostname) {
HOOK_MALLOC_FAILED;
goto handle_error;
}
item->port = srv->port;
item->txt = _copy_mdns_txt_items(srv->txt, &(item->txt_value_len), &(item->txt_count));
item->addr = _copy_delegated_host_address_list(item->hostname);
if (!item->addr) {
goto handle_error;
}
if (num_results < max_results) {
num_results++;
}
if (num_results >= max_results) {
break;
}
}
}
s = s->next;
}
return results;
handle_error:
mdns_query_results_free(results);
return NULL;
}
esp_err_t mdns_service_port_set_for_host(const char *instance, const char *service, const char *proto, const char *hostname, uint16_t port)
{
if (!_mdns_server || !_mdns_server->services || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) {
@ -6156,6 +6334,21 @@ esp_err_t mdns_query_txt(const char *instance, const char *service, const char *
return mdns_query(instance, service, proto, MDNS_TYPE_TXT, timeout, 1, result);
}
esp_err_t mdns_lookup_delegated_service(const char *instance, const char *service, const char *proto, size_t max_results,
mdns_result_t **result)
{
if (!_mdns_server) {
return ESP_ERR_INVALID_STATE;
}
if (!result || _str_null_or_empty(service) || _str_null_or_empty(proto)) {
return ESP_ERR_INVALID_ARG;
}
MDNS_SERVICE_LOCK();
*result = _mdns_lookup_delegated_service(instance, service, proto, max_results);
MDNS_SERVICE_UNLOCK();
return ESP_OK;
}
esp_err_t mdns_query_a(const char *name, uint32_t timeout, esp_ip4_addr_t *addr)
{
mdns_result_t *result = NULL;

View File

@ -21,16 +21,26 @@
#include "mdns_networking.h"
#include "esp_netif_net_stack.h"
extern mdns_server_t *_mdns_server;
/*
* MDNS Server Networking
*
*/
static const char *TAG = "mdns_networking";
enum interface_protocol {
PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4,
PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6
};
typedef struct interfaces {
bool ready;
int proto;
} interfaces_t;
static interfaces_t s_interfaces[MDNS_MAX_INTERFACES];
static struct udp_pcb *_pcb_main = NULL;
static const char *TAG = "mdns_networking";
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
/**
@ -53,7 +63,7 @@ static esp_err_t _udp_pcb_main_init(void)
_pcb_main->mcast_ttl = 255;
_pcb_main->remote_port = MDNS_SERVICE_PORT;
ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE));
udp_recv(_pcb_main, &_udp_recv, _mdns_server);
udp_recv(_pcb_main, &_udp_recv, NULL);
return ESP_OK;
}
@ -168,29 +178,26 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
//lwip does not return the proper pcb if you have more than one for the same multicast address (but different interfaces)
struct netif *netif = NULL;
struct udp_pcb *pcb = NULL;
bool found = false;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
pcb = _mdns_server->interfaces[i].pcbs[packet->ip_protocol].pcb;
netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
if (pcb && netif && netif == ip_current_input_netif ()) {
if (s_interfaces[i].proto && netif && netif == ip_current_input_netif ()) {
if (packet->src.type == IPADDR_TYPE_V4) {
#if CONFIG_LWIP_IPV6
if ((packet->src.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr) != (netif->ip_addr.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr)) {
#else
if ((packet->src.u_addr.ip4.addr & netif->netmask.addr) != (netif->ip_addr.addr & netif->netmask.addr)) {
#endif //packet source is not in the same subnet
pcb = NULL;
break;
}
}
packet->tcpip_if = i;
found = true;
break;
}
pcb = NULL;
}
if (!pcb || !_mdns_server || !_mdns_server->action_queue
|| _mdns_send_rx_action(packet) != ESP_OK) {
if (!found || _mdns_send_rx_action(packet) != ESP_OK) {
pbuf_free(this_pb);
free(packet);
}
@ -198,6 +205,12 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
}
bool mdns_is_netif_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto)
{
return s_interfaces[netif].ready &&
s_interfaces[netif].proto & (ip_proto == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
}
/**
* @brief Check if any of the interfaces is up
*/
@ -206,7 +219,7 @@ static bool _udp_pcb_is_in_use(void)
int i, p;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (p = 0; p < MDNS_IP_PROTOCOL_MAX; p++) {
if (_mdns_server->interfaces[i].pcbs[p].pcb) {
if (mdns_is_netif_ready(i, p)) {
return true;
}
}
@ -219,19 +232,9 @@ static bool _udp_pcb_is_in_use(void)
*/
static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (!_mdns_server) {
return;
}
mdns_pcb_t *_pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
if (_pcb->pcb) {
free(_pcb->probe_services);
_pcb->state = PCB_OFF;
_pcb->pcb = NULL;
_pcb->probe_ip = false;
_pcb->probe_services = NULL;
_pcb->probe_services_len = 0;
_pcb->probe_running = false;
_pcb->failed_probes = 0;
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (s_interfaces[tcpip_if].proto == 0) {
s_interfaces[tcpip_if].ready = false;
_udp_join_group(tcpip_if, ip_protocol, false);
if (!_udp_pcb_is_in_use()) {
_udp_pcb_main_deinit();
@ -244,7 +247,7 @@ static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
*/
static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (!_mdns_server || _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
if (mdns_is_netif_ready(tcpip_if, ip_protocol)) {
return ESP_ERR_INVALID_STATE;
}
@ -257,9 +260,9 @@ static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco
if (err) {
return err;
}
s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
s_interfaces[tcpip_if].ready = true;
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = _pcb_main;
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
return ESP_OK;
}
@ -323,14 +326,13 @@ static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
{
void *nif = NULL;
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
mdns_pcb_t *_pcb = &_mdns_server->interfaces[msg->tcpip_if].pcbs[msg->ip_protocol];
nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if));
if (!nif) {
if (!nif || !mdns_is_netif_ready(msg->tcpip_if, msg->ip_protocol) || _pcb_main == NULL) {
pbuf_free(msg->pbt);
msg->err = ERR_IF;
return ERR_IF;
}
esp_err_t err = udp_sendto_if (_pcb->pcb, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
esp_err_t err = udp_sendto_if (_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
pbuf_free(msg->pbt);
msg->err = err;
return err;

View File

@ -27,7 +27,17 @@
#include <net/if.h>
#endif
extern mdns_server_t *_mdns_server;
enum interface_protocol {
PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4,
PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6
};
typedef struct interfaces {
int sock;
int proto;
} interfaces_t;
static interfaces_t s_interfaces[MDNS_MAX_INTERFACES];
static const char *TAG = "mdns_networking";
static bool s_run_sock_recv_task = false;
@ -47,28 +57,22 @@ struct pbuf {
#define s6_addr32 un.u32_addr
#endif // CONFIG_IDF_TARGET_LINUX
static void __attribute__((constructor)) ctor_networking_socket(void)
{
for (int i = 0; i < sizeof(s_interfaces) / sizeof(s_interfaces[0]); ++i) {
s_interfaces[i].sock = -1;
s_interfaces[i].proto = 0;
}
}
static void delete_socket(int sock)
{
close(sock);
}
static struct udp_pcb *sock_to_pcb(int sock)
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (sock < 0) {
return NULL;
}
// Note: sock=0 is a valid descriptor, so save it as +1 ("1" is a valid pointer)
intptr_t sock_plus_one = sock + 1;
return (struct udp_pcb *)sock_plus_one;
}
static int pcb_to_sock(struct udp_pcb *pcb)
{
if (pcb == NULL) {
return -1;
}
intptr_t sock_plus_one = (intptr_t)pcb;
return sock_plus_one - 1;
return s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
}
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
@ -90,24 +94,18 @@ void _mdns_packet_free(mdns_rx_packet_t *packet)
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
struct udp_pcb *pcb = _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = NULL;
if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb == NULL &&
_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb == NULL) {
// if the interface for both protocol uninitialized, close the interface socket
int sock = pcb_to_sock(pcb);
if (sock >= 0) {
delete_socket(sock);
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
if (s_interfaces[tcpip_if].proto == 0) {
// if the interface for both protocols uninitialized, close the interface socket
if (s_interfaces[tcpip_if].sock >= 0) {
delete_socket(s_interfaces[tcpip_if].sock);
}
}
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (int j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
if (_mdns_server->interfaces[i].pcbs[j].pcb)
// If any of the interfaces/protocol initialized
{
return ESP_OK;
}
if (s_interfaces[i].sock >= 0) {
// If any of the interfaces initialized
return ESP_OK;
}
}
@ -191,7 +189,10 @@ static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t p
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
{
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb);
if (!(s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6))) {
return 0;
}
int sock = s_interfaces[tcpip_if].sock;
if (sock < 0) {
return 0;
}
@ -250,12 +251,10 @@ void sock_recv_task(void *arg)
FD_ZERO(&rfds);
int max_sock = -1;
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
for (int j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
int sock = pcb_to_sock(_mdns_server->interfaces[i].pcbs[j].pcb);
if (sock >= 0) {
FD_SET(sock, &rfds);
max_sock = MAX(max_sock, sock);
}
int sock = s_interfaces[i].sock;
if (sock >= 0) {
FD_SET(sock, &rfds);
max_sock = MAX(max_sock, sock);
}
}
if (max_sock < 0) {
@ -270,11 +269,7 @@ void sock_recv_task(void *arg)
break;
} else if (s > 0) {
for (int tcpip_if = 0; tcpip_if < MDNS_MAX_INTERFACES; tcpip_if++) {
// Both protocols share once socket
int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb);
if (sock < 0) {
sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb);
}
int sock = s_interfaces[tcpip_if].sock;
if (sock < 0) {
continue;
}
@ -323,7 +318,7 @@ void sock_recv_task(void *arg)
packet->dest.type = packet->src.type;
packet->ip_protocol =
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
if (!_mdns_server || !_mdns_server->action_queue || _mdns_send_rx_action(packet) != ESP_OK) {
if (_mdns_send_rx_action(packet) != ESP_OK) {
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
free(packet->pb->payload);
free(packet->pb);
@ -344,39 +339,43 @@ static void mdns_networking_init(void)
}
}
static struct udp_pcb *create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
static bool create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
return _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
if (s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6)) {
return true;
}
mdns_ip_protocol_t other_ip_proto = ip_protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_IP_PROTOCOL_V6 : MDNS_IP_PROTOCOL_V4;
int sock = s_interfaces[tcpip_if].sock;
esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
if (_mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb) {
struct udp_pcb *other_pcb = _mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb;
int err = join_mdns_multicast_group(pcb_to_sock(other_pcb), netif, ip_protocol);
if (sock >= 0) {
mdns_ip_protocol_t other_ip_proto = ip_protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_IP_PROTOCOL_V6 : MDNS_IP_PROTOCOL_V4;
int err = join_mdns_multicast_group(sock, netif, other_ip_proto);
if (err < 0) {
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
return NULL;
return false;
}
return other_pcb;
s_interfaces[tcpip_if].proto |= (other_ip_proto == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
return true;
}
int sock = create_socket(netif);
sock = create_socket(netif);
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create the socket!");
return NULL;
return false;
}
int err = join_mdns_multicast_group(sock, netif, ip_protocol);
if (err < 0) {
ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
}
return sock_to_pcb(sock);
s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
s_interfaces[tcpip_if].sock = sock;
return true;
}
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%d, ip_protocol=%d)", tcpip_if, ip_protocol);
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = create_pcb(tcpip_if, ip_protocol);
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
if (!create_pcb(tcpip_if, ip_protocol)) {
return ESP_FAIL;
}
mdns_networking_init();
return ESP_OK;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -19,6 +19,8 @@
*/
esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet);
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
/**
* @brief Start PCB
*/

View File

@ -33,6 +33,12 @@
#endif
#define MDNS_MAX_PREDEF_INTERFACES (CONFIG_MDNS_PREDEF_NETIF_STA + CONFIG_MDNS_PREDEF_NETIF_AP + CONFIG_MDNS_PREDEF_NETIF_ETH)
#ifdef CONFIG_LWIP_IPV6_NUM_ADDRESSES
#define NETIF_IPV6_MAX_NUMS CONFIG_LWIP_IPV6_NUM_ADDRESSES
#else
#define NETIF_IPV6_MAX_NUMS 3
#endif
/** Number of configured interfaces */
#if MDNS_MAX_PREDEF_INTERFACES > CONFIG_MDNS_MAX_INTERFACES
#warning Number of configured interfaces is less then number of predefined interfaces. Please update CONFIG_MDNS_MAX_INTERFACES.
@ -341,7 +347,6 @@ typedef struct mdns_tx_packet_s {
typedef struct {
mdns_pcb_state_t state;
struct udp_pcb *pcb;
mdns_srv_item_t **probe_services;
uint8_t probe_services_len;
uint8_t probe_ip;
@ -382,7 +387,6 @@ typedef struct mdns_server_s {
const char *hostname;
const char *instance;
mdns_srv_item_t *services;
SemaphoreHandle_t lock;
QueueHandle_t action_queue;
SemaphoreHandle_t action_sema;
mdns_tx_packet_t *tx_queue_head;

View File

@ -5,3 +5,7 @@ set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main esp_netif_linux)
project(mdns_host)
# Enable sanitizers for mdns implementation
idf_component_get_property(mdns mdns COMPONENT_LIB)
target_link_options(${mdns} INTERFACE -fsanitize=address -fsanitize=undefined)

View File

@ -1,2 +0,0 @@
idf_component_register(SRCS esp_event_mock.c
INCLUDE_DIRS include)

View File

@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h"
#include "esp_event.h"
const char *WIFI_EVENT = "WIFI_EVENT";
const char *IP_EVENT = "IP_EVENT";
esp_err_t esp_event_handler_register(const char *event_base, int32_t event_id, void *event_handler, void *event_handler_arg)
{
return ESP_OK;
}
esp_err_t esp_event_handler_unregister(const char *event_base, int32_t event_id, void *event_handler)
{
return ESP_OK;
}

View File

@ -1,15 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "stdbool.h"
#include "esp_err.h"
#include "esp_event_base.h"
#include "bsd/string.h"
esp_err_t esp_event_handler_register(const char *event_base, int32_t event_id, void *event_handler, void *event_handler_arg);
esp_err_t esp_event_handler_unregister(const char *event_base, int32_t event_id, void *event_handler);

View File

@ -1,19 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
typedef enum {
WIFI_EVENT_STA_CONNECTED, /**< ESP32 station connected to AP */
WIFI_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */
WIFI_EVENT_AP_START, /**< ESP32 soft-AP start */
WIFI_EVENT_AP_STOP, /**< ESP32 soft-AP stop */
} mdns_used_event_t;
#define ESP_EVENT_DECLARE_BASE(x)
#define ESP_EVENT_ANY_ID (-1)
typedef void *esp_event_base_t;
typedef void *system_event_t;

View File

@ -1,3 +1,2 @@
idf_component_register(SRCS esp_netif_linux.c
INCLUDE_DIRS include $ENV{IDF_PATH}/components/esp_netif/include
REQUIRES esp_event)
INCLUDE_DIRS include $ENV{IDF_PATH}/components/esp_netif/include)

View File

@ -55,6 +55,7 @@ esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return ESP_OK;
}
@ -63,6 +64,29 @@ esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_stat
return ESP_OK;
}
int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[])
{
if (esp_netif == NULL) {
return 0;
}
struct ifaddrs *addrs, *tmp;
int addr_count = 0;
getifaddrs(&addrs);
tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
memcpy(&if_ip6[addr_count++], &pAddr->sin6_addr, 4 * 4);
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return addr_count;
}
esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6)
{

View File

@ -1,6 +1,5 @@
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
INCLUDE_DIRS include
REQUIRES esp_event)
INCLUDE_DIRS include)
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17

View File

@ -7,6 +7,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "bsd/string.h"
typedef struct esp_timer *esp_timer_handle_t;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -24,3 +24,8 @@ static inline void _mdns_packet_free(mdns_rx_packet_t *packet)
free(packet->pb);
free(packet);
}
static inline bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
{
return true;
}

View File

@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n

View File

@ -93,14 +93,6 @@ 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.
@ -267,13 +259,6 @@ 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
@ -829,22 +814,23 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
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
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
# The last two are generated
# This tag can be used to specify the character encoding of the source files
@ -1094,12 +1080,6 @@ 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
@ -1518,16 +1498,6 @@ 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
@ -1821,15 +1791,6 @@ 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
@ -1911,15 +1872,6 @@ 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
@ -2017,14 +1969,6 @@ 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
@ -2204,14 +2148,6 @@ 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
@ -2245,22 +2181,7 @@ 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
@ -2474,17 +2395,6 @@ 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

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

@ -0,0 +1,132 @@
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 DCEs 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.
DTEs
~~~~~
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,63 @@
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
.. figure:: DCE_DTE_collaboration.png
:alt: DCE_architecture
DCE_architecture
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.
.. figure:: CMux_collaboration.png
:alt: CMUX Terminal
CMUX Terminal

56
docs/esp_modem/generate_docs Executable file
View File

@ -0,0 +1,56 @@
# Cleanup the generated html
rm -rf html
# Generate C++ API header of the DCE
cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang++ -E -P -CC -xc++ -I../../components/esp_modem/include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > en/esp_modem_dce.hpp
# Generate C API header of the modem_api.h
cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang -E -P -CC -xc -I../../components/esp_modem/include -DGENERATE_DOCS - | sed -n '1,/DCE command documentation/!p' > en/esp_modem_api_commands.h
# RST with links to C++ API
cat ../../components/esp_modem/include/generate/esp_modem_command_declare.inc | clang -E -P -xc -I../../components/esp_modem/include -DGENERATE_DOCS -DGENERATE_RST_LINKS - | sed 's/NL/\n/g' > en/cxx_api_links.rst
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('select');
mySpan.style.float = 'left';
latest_ver = document.createElement('option');
latest_ver.value = 'latest';
latest_ver.textContent = 'latest(master)';
mySpan.append(latest_ver);
var myArray = ['v1.0.0''];
for (var i = myArray.length - 1; i >= 0; i--) {
current_ver = document.createElement('option');
current_ver.value = myArray[i];
current_ver.textContent = 'release-'+myArray[i];
mySpan.append(current_ver);
}
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
mySpan.addEventListener('change', function() {
window.location.href='https://docs.espressif.com/projects/esp-protocols/esp_modem/docs/'+event.target.value+'/index.html'
});
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.style.float = 'left';
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

@ -21,8 +21,8 @@ PROJECT_NAME = "MQTT C++ client"
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/include/esp_mqtt.hpp \
$(PROJECT_PATH)/include/esp_mqtt_client_config.hpp
$(PROJECT_PATH)/../components/esp_mqtt_cxx/include/esp_mqtt.hpp \
$(PROJECT_PATH)/../components/esp_mqtt_cxx/include/esp_mqtt_client_config.hpp
## Get warnings for functions that have no documentation for their parameters or return value
##

View File

@ -36,8 +36,8 @@ Events are dispatched throug calls to member functions each one dedicated to a t
Application Example
-------------------
* :example:`tcp <../examples/tcp>`
* :example:`ssl <../examples/ssl>`
* :example:`tcp <../../components/esp_mqtt_cxx/examples/tcp>`
* :example:`ssl <../../components/esp_mqtt_cxx/examples/ssl>`
API Reference
-------------

View File

@ -21,7 +21,7 @@ PROJECT_NAME = "ESP Protocols Programming Guide"
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/include/mdns.h
$(PROJECT_PATH)/../components/esp_websocket_client/include/esp_websocket_client.h
## Get warnings for functions that have no documentation for their parameters or return value
##

View File

@ -0,0 +1,23 @@
# flake8: noqa
from esp_docs.conf_docs import *
extensions += [
'sphinx_copybutton',
# Needed as a trigger for running doxygen
'esp_docs.esp_extensions.dummy_build_system',
'esp_docs.esp_extensions.run_doxygen',
]
# link roles config
github_repo = 'espressif/esp-protocols'
# context used by sphinx_idf_theme
html_context['github_user'] = 'espressif'
html_context['github_repo'] = 'esp-docs'
# Extra options required by sphinx_idf_theme
project_slug = 'esp-idf' # >=5.0
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
idf_targets = ['esp32']
languages = ['en']

5
docs/generate_docs Executable file
View File

@ -0,0 +1,5 @@
cd asio; ./generate_docs; cd ../
cd esp_modem; ./generate_docs; cd ../
cd esp_websocket_client; ./generate_docs; cd ../
cd esp_mqtt_cxx; ./generate_docs; cd ../
cd mdns; ./generate_docs; cd ../

View File

@ -21,7 +21,7 @@ PROJECT_NAME = "ESP Protocols Programming Guide"
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/include/esp_websocket_client.h
$(PROJECT_PATH)/../components/mdns/include/mdns.h
## Get warnings for functions that have no documentation for their parameters or return value
##

24
docs/mdns/en/conf.py Normal file
View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses ../conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
try:
from conf_common import * # noqa: F403,F401
except ImportError:
import os
import sys
sys.path.insert(0, os.path.abspath('../'))
from conf_common import * # noqa: F403,F401
# General information about the project.
project = u'ESP-Protocols'
copyright = u'2016 - 2022, Espressif Systems (Shanghai) Co., Ltd'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'

View File

@ -1,8 +1,8 @@
build-docs --target esp32 --language en
build-docs --target esp32 --language zh_CN
cp -rf _build/en/esp32/html html_en
cp -rf _build/zh_CN/esp32/html html_zh_CN
cp -rf _build/en/esp32/html en
cp -rf _build/zh_CN/esp32/html zh_CN
rm -rf _build __pycache__ tee
# Modifes some version and target fields of index.html

18
docs/utils.sh Normal file
View File

@ -0,0 +1,18 @@
# Bash helper functions for adding SSH keys
function add_ssh_keys() {
local key_string="${1}"
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo -n "${key_string}" >~/.ssh/id_rsa_base64
base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
}
function add_doc_server_ssh_keys() {
local key_string="${1}"
local server_url="${2}"
local server_user="${3}"
add_ssh_keys "${key_string}"
echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config
}