forked from espressif/esp-protocols
Compare commits
72 Commits
websocket-
...
modem-v0.1
Author | SHA1 | Date | |
---|---|---|---|
9b48b0a5e0 | |||
25ac2d98c6 | |||
ce175df376 | |||
9d9d0db1ee | |||
b704aaa33b | |||
7a21db23ad | |||
fcd6f0bb14 | |||
52d7458b99 | |||
56cb58ced7 | |||
77a7538a02 | |||
c8d0a13f0e | |||
582f5b5ff6 | |||
06dd536d52 | |||
e66b19c414 | |||
0ad6cad8b2 | |||
ffa13e4b90 | |||
0fc1fc6b23 | |||
f1d1d79daf | |||
36d0d32b35 | |||
3bf488eb86 | |||
061885ad23 | |||
ab70791625 | |||
62cb235aa9 | |||
04c711f757 | |||
467fec5c9b | |||
5193ebc6ea | |||
07a347f907 | |||
d9c9681094 | |||
7310a7a0bc | |||
accf9244ca | |||
290197c210 | |||
b8d1e58778 | |||
cc4d33d871 | |||
ef0e48a678 | |||
187ef7676e | |||
a8714730fb | |||
d2f519f9e5 | |||
a045c1c885 | |||
543521a220 | |||
85be67e708 | |||
5b1b2cce75 | |||
1029078541 | |||
0015e5411c | |||
65b64e1fc1 | |||
d07237b2ce | |||
3c65fde2a7 | |||
35833d2730 | |||
bbb4b7e686 | |||
64e31dae48 | |||
63ce3a5d59 | |||
5dd138c883 | |||
088f7ac3f8 | |||
e079f8ba98 | |||
ff2b0734ea | |||
330332a0fb | |||
48c157bc46 | |||
98bf3efeb6 | |||
ae8479c77e | |||
444fae9066 | |||
bb4c002841 | |||
656ab21c9b | |||
2099434b3f | |||
8f00fc182a | |||
d74c296182 | |||
c4e85bd099 | |||
85ba60e405 | |||
89e1bd27b3 | |||
341fcb0f40 | |||
469f953b28 | |||
381eb314dc | |||
1ffc20c8e3 | |||
3456781494 |
2
.github/workflows/publish-docs-component.yml
vendored
2
.github/workflows/publish-docs-component.yml
vendored
@ -9,6 +9,8 @@ jobs:
|
||||
docs_build:
|
||||
name: Docs-Build-And-Upload
|
||||
runs-on: ubuntu-latest
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
|
171
.github/workflows/target-test.yml
vendored
171
.github/workflows/target-test.yml
vendored
@ -39,8 +39,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32c3"]
|
||||
|
||||
idf_target: ["esp32", "esp32s2", "esp32c3"]
|
||||
config: ["eth_custom_netif", "eth_def", "eth_no_ipv6", "eth_socket"]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -48,26 +48,41 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }} for ${{ matrix.config }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: components/mdns/examples/
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/examples/
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
cat sdkconfig.ci.eth_def >> sdkconfig.defaults
|
||||
idf.py build
|
||||
rm sdkconfig.defaults
|
||||
cat sdkconfig.ci.eth_custom_netif >> sdkconfig.defaults
|
||||
idf.py build
|
||||
rm sdkconfig.defaults
|
||||
cat sdkconfig.ci.eth_socket >> sdkconfig.defaults
|
||||
idf.py build
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_apps/
|
||||
rm -rf sdkconfig sdkconfig.defaults build build_${{ matrix.config }}
|
||||
cat sdkconfig.ci.${{ matrix.config }} >> sdkconfig.defaults
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
idf.py build
|
||||
mv build build_${{ matrix.config }}
|
||||
- name: Merge binaries with IDF-${{ matrix.idf_ver }} for ${{ matrix.config }}
|
||||
working-directory: components/mdns/examples
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd build_${{ matrix.config }}
|
||||
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
|
||||
cd ../
|
||||
cat build_${{ matrix.config }}/config/sdkconfig.json
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}
|
||||
path: |
|
||||
components/mdns/examples/build_${{ matrix.config }}/bootloader/bootloader.bin
|
||||
components/mdns/examples/build_${{ matrix.config }}/partition_table/partition-table.bin
|
||||
components/mdns/examples/build_${{ matrix.config }}/*.bin
|
||||
components/mdns/examples/build_${{ matrix.config }}/*.elf
|
||||
components/mdns/examples/build_${{ matrix.config }}/flasher_args.json
|
||||
components/mdns/examples/build_${{ matrix.config }}/config/sdkconfig.h
|
||||
components/mdns/examples/build_${{ matrix.config }}/config/sdkconfig.json
|
||||
if-no-files-found: error
|
||||
|
||||
build_asio:
|
||||
strategy:
|
||||
@ -75,7 +90,6 @@ jobs:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2"]
|
||||
example: ["asio_chat", "async_request", "socks4", "ssl_client_server", "tcp_echo_server", "udp_echo_server"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -83,23 +97,43 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
working-directory: components/asio/examples/${{ matrix.example }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/asio/examples/${{ matrix.example }}
|
||||
test -f sdkconfig.ci && cat sdkconfig.ci >> sdkconfig.defaults || echo "No sdkconfig.ci"
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
idf.py build
|
||||
- name: Merge binaries with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }} for ${{ matrix.example }}
|
||||
working-directory: components/asio/examples/${{ matrix.example }}/build
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }}
|
||||
path: |
|
||||
components/asio/examples/${{ matrix.example }}/build/bootloader/bootloader.bin
|
||||
components/asio/examples/${{ matrix.example }}/build//partition_table/partition-table.bin
|
||||
components/asio/examples/${{ matrix.example }}/build/*.bin
|
||||
components/asio/examples/${{ matrix.example }}/build/*.elf
|
||||
components/asio/examples/${{ matrix.example }}/build/flasher_args.json
|
||||
components/asio/examples/${{ matrix.example }}/build/config/sdkconfig.h
|
||||
components/asio/examples/${{ matrix.example }}/build/config/sdkconfig.json
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
build_websocket:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
@ -127,7 +161,14 @@ jobs:
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: components/esp_websocket_client/examples/build/
|
||||
path: |
|
||||
components/esp_websocket_client/examples/build/bootloader/bootloader.bin
|
||||
components/esp_websocket_client/examples/build/partition_table/partition-table.bin
|
||||
components/esp_websocket_client/examples/build/*.bin
|
||||
components/esp_websocket_client/examples/build/*.elf
|
||||
components/esp_websocket_client/examples/build/flasher_args.json
|
||||
components/esp_websocket_client/examples/build/config/sdkconfig.h
|
||||
components/esp_websocket_client/examples/build/config/sdkconfig.json
|
||||
if-no-files-found: error
|
||||
|
||||
run-target:
|
||||
@ -141,6 +182,8 @@ jobs:
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
container:
|
||||
image: python:3.7-buster
|
||||
options: --privileged # Privileged mode has access to serial ports
|
||||
@ -154,16 +197,98 @@ jobs:
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
pip install -r $GITHUB_WORKSPACE/components/esp_websocket_client/examples/requirements.txt
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/components/esp_websocket_client/examples/requirements.txt
|
||||
- name: Download Example Test to target
|
||||
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
|
||||
- name: Run Example Test on target
|
||||
working-directory: components/esp_websocket_client/examples
|
||||
run: |
|
||||
cp sdkconfig.ci sdkconfig.defaults
|
||||
pytest --log-cli-level DEBUG --junit-xml=./test_app_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
|
||||
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: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: examples/*.xml
|
||||
path: components/esp_websocket_client/examples/*.xml
|
||||
|
||||
run-target-mdns:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
config: ["eth_custom_netif", "eth_def", "eth_no_ipv6", "eth_socket"]
|
||||
name: Run mDNS Example Test on target
|
||||
needs: build_mdns
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
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 }}_${{ matrix.config }}
|
||||
path: components/mdns/examples/build_${{ matrix.config }}
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Download Example Test to target ${{ matrix.config }}
|
||||
run: |
|
||||
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/mdns/examples/build_${{ matrix.config }}/flash_image.bin
|
||||
- name: Run Example Test on target ${{ matrix.config }}
|
||||
working-directory: components/mdns/examples
|
||||
run: |
|
||||
rm -rf build
|
||||
mv build_${{ matrix.config }} build
|
||||
cat sdkconfig.ci.${{ matrix.config }} >> sdkconfig.defaults
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
rm -rf build sdkconfig.defaults
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}
|
||||
path: components/mdns/examples/*.xml
|
||||
|
||||
run-target-asio:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
example: ["asio_chat", "tcp_echo_server", "udp_echo_server", "ssl_client_server"]
|
||||
name: Run ASIO Example Test on target
|
||||
needs: build_asio
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
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 }}_${{ matrix.example }}
|
||||
path: components/asio/examples/${{ matrix.example }}/build
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Download Example Test to target ${{ matrix.config }}
|
||||
run: |
|
||||
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/asio/examples/${{ matrix.example }}/build/flash_image.bin
|
||||
- name: Run Example Test ${{ matrix.example }} on target
|
||||
working-directory: components/asio/examples/${{ matrix.example }}
|
||||
run: |
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }}
|
||||
path: components/asio/examples/${{ matrix.example }}/*.xml
|
||||
|
@ -396,7 +396,9 @@ static esp_netif_t *eth_start(void)
|
||||
#endif
|
||||
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||
gpio_install_isr_service(0);
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
spi_device_handle_t spi_handle = NULL;
|
||||
#endif
|
||||
spi_bus_config_t buscfg = {
|
||||
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
|
||||
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
|
||||
@ -414,9 +416,14 @@ static esp_netif_t *eth_start(void)
|
||||
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
|
||||
.queue_size = 20
|
||||
};
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
|
||||
/* dm9051 ethernet driver is based on spi driver */
|
||||
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
|
||||
#else
|
||||
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg);
|
||||
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
|
||||
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
|
||||
s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
||||
s_phy = esp_eth_phy_new_dm9051(&phy_config);
|
||||
@ -429,9 +436,13 @@ static esp_netif_t *eth_start(void)
|
||||
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
|
||||
.queue_size = 20
|
||||
};
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
|
||||
/* w5500 ethernet driver is based on spi driver */
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
||||
#else
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg);
|
||||
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
w5500_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
|
||||
s_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||
s_phy = esp_eth_phy_new_w5500(&phy_config);
|
||||
|
@ -1,29 +0,0 @@
|
||||
# This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, this
|
||||
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_asio_chat(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
msg = 'asio-chat: received hi'
|
||||
dut = env.get_dut('asio_chat', 'examples/protocols/asio/asio_chat')
|
||||
# start the test and expect the client to receive back it's original data
|
||||
dut.start_app()
|
||||
dut.expect(re.compile(r'{}'.format('Waiting for input')), timeout=30)
|
||||
dut.write(msg)
|
||||
dut.write('exit')
|
||||
dut.expect(re.compile(r'{}'.format(msg)), timeout=30)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_chat()
|
24
components/asio/examples/asio_chat/pytest.ini
Normal file
24
components/asio/examples/asio_chat/pytest.ini
Normal file
@ -0,0 +1,24 @@
|
||||
[pytest]
|
||||
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
|
||||
python_files = pytest_*.py
|
||||
|
||||
addopts =
|
||||
-s
|
||||
--embedded-services esp,idf
|
||||
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||
--tb short
|
||||
|
||||
# ignore DeprecationWarning
|
||||
filterwarnings =
|
||||
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
|
||||
|
||||
# log related
|
||||
log_cli = True
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
log_file = test.log
|
||||
log_file_level = INFO
|
||||
log_file_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_file_date_format = %Y-%m-%d %H:%M:%S
|
21
components/asio/examples/asio_chat/pytest_example_test.py
Normal file
21
components/asio/examples/asio_chat/pytest_example_test.py
Normal file
@ -0,0 +1,21 @@
|
||||
# This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, this
|
||||
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
def test_examples_asio_chat(dut):
|
||||
msg = 'asio-chat: received hi'
|
||||
# start the test and expect the client to receive back it's original data
|
||||
dut.expect(re.compile(b'Waiting for input'), timeout=30)
|
||||
dut.write(msg)
|
||||
dut.write('exit')
|
||||
msg = bytes(msg, encoding='utf8')
|
||||
dut.expect(re.compile(msg), timeout=30)
|
@ -1,16 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32c3'])
|
||||
def test_examples_asio_ssl(env, extra_data):
|
||||
|
||||
dut = env.get_dut('asio_ssl_client_server', 'examples/protocols/asio/ssl_client_server')
|
||||
dut.start_app()
|
||||
|
||||
dut.expect('Reply: GET / HTTP/1.1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_ssl()
|
24
components/asio/examples/ssl_client_server/pytest.ini
Normal file
24
components/asio/examples/ssl_client_server/pytest.ini
Normal file
@ -0,0 +1,24 @@
|
||||
[pytest]
|
||||
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
|
||||
python_files = pytest_*.py
|
||||
|
||||
addopts =
|
||||
-s
|
||||
--embedded-services esp,idf
|
||||
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||
--tb short
|
||||
|
||||
# ignore DeprecationWarning
|
||||
filterwarnings =
|
||||
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
|
||||
|
||||
# log related
|
||||
log_cli = True
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
log_file = test.log
|
||||
log_file_level = INFO
|
||||
log_file_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_file_date_format = %Y-%m-%d %H:%M:%S
|
@ -0,0 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
def test_examples_asio_ssl(dut):
|
||||
dut.expect('Reply: GET / HTTP/1.1')
|
24
components/asio/examples/tcp_echo_server/pytest.ini
Normal file
24
components/asio/examples/tcp_echo_server/pytest.ini
Normal file
@ -0,0 +1,24 @@
|
||||
[pytest]
|
||||
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
|
||||
python_files = pytest_*.py
|
||||
|
||||
addopts =
|
||||
-s
|
||||
--embedded-services esp,idf
|
||||
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||
--tb short
|
||||
|
||||
# ignore DeprecationWarning
|
||||
filterwarnings =
|
||||
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
|
||||
|
||||
# log related
|
||||
log_cli = True
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
log_file = test.log
|
||||
log_file_level = INFO
|
||||
log_file_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_file_date_format = %Y-%m-%d %H:%M:%S
|
@ -1,12 +1,10 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import ttfw_idf
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_tcp_server(env, extra_data):
|
||||
def test_examples_protocol_asio_tcp_server(dut):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
@ -16,20 +14,13 @@ def test_examples_protocol_asio_tcp_server(env, extra_data):
|
||||
5. Test evaluates received test message on server stdout
|
||||
"""
|
||||
test_msg = b'echo message from client to server'
|
||||
dut1 = env.get_dut('tcp_echo_server', 'examples/protocols/asio/tcp_echo_server', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_tcp_echo_server.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_tcp_echo_server_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start test
|
||||
dut1.start_app()
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
data = dut.expect(re.compile(b' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30).group(1).decode()
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
dut.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
cli.connect((data, 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
@ -40,8 +31,5 @@ def test_examples_protocol_asio_tcp_server(env, extra_data):
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asi tcp server: {} (expected:{})'.format(data, test_msg))
|
||||
# 5. check the client message appears also on server terminal
|
||||
dut1.expect(test_msg.decode())
|
||||
dut.expect(test_msg.decode())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_tcp_server()
|
2
components/asio/examples/tcp_echo_server/sdkconfig.ci
Normal file
2
components/asio/examples/tcp_echo_server/sdkconfig.ci
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
@ -1,47 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_udp_server(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Start server
|
||||
3. Test connects to server and sends a test message
|
||||
4. Test evaluates received test message from server
|
||||
5. Test evaluates received test message on server stdout
|
||||
"""
|
||||
test_msg = b'echo message from client to server'
|
||||
dut1 = env.get_dut('udp_echo_server', 'examples/protocols/asio/udp_echo_server', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_udp_echo_server.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_udp_echo_server_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start test
|
||||
dut1.start_app()
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
if (data == test_msg):
|
||||
print('PASS: Received correct message')
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asio udp server: {} (expected:{})'.format(data, test_msg))
|
||||
# 5. check the client message appears also on server terminal
|
||||
dut1.expect(test_msg.decode())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_udp_server()
|
24
components/asio/examples/udp_echo_server/pytest.ini
Normal file
24
components/asio/examples/udp_echo_server/pytest.ini
Normal file
@ -0,0 +1,24 @@
|
||||
[pytest]
|
||||
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
|
||||
python_files = pytest_*.py
|
||||
|
||||
addopts =
|
||||
-s
|
||||
--embedded-services esp,idf
|
||||
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||
--tb short
|
||||
|
||||
# ignore DeprecationWarning
|
||||
filterwarnings =
|
||||
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
|
||||
|
||||
# log related
|
||||
log_cli = True
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
log_file = test.log
|
||||
log_file_level = INFO
|
||||
log_file_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_file_date_format = %Y-%m-%d %H:%M:%S
|
@ -0,0 +1,33 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
def test_examples_protocol_asio_udp_server(dut):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Start server
|
||||
3. Test connects to server and sends a test message
|
||||
4. Test evaluates received test message from server
|
||||
5. Test evaluates received test message on server stdout
|
||||
"""
|
||||
test_msg = b'echo message from client to server'
|
||||
data = dut.expect(re.compile(b' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30).group(1).decode()
|
||||
dut.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data, 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
if (data == test_msg):
|
||||
print('PASS: Received correct message')
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asio udp server: {} (expected:{})'.format(data, test_msg))
|
||||
# 5. check the client message appears also on server terminal
|
||||
dut.expect(test_msg.decode())
|
2
components/asio/examples/udp_echo_server/sdkconfig.ci
Normal file
2
components/asio/examples/udp_echo_server/sdkconfig.ci
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
@ -14,6 +14,7 @@ else()
|
||||
set(dependencies driver esp_event esp_netif)
|
||||
endif()
|
||||
|
||||
|
||||
set(srcs ${platform_srcs}
|
||||
"src/esp_modem_dte.cpp"
|
||||
"src/esp_modem_dce.cpp"
|
||||
@ -34,15 +35,15 @@ idf_component_register(SRCS "${srcs}"
|
||||
PRIV_INCLUDE_DIRS private_include
|
||||
REQUIRES ${dependencies})
|
||||
|
||||
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES COMPILE_FLAGS -Wno-format)
|
||||
endif()
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
28
components/esp_modem/Kconfig
Normal file
28
components/esp_modem/Kconfig
Normal file
@ -0,0 +1,28 @@
|
||||
menu "esp-modem"
|
||||
|
||||
config ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD
|
||||
bool "Defragment CMUX messages internally"
|
||||
default y
|
||||
help
|
||||
If enabled (default), the esp-modem automatically defragments CMUX messages
|
||||
to only pass the completed CMUX message to higher layers.
|
||||
This is useful for messages in command mode (if they're received fragmented).
|
||||
It's not a problem for messages in data mode as the upper layer (PPP protocol)
|
||||
defines message boundaries.
|
||||
Keep the default to true for most cases (as most devices use simply 1 byte CMUX
|
||||
length, as the internal Rx buffer of size >= 256 bytes won't overflow)
|
||||
Set to false if your devices uses 2 byte CMUX payload (e.g. A7672S).
|
||||
The operation would work without an issue in data mode, but some replies
|
||||
in command mode might come fragmented in rare cases so might need to retry
|
||||
AT commands.
|
||||
|
||||
config ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP
|
||||
int "Delay in ms to wait before creating another virtual terminal"
|
||||
default 0
|
||||
help
|
||||
Some devices might need a pause before sending SABM command that creates
|
||||
virtual terminal. This delay applies only to establishing a CMUX mode.
|
||||
The typical reason for failing SABM request without a delay is that
|
||||
some devices (SIM800) send MSC requests just after opening a new DLCI.
|
||||
|
||||
endmenu
|
@ -69,10 +69,24 @@ after creating multiple virtual terminals, designating some of them solely to da
|
||||
|
||||
### DTE's
|
||||
|
||||
Currently we support only UART, but modern modules support other communication interfaces, such as USB, SPI.
|
||||
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`
|
||||
|
@ -7,3 +7,4 @@ endif()
|
||||
idf_component_register(SRCS "ap_to_pppos.c"
|
||||
${NETWORK_DCE}
|
||||
INCLUDE_DIRS ".")
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
@ -11,12 +11,13 @@
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "network_dce.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "esp_mac.h"
|
||||
#include "dhcpserver/dhcpserver.h"
|
||||
#endif
|
||||
|
@ -61,7 +61,7 @@ int main()
|
||||
|
||||
bool pin_ok = true;
|
||||
if (dce->read_pin(pin_ok) == command_result::OK && !pin_ok) {
|
||||
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
||||
ESP_MODEM_THROW_IF_FALSE(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
||||
usleep(1000000);
|
||||
}
|
||||
std::string str;
|
||||
|
@ -70,9 +70,9 @@ public:
|
||||
.skip_phy_setup = false,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
throw_if_esp_fail(usb_host_install(&host_config), "USB Host install failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(usb_host_install(&host_config), "USB Host install failed");
|
||||
ESP_LOGD(TAG, "USB Host installed");
|
||||
throw_if_false(pdTRUE == xTaskCreatePinnedToCore(usb_host_task, "usb_host", 4096, NULL, config->task_priority + 1, NULL, usb_config->xCoreID), "USB host task failed");
|
||||
ESP_MODEM_THROW_IF_FALSE(pdTRUE == xTaskCreatePinnedToCore(usb_host_task, "usb_host", 4096, NULL, config->task_priority + 1, NULL, usb_config->xCoreID), "USB host task failed");
|
||||
}
|
||||
|
||||
// Install CDC-ACM driver
|
||||
@ -95,11 +95,11 @@ public:
|
||||
};
|
||||
|
||||
if (usb_config->cdc_compliant) {
|
||||
throw_if_esp_fail(this->CdcAcmDevice::open(usb_config->vid, usb_config->pid,
|
||||
ESP_MODEM_THROW_IF_ERROR(this->CdcAcmDevice::open(usb_config->vid, usb_config->pid,
|
||||
usb_config->interface_idx, &esp_modem_cdc_acm_device_config),
|
||||
"USB Device open failed");
|
||||
} else {
|
||||
throw_if_esp_fail(this->CdcAcmDevice::open_vendor_specific(usb_config->vid, usb_config->pid,
|
||||
ESP_MODEM_THROW_IF_ERROR(this->CdcAcmDevice::open_vendor_specific(usb_config->vid, usb_config->pid,
|
||||
usb_config->interface_idx, &esp_modem_cdc_acm_device_config),
|
||||
"USB Device open failed");
|
||||
}
|
||||
|
@ -1,17 +1,190 @@
|
||||
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_SERIAL_CONFIG
|
||||
prompt "Type of serial connection to the modem"
|
||||
default EXAMPLE_SERIAL_CONFIG_UART
|
||||
config EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "UART"
|
||||
help
|
||||
Connect to modem via UART.
|
||||
config EXAMPLE_SERIAL_CONFIG_USB
|
||||
bool "USB"
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
help
|
||||
Connect to modem via USB (CDC-ACM class). For IDF version >= 4.4.
|
||||
endchoice
|
||||
endmenu
|
||||
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_SERIAL_CONFIG
|
||||
prompt "Type of serial connection to the modem"
|
||||
default EXAMPLE_SERIAL_CONFIG_UART
|
||||
config EXAMPLE_SERIAL_CONFIG_UART
|
||||
bool "UART"
|
||||
help
|
||||
Connect to modem via UART.
|
||||
config EXAMPLE_SERIAL_CONFIG_USB
|
||||
bool "USB"
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
help
|
||||
Connect to modem via USB (CDC-ACM class). For IDF version >= 4.4.
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
help
|
||||
Select modem device connected to the ESP DTE.
|
||||
config EXAMPLE_MODEM_DEVICE_SHINY
|
||||
bool "SHINY"
|
||||
help
|
||||
SHINY is a GSM/GPRS module.
|
||||
It supports SHINY.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM800
|
||||
bool "SIM800"
|
||||
help
|
||||
SIMCom SIM800L is a GSM/GPRS module.
|
||||
It supports Quad-band 850/900/1800/1900MHz.
|
||||
config EXAMPLE_MODEM_DEVICE_BG96
|
||||
bool "BG96"
|
||||
help
|
||||
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7000
|
||||
bool "SIM7000"
|
||||
help
|
||||
SIM7000 is a Multi-Band LTE-FDD and GSM/GPRS/EDGE module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7070
|
||||
bool "SIM7070"
|
||||
help
|
||||
SIM7070 is Multi-Band CAT M and NB IoT module.
|
||||
config EXAMPLE_MODEM_DEVICE_SIM7600
|
||||
bool "SIM7600"
|
||||
help
|
||||
SIM7600 is a Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module.
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MODEM_PPP_APN
|
||||
string "Set MODEM APN"
|
||||
default "internet"
|
||||
help
|
||||
Set APN (Access Point Name), a logical name to choose data network
|
||||
|
||||
config EXAMPLE_MODEM_PPP_AUTH_USERNAME
|
||||
string "Set username for authentication"
|
||||
default "espressif"
|
||||
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
|
||||
help
|
||||
Set username for PPP Authentication.
|
||||
|
||||
config EXAMPLE_MODEM_PPP_AUTH_PASSWORD
|
||||
string "Set password for authentication"
|
||||
default "esp32"
|
||||
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
|
||||
help
|
||||
Set password for PPP Authentication.
|
||||
|
||||
config EXAMPLE_MODEM_PPP_AUTH_NONE
|
||||
bool "Skip PPP authentication"
|
||||
default n
|
||||
help
|
||||
Set to true for the PPP client to skip authentication
|
||||
|
||||
config EXAMPLE_SEND_MSG
|
||||
bool "Short message (SMS)"
|
||||
default n
|
||||
help
|
||||
Select this, the modem will send a short message before power off.
|
||||
|
||||
if EXAMPLE_SEND_MSG
|
||||
config EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER
|
||||
string "Peer Phone Number (with area code)"
|
||||
default "+8610086"
|
||||
help
|
||||
Enter the peer phone number that you want to send message to.
|
||||
endif
|
||||
|
||||
config EXAMPLE_NEED_SIM_PIN
|
||||
bool "SIM PIN needed"
|
||||
default n
|
||||
help
|
||||
Enable to set SIM PIN before starting the example
|
||||
|
||||
config EXAMPLE_SIM_PIN
|
||||
string "Set SIM PIN"
|
||||
default "1234"
|
||||
depends on EXAMPLE_NEED_SIM_PIN
|
||||
help
|
||||
Pin to unlock the SIM
|
||||
|
||||
choice EXAMPLE_FLOW_CONTROL
|
||||
bool "Set preferred modem control flow"
|
||||
default EXAMPLE_FLOW_CONTROL_NONE
|
||||
help
|
||||
Set the modem's preferred control flow
|
||||
|
||||
config EXAMPLE_FLOW_CONTROL_NONE
|
||||
bool "No control flow"
|
||||
config EXAMPLE_FLOW_CONTROL_SW
|
||||
bool "SW control flow"
|
||||
config EXAMPLE_FLOW_CONTROL_HW
|
||||
bool "HW control flow"
|
||||
endchoice
|
||||
|
||||
menu "UART Configuration"
|
||||
config EXAMPLE_MODEM_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
default 25
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
default 26
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RTS_PIN
|
||||
int "RTS Pin Number"
|
||||
default 27
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RTS.
|
||||
|
||||
config EXAMPLE_MODEM_UART_CTS_PIN
|
||||
int "CTS Pin Number"
|
||||
default 23
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART CTS.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE
|
||||
int "UART Event Task Stack Size"
|
||||
range 2000 6000
|
||||
default 2048
|
||||
help
|
||||
Stack size of UART event task.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY
|
||||
int "UART Event Task Priority"
|
||||
range 3 22
|
||||
default 5
|
||||
help
|
||||
Priority of UART event task.
|
||||
|
||||
config EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE
|
||||
int "UART Event Queue Size"
|
||||
range 10 40
|
||||
default 30
|
||||
help
|
||||
Length of UART event queue.
|
||||
|
||||
config EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE
|
||||
int "UART Pattern Queue Size"
|
||||
range 10 40
|
||||
default 20
|
||||
help
|
||||
Length of UART pattern queue.
|
||||
|
||||
config EXAMPLE_MODEM_UART_TX_BUFFER_SIZE
|
||||
int "UART TX Buffer Size"
|
||||
range 256 2048
|
||||
default 512
|
||||
help
|
||||
Buffer size of UART TX buffer.
|
||||
|
||||
config EXAMPLE_MODEM_UART_RX_BUFFER_SIZE
|
||||
int "UART RX Buffer Size"
|
||||
range 256 2048
|
||||
default 1024
|
||||
help
|
||||
Buffer size of UART RX buffer.
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
|
@ -26,6 +26,14 @@
|
||||
#include "console_helper.hpp"
|
||||
#include "my_module_dce.hpp"
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
|
||||
#elif defined(CONFIG_EXAMPLE_FLOW_CONTROL_SW)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_SW
|
||||
#elif defined(CONFIG_EXAMPLE_FLOW_CONTROL_HW)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_HW
|
||||
#endif
|
||||
|
||||
#define CHECK_ERR(cmd, success_action) do { \
|
||||
auto err = cmd; \
|
||||
if (err == command_result::OK) { \
|
||||
@ -64,8 +72,43 @@ extern "C" void app_main(void)
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_SERIAL_CONFIG_UART)
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
/* setup UART specific configuration based on kconfig options */
|
||||
dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
|
||||
dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
|
||||
dte_config.uart_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
|
||||
dte_config.uart_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
|
||||
dte_config.uart_config.flow_control = EXAMPLE_FLOW_CONTROL;
|
||||
dte_config.uart_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
|
||||
dte_config.uart_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
|
||||
dte_config.uart_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
|
||||
dte_config.task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE;
|
||||
dte_config.task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
|
||||
dte_config.dte_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
|
||||
auto uart_dte = create_uart_dte(&dte_config);
|
||||
|
||||
#if CONFIG_EXAMPLE_MODEM_DEVICE_SHINY == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SHINY module...");
|
||||
auto dce = create_shiny_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the BG96 module...");
|
||||
auto dce = create_BG96_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM800 module...");
|
||||
auto dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7000 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7000 module...");
|
||||
auto dce = create_SIM7000_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7070 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7070 module...");
|
||||
auto dce = create_SIM7070_dce(&dce_config, uart_dte, esp_netif);
|
||||
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for the SIM7600 module...");
|
||||
auto dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif);
|
||||
#else
|
||||
ESP_LOGI(TAG, "Initializing esp_modem for a generic module...");
|
||||
auto dce = create_generic_dce(&dce_config, uart_dte, esp_netif);
|
||||
#endif
|
||||
|
||||
|
||||
#elif defined(CONFIG_EXAMPLE_SERIAL_CONFIG_USB)
|
||||
struct esp_modem_usb_term_config usb_config = ESP_MODEM_DEFAULT_USB_CONFIG(0x2C7C, 0x0296); // VID and PID of BG96 modem
|
||||
@ -82,6 +125,14 @@ extern "C" void app_main(void)
|
||||
|
||||
assert(dce != nullptr);
|
||||
|
||||
if(dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
if (command_result::OK != dce->set_flow_control(2, 2)) {
|
||||
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "set_flow_control OK");
|
||||
}
|
||||
|
||||
// init console REPL environment
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
|
@ -61,4 +61,10 @@ menu "Example Configuration"
|
||||
help
|
||||
HTTPS address of the update binary.
|
||||
|
||||
config EXAMPLE_CLOSE_CMUX_AT_END
|
||||
bool "Close multiplexed mode at the end of the example"
|
||||
default n
|
||||
help
|
||||
Close the multiplexed mode at the end of the example and rollback to command mode.
|
||||
|
||||
endmenu
|
||||
|
@ -82,7 +82,7 @@ extern "C" void app_main(void)
|
||||
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
|
||||
bool pin_ok = true;
|
||||
if (dce->read_pin(pin_ok) == command_result::OK && !pin_ok) {
|
||||
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
||||
ESP_MODEM_THROW_IF_FALSE(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // Need to wait for some time after unlocking the SIM
|
||||
}
|
||||
#endif
|
||||
@ -164,4 +164,14 @@ extern "C" void app_main(void)
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_PERFORM_OTA
|
||||
|
||||
/* Close multiplexed command/data mode */
|
||||
#if CONFIG_EXAMPLE_CLOSE_CMUX_AT_END == 1
|
||||
if (dce->set_mode(esp_modem::modem_mode::COMMAND_MODE)) {
|
||||
std::cout << "Modem has correctly entered command mode" << std::endl;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to configure desired mode... exiting");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: "0.1.18"
|
||||
version: "0.1.24"
|
||||
description: esp modem
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
|
||||
dependencies:
|
||||
|
@ -69,12 +69,12 @@ class Creator {
|
||||
public:
|
||||
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif): dte(std::move(dte)), device(nullptr), netif(esp_netif)
|
||||
{
|
||||
throw_if_false(netif != nullptr, "Null netif");
|
||||
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
|
||||
}
|
||||
|
||||
Creator(std::shared_ptr<DTE> dte, esp_netif_t *esp_netif, std::shared_ptr<T_Module> dev): dte(std::move(dte)), device(std::move(dev)), netif(esp_netif)
|
||||
{
|
||||
throw_if_false(netif != nullptr, "Null netif");
|
||||
ESP_MODEM_THROW_IF_FALSE(netif != nullptr, "Null netif");
|
||||
}
|
||||
|
||||
~Creator()
|
||||
|
@ -75,6 +75,12 @@ public:
|
||||
*/
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
|
||||
|
||||
/**
|
||||
* @brief Sets DTE error callback
|
||||
* @param f Function to be called on DTE error
|
||||
*/
|
||||
void set_error_cb(std::function<void(terminal_error err)> f);
|
||||
|
||||
/**
|
||||
* @brief Sets the DTE to desired mode (Command/Data/Cmux)
|
||||
* @param m Desired operation mode
|
||||
|
@ -16,14 +16,21 @@
|
||||
|
||||
#include <string>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifndef __FILENAME__
|
||||
#define __FILENAME__ __FILE__
|
||||
#endif
|
||||
#define ESP_MODEM_THROW_IF_FALSE(...) esp_modem::throw_if_false(__FILENAME__, __LINE__, __VA_ARGS__)
|
||||
#define ESP_MODEM_THROW_IF_ERROR(...) esp_modem::throw_if_error(__FILENAME__, __LINE__, __VA_ARGS__)
|
||||
|
||||
namespace esp_modem {
|
||||
|
||||
#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
#define THROW(exception) throw(exception)
|
||||
#define ESP_MODEM_THROW(exception) throw(exception)
|
||||
|
||||
class esp_err_exception: virtual public std::exception {
|
||||
public:
|
||||
explicit esp_err_exception(esp_err_t err): esp_err(err) {}
|
||||
explicit esp_err_exception(std::string msg): esp_err(ESP_FAIL), message(std::move(msg)) {}
|
||||
explicit esp_err_exception(std::string msg, esp_err_t err): esp_err(err), message(std::move(msg)) {}
|
||||
virtual esp_err_t get_err_t()
|
||||
@ -31,7 +38,7 @@ public:
|
||||
return esp_err;
|
||||
}
|
||||
~esp_err_exception() noexcept override = default;
|
||||
virtual const char *what() const noexcept
|
||||
[[nodiscard]] const char *what() const noexcept override
|
||||
{
|
||||
return message.c_str();
|
||||
}
|
||||
@ -39,28 +46,43 @@ private:
|
||||
esp_err_t esp_err;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
#else
|
||||
#define THROW(exception) abort()
|
||||
#define ESP_MODEM_THROW(exception) do { exception; abort(); } while(0)
|
||||
|
||||
class esp_err_exception {
|
||||
void print(std::string msg) { ESP_LOGE("ESP_MODEM_THROW", "%s\n", msg.c_str()); }
|
||||
public:
|
||||
explicit esp_err_exception(std::string msg) { print(std::move(msg)); }
|
||||
explicit esp_err_exception(std::string msg, esp_err_t err) { print(std::move(msg)); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static inline void throw_if_false(bool condition, std::string message)
|
||||
static inline std::string make_message(const std::string& filename, int line, const std::string& message = "ERROR")
|
||||
{
|
||||
std::string text = filename + ":" + std::to_string(line) + " " + message;
|
||||
return text;
|
||||
}
|
||||
|
||||
static inline void throw_if_false(const std::string& filename, int line, bool condition, const std::string& message)
|
||||
{
|
||||
if (!condition) {
|
||||
THROW(esp_err_exception(std::move(message)));
|
||||
ESP_MODEM_THROW(esp_err_exception(make_message(filename, line, message)));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void throw_if_esp_fail(esp_err_t err, std::string message)
|
||||
static inline void throw_if_error(const std::string& filename, int line, esp_err_t err, const std::string& message)
|
||||
{
|
||||
if (err != ESP_OK) {
|
||||
THROW(esp_err_exception(std::move(message), err));
|
||||
ESP_MODEM_THROW(esp_err_exception(make_message(filename, line, message), err));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void throw_if_esp_fail(esp_err_t err)
|
||||
static inline void throw_if_error(const std::string& filename, int line, esp_err_t err)
|
||||
{
|
||||
if (err != ESP_OK) {
|
||||
THROW(esp_err_exception(err));
|
||||
ESP_MODEM_THROW(esp_err_exception(make_message(filename, line), err));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ enum class terminal_error {
|
||||
BUFFER_OVERFLOW,
|
||||
CHECKSUM_ERROR,
|
||||
UNEXPECTED_CONTROL_FLOW,
|
||||
DEVICE_GONE,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -60,6 +60,23 @@ typedef enum esp_modem_dce_device
|
||||
ESP_MODEM_DCE_SIM800,
|
||||
} esp_modem_dce_device_t;
|
||||
|
||||
/**
|
||||
* @brief Terminal errors
|
||||
*/
|
||||
typedef enum esp_modem_terminal_error
|
||||
{
|
||||
ESP_MODEM_TERMINAL_BUFFER_OVERFLOW,
|
||||
ESP_MODEM_TERMINAL_CHECKSUM_ERROR,
|
||||
ESP_MODEM_TERMINAL_UNEXPECTED_CONTROL_FLOW,
|
||||
ESP_MODEM_TERMINAL_DEVICE_GONE,
|
||||
ESP_MODEM_TERMINAL_UNKNOWN_ERROR,
|
||||
} esp_modem_terminal_error_t;
|
||||
|
||||
/**
|
||||
* @brief Terminal error callback
|
||||
*/
|
||||
typedef void (*esp_modem_terminal_error_cbt)(esp_modem_terminal_error_t);
|
||||
|
||||
/**
|
||||
* @brief Create a generic DCE handle for new modem API
|
||||
*
|
||||
@ -90,6 +107,15 @@ esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_mode
|
||||
*/
|
||||
void esp_modem_destroy(esp_modem_dce_t * dce);
|
||||
|
||||
/**
|
||||
* @brief Set DTE's error callback
|
||||
*
|
||||
* @param dce Modem DCE handle
|
||||
* @param[in] err_cb Error callback
|
||||
* @return ESP_OK on success, ESP_FAIL on failure
|
||||
*/
|
||||
esp_err_t esp_modem_set_error_cb(esp_modem_dce_t * dce, esp_modem_terminal_error_cbt err_cb);
|
||||
|
||||
/**
|
||||
* @brief Set operation mode for this DCE
|
||||
* @param dce Modem DCE handle
|
||||
|
87
components/esp_modem/include/esp_private/c_api_wrapper.hpp
Normal file
87
components/esp_modem/include/esp_private/c_api_wrapper.hpp
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file c_api_wrapper.hpp
|
||||
* @brief Collection of C API wrappers
|
||||
*
|
||||
* This file is located in include/esp_private because it is not intended for users,
|
||||
* but rather for esp_modem C extension developers.
|
||||
*
|
||||
* The C extension API must provide a 'factory function' that returns initialized pointer to esp_modem_dce_wrap.
|
||||
* Helper functions provided below, can be used for conversion between C++ enum classes and standard C enums.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "esp_modem_c_api_types.h"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
struct esp_modem_dce_wrap { // need to mimic the polymorphic dispatch as CPP uses templated dispatch
|
||||
enum class modem_wrap_dte_type { UART, VFS, USB } dte_type;
|
||||
dce_factory::ModemType modem_type;
|
||||
DCE *dce;
|
||||
std::shared_ptr<DTE> dte;
|
||||
esp_modem_dce_wrap() : dce(nullptr), dte(nullptr) {}
|
||||
};
|
||||
|
||||
inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t module)
|
||||
{
|
||||
switch (module) {
|
||||
case ESP_MODEM_DCE_SIM7600:
|
||||
return esp_modem::dce_factory::ModemType::SIM7600;
|
||||
case ESP_MODEM_DCE_SIM7070:
|
||||
return esp_modem::dce_factory::ModemType::SIM7070;
|
||||
case ESP_MODEM_DCE_SIM7000:
|
||||
return esp_modem::dce_factory::ModemType::SIM7000;
|
||||
case ESP_MODEM_DCE_BG96:
|
||||
return esp_modem::dce_factory::ModemType::BG96;
|
||||
case ESP_MODEM_DCE_SIM800:
|
||||
return esp_modem::dce_factory::ModemType::SIM800;
|
||||
default:
|
||||
case ESP_MODEM_DCE_GENETIC:
|
||||
return esp_modem::dce_factory::ModemType::GenericModule;
|
||||
}
|
||||
}
|
||||
|
||||
inline esp_modem_terminal_error_t convert_terminal_error_enum(terminal_error err)
|
||||
{
|
||||
switch (err) {
|
||||
case terminal_error::BUFFER_OVERFLOW:
|
||||
return ESP_MODEM_TERMINAL_BUFFER_OVERFLOW;
|
||||
case terminal_error::CHECKSUM_ERROR:
|
||||
return ESP_MODEM_TERMINAL_CHECKSUM_ERROR;
|
||||
case terminal_error::UNEXPECTED_CONTROL_FLOW:
|
||||
return ESP_MODEM_TERMINAL_UNEXPECTED_CONTROL_FLOW;
|
||||
case terminal_error::DEVICE_GONE:
|
||||
return ESP_MODEM_TERMINAL_DEVICE_GONE;
|
||||
default:
|
||||
return ESP_MODEM_TERMINAL_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
inline esp_err_t command_response_to_esp_err(command_result res)
|
||||
{
|
||||
switch (res) {
|
||||
case command_result::OK:
|
||||
return ESP_OK;
|
||||
case command_result::FAIL:
|
||||
return ESP_FAIL;
|
||||
case command_result::TIMEOUT:
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
@ -47,6 +47,7 @@
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(sync, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Reads the operator name
|
||||
* @param[out] operator name
|
||||
@ -72,9 +73,10 @@ ESP_MODEM_DECLARE_DCE_COMMAND(set_pin, command_result, 1, STRING_IN(p1, pin)) \
|
||||
* @brief Execute the supplied AT command
|
||||
* @param[in] at AT command
|
||||
* @param[out] out Command output string
|
||||
* @param[in] timeout AT command timeout in milliseconds
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/\
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(at, command_result, 2, STRING_IN(p1, cmd), STRING_OUT(p2, out)) \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(at, command_result, 3, STRING_IN(p1, cmd), STRING_OUT(p2, out), INT_IN(p3, timeout)) \
|
||||
\
|
||||
/**
|
||||
* @brief Checks if the SIM needs a PIN
|
||||
@ -112,7 +114,7 @@ ESP_MODEM_DECLARE_DCE_COMMAND(sms_character_set, command_result, 0) \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(send_sms, command_result, 2, STRING_IN(p1, number), STRING_IN(p2, message)) \
|
||||
\
|
||||
/**
|
||||
* @brief Resumes data mode (Switches back to th data mode, which was temporarily suspended)
|
||||
* @brief Resumes data mode (Switches back to the data mode, which was temporarily suspended)
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(resume_data_mode, command_result, 0) \
|
||||
|
@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
/**
|
||||
@ -23,7 +24,7 @@
|
||||
*/
|
||||
static inline int uart_write_bytes_compat(uart_port_t uart_num, const void* src, size_t size)
|
||||
{
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
const void *data = src;
|
||||
#else
|
||||
auto *data = reinterpret_cast<const char*>(src);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "esp_modem_c_api_types.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "exception_stub.hpp"
|
||||
#include "esp_private/c_api_wrapper.hpp"
|
||||
#include "cstring"
|
||||
|
||||
#ifndef ESP_MODEM_C_API_STR_MAX
|
||||
@ -35,44 +36,6 @@ size_t strlcpy(char *dest, const char *src, size_t len);
|
||||
// C API definitions
|
||||
using namespace esp_modem;
|
||||
|
||||
struct esp_modem_dce_wrap { // need to mimic the polymorphic dispatch as CPP uses templated dispatch
|
||||
enum class modem_wrap_dte_type { UART, } dte_type;
|
||||
dce_factory::ModemType modem_type;
|
||||
DCE *dce;
|
||||
};
|
||||
|
||||
static inline esp_err_t command_response_to_esp_err(command_result res)
|
||||
{
|
||||
switch (res) {
|
||||
case command_result::OK:
|
||||
return ESP_OK;
|
||||
case command_result::FAIL:
|
||||
return ESP_FAIL;
|
||||
case command_result::TIMEOUT:
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
static inline dce_factory::ModemType convert_modem_enum(esp_modem_dce_device_t module)
|
||||
{
|
||||
switch (module) {
|
||||
case ESP_MODEM_DCE_SIM7600:
|
||||
return esp_modem::dce_factory::ModemType::SIM7600;
|
||||
case ESP_MODEM_DCE_SIM7070:
|
||||
return esp_modem::dce_factory::ModemType::SIM7070;
|
||||
case ESP_MODEM_DCE_SIM7000:
|
||||
return esp_modem::dce_factory::ModemType::SIM7000;
|
||||
case ESP_MODEM_DCE_BG96:
|
||||
return esp_modem::dce_factory::ModemType::BG96;
|
||||
case ESP_MODEM_DCE_SIM800:
|
||||
return esp_modem::dce_factory::ModemType::SIM800;
|
||||
default:
|
||||
case ESP_MODEM_DCE_GENETIC:
|
||||
return esp_modem::dce_factory::ModemType::GenericModule;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, const esp_modem_dte_config_t *dte_config, const esp_modem_dce_config_t *dce_config, esp_netif_t *netif)
|
||||
{
|
||||
auto dce_wrap = new (std::nothrow) esp_modem_dce_wrap;
|
||||
@ -84,6 +47,7 @@ extern "C" esp_modem_dce_t *esp_modem_new_dev(esp_modem_dce_device_t module, con
|
||||
delete dce_wrap;
|
||||
return nullptr;
|
||||
}
|
||||
dce_wrap->dte = dte;
|
||||
dce_factory::Factory f(convert_modem_enum(module));
|
||||
dce_wrap->dce = f.build(dce_config, std::move(dte), netif);
|
||||
if (dce_wrap->dce == nullptr) {
|
||||
@ -108,6 +72,22 @@ extern "C" void esp_modem_destroy(esp_modem_dce_t *dce_wrap)
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_error_cb(esp_modem_dce_t * dce_wrap, esp_modem_terminal_error_cbt err_cb)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || dce_wrap->dte == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (err_cb) {
|
||||
dce_wrap->dte->set_error_cb([err_cb](terminal_error err) {
|
||||
err_cb(convert_terminal_error_enum(err));
|
||||
});
|
||||
} else {
|
||||
dce_wrap->dte->set_error_cb(nullptr);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_sync(esp_modem_dce_t *dce_wrap)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
@ -179,14 +159,14 @@ extern "C" esp_err_t esp_modem_set_pin(esp_modem_dce_t *dce_wrap, const char *pi
|
||||
return command_response_to_esp_err(dce_wrap->dce->set_pin(pin_str));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_at(esp_modem_dce_t *dce_wrap, const char *at, char *p_out)
|
||||
extern "C" esp_err_t esp_modem_at(esp_modem_dce_t *dce_wrap, const char *at, char *p_out, int timeout)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
std::string out;
|
||||
std::string at_str(at);
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->at(at_str, out));
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->at(at_str, out, timeout));
|
||||
if ((p_out != NULL) && (!out.empty())) {
|
||||
strlcpy(p_out, out.c_str(), ESP_MODEM_C_API_STR_MAX);
|
||||
}
|
||||
@ -243,15 +223,17 @@ extern "C" esp_err_t esp_modem_get_imei(esp_modem_dce_t *dce_wrap, char *p_imei)
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce_wrap, char *p_name)
|
||||
extern "C" esp_err_t esp_modem_get_operator_name(esp_modem_dce_t *dce_wrap, char *p_name, int *p_act)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr || p_name == nullptr || p_act == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
std::string name;
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->get_operator_name(name));
|
||||
int act;
|
||||
auto ret = command_response_to_esp_err(dce_wrap->dce->get_operator_name(name, act));
|
||||
if (ret == ESP_OK && !name.empty()) {
|
||||
strlcpy(p_name, name.c_str(), ESP_MODEM_C_API_STR_MAX);
|
||||
*p_act = act;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -17,15 +17,18 @@
|
||||
#include <cxx_include/esp_modem_cmux.hpp>
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_CMUX_DEFRAGMENT_PAYLOAD
|
||||
/**
|
||||
* @brief Define this to defragment partially received data of CMUX payload
|
||||
* This is useful if upper layers expect the entire payload available
|
||||
* for parsing.
|
||||
*/
|
||||
#define DEFRAGMENT_CMUX_PAYLOAD
|
||||
#endif
|
||||
|
||||
#define EA 0x01 /* Extension bit */
|
||||
#define CR 0x02 /* Command / Response */
|
||||
@ -82,7 +85,7 @@ void CMux::send_disconnect(size_t i)
|
||||
{
|
||||
if (i == 0) { // control terminal
|
||||
uint8_t frame[] = {
|
||||
SOF_MARKER, 0x3, 0xFF, 0x5, 0xC3, 0x1, 0xE7, SOF_MARKER };
|
||||
SOF_MARKER, 0x3, 0xEF, 0x5, 0xC3, 0x1, 0xF2, SOF_MARKER };
|
||||
term->write(frame, 8);
|
||||
} else { // separate virtual terminal
|
||||
uint8_t frame[] = {
|
||||
@ -142,7 +145,11 @@ void CMux::data_available(uint8_t *data, size_t len)
|
||||
read_cb[virtual_term](payload_start, total_payload_size);
|
||||
#endif
|
||||
}
|
||||
} else if (type == 0xFF && dlci == 0) { // notify the internal DISC command
|
||||
} else if ((type&FT_UIH) == FT_UIH && dlci == 0) { // notify the internal DISC command
|
||||
if (len > 0 && (data[0] & 0xE1) == 0xE1) {
|
||||
// Not a DISC, ignore (MSC frame)
|
||||
return;
|
||||
}
|
||||
Scoped<Lock> l(lock);
|
||||
sabm_ack = dlci;
|
||||
}
|
||||
@ -286,7 +293,7 @@ bool CMux::on_cmux_data(uint8_t *data, size_t actual_len)
|
||||
actual_len = term->read(data, data_to_read);
|
||||
#else
|
||||
data = buffer.get();
|
||||
actual_len = term->read(data, buffer_size);
|
||||
actual_len = term->read(data, buffer.size);
|
||||
#endif
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP("CMUX Received", data, actual_len, ESP_LOG_VERBOSE);
|
||||
@ -325,11 +332,12 @@ bool CMux::on_cmux_data(uint8_t *data, size_t actual_len)
|
||||
|
||||
bool CMux::deinit()
|
||||
{
|
||||
int timeout = 0;
|
||||
int timeout;
|
||||
sabm_ack = -1;
|
||||
// First disconnect all (2) virtual terminals
|
||||
for (size_t i = 1; i < 3; i++) {
|
||||
send_disconnect(i);
|
||||
timeout = 0;
|
||||
while (true) {
|
||||
usleep(10'000);
|
||||
Scoped<Lock> l(lock);
|
||||
@ -345,6 +353,7 @@ bool CMux::deinit()
|
||||
sabm_ack = -1;
|
||||
// Then disconnect the control terminal
|
||||
send_disconnect(0);
|
||||
timeout = 0;
|
||||
while (true) {
|
||||
usleep(10'000);
|
||||
Scoped<Lock> l(lock);
|
||||
@ -383,6 +392,9 @@ bool CMux::init()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i > 1) { // wait for each virtual terminal to settle MSC (no need for control term, DLCI=0)
|
||||
usleep(CONFIG_ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP * 1'000);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ command_result set_pdp_context(CommandableIf *t, PdpContext &pdp)
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string pdp_command = "AT+CGDCONT=" + std::to_string(pdp.context_id) +
|
||||
",\"" + pdp.protocol_type + "\",\"" + pdp.apn + "\"\r";
|
||||
return generic_command_common(t, pdp_command);
|
||||
return generic_command_common(t, pdp_command, 150000);
|
||||
}
|
||||
|
||||
command_result set_data_mode(CommandableIf *t)
|
||||
@ -389,11 +389,11 @@ command_result set_pin(CommandableIf *t, const std::string &pin)
|
||||
return generic_command_common(t, set_pin_command);
|
||||
}
|
||||
|
||||
command_result at(CommandableIf *t, const std::string &cmd, std::string &out)
|
||||
command_result at(CommandableIf *t, const std::string &cmd, std::string &out, int timeout = 500)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string at_command = cmd + "\r";
|
||||
return generic_get_string(t, at_command, out);
|
||||
return generic_get_string(t, at_command, out, timeout);
|
||||
}
|
||||
|
||||
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
|
||||
@ -458,7 +458,7 @@ command_result get_network_attachment_state(CommandableIf *t, int &state)
|
||||
command_result set_radio_state(CommandableIf *t, int state)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return generic_command_common(t, "AT+CFUN=" + std::to_string(state) + "\r");
|
||||
return generic_command_common(t, "AT+CFUN=" + std::to_string(state) + "\r", 15000);
|
||||
}
|
||||
|
||||
command_result get_radio_state(CommandableIf *t, int &state)
|
||||
|
@ -36,6 +36,7 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui
|
||||
{
|
||||
Scoped<Lock> l(internal_lock);
|
||||
command_result res = command_result::TIMEOUT;
|
||||
signal.clear(GOT_LINE);
|
||||
command_term->set_read_cb([&](uint8_t *data, size_t len) {
|
||||
if (!data) {
|
||||
data = buffer.get();
|
||||
@ -56,7 +57,7 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui
|
||||
command_term->write((uint8_t *)command.c_str(), command.length());
|
||||
auto got_lf = signal.wait(GOT_LINE, time_ms);
|
||||
if (got_lf && res == command_result::TIMEOUT) {
|
||||
throw_if_esp_fail(ESP_ERR_INVALID_STATE);
|
||||
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
buffer.consumed = 0;
|
||||
command_term->set_read_cb(nullptr);
|
||||
@ -155,6 +156,12 @@ void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
|
||||
});
|
||||
}
|
||||
|
||||
void DTE::set_error_cb(std::function<void(terminal_error err)> f)
|
||||
{
|
||||
data_term->set_error_cb(f);
|
||||
command_term->set_error_cb(f);
|
||||
}
|
||||
|
||||
int DTE::read(uint8_t **d, size_t len)
|
||||
{
|
||||
auto data_to_read = std::min(len, buffer.size);
|
||||
|
@ -14,8 +14,10 @@
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <inttypes.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include "esp_idf_version.h"
|
||||
#include "cxx_include/esp_modem_netif.hpp"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_netif_ppp.h"
|
||||
@ -28,7 +30,7 @@ void Netif::on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
{
|
||||
auto *ppp = static_cast<Netif *>(arg);
|
||||
if (event_id < NETIF_PP_PHASE_OFFSET) {
|
||||
ESP_LOGI("esp_modem_netif", "PPP state changed event %d", event_id);
|
||||
ESP_LOGI("esp_modem_netif", "PPP state changed event %" PRId32, event_id);
|
||||
// only notify the modem on state/error events, ignoring phase transitions
|
||||
ppp->signal.set(PPP_EXIT);
|
||||
}
|
||||
@ -58,7 +60,7 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
esp_netif_ppp_config_t ppp_config = { .ppp_phase_event_enabled = true, // assuming phase enabled, as earlier IDFs
|
||||
.ppp_error_event_enabled = false
|
||||
}; // don't provide cfg getters so we enable both events
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 4
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
esp_netif_ppp_get_params(esp_netif, &ppp_config);
|
||||
#endif // ESP-IDF >= v4.4
|
||||
if (!ppp_config.ppp_error_event_enabled) {
|
||||
@ -82,11 +84,11 @@ Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
|
||||
driver.base.netif = ppp_netif;
|
||||
driver.ppp = this;
|
||||
driver.base.post_attach = esp_modem_post_attach;
|
||||
throw_if_esp_fail(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, (void *) this));
|
||||
throw_if_esp_fail(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, ppp_netif));
|
||||
throw_if_esp_fail(
|
||||
ESP_MODEM_THROW_IF_ERROR(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, (void *) this));
|
||||
ESP_MODEM_THROW_IF_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, ppp_netif));
|
||||
ESP_MODEM_THROW_IF_ERROR(
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_LOST_IP, esp_netif_action_disconnected, ppp_netif));
|
||||
throw_if_esp_fail(esp_netif_attach(ppp_netif, &driver));
|
||||
ESP_MODEM_THROW_IF_ERROR(esp_netif_attach(ppp_netif, &driver));
|
||||
}
|
||||
|
||||
void Netif::start()
|
||||
|
@ -28,7 +28,7 @@ void Lock::unlock()
|
||||
Lock::Lock(): m(nullptr)
|
||||
{
|
||||
m = xSemaphoreCreateRecursiveMutex();
|
||||
throw_if_false(m != nullptr, "create signal event group failed");
|
||||
ESP_MODEM_THROW_IF_FALSE(m != nullptr, "create signal event group failed");
|
||||
}
|
||||
|
||||
Lock::~Lock()
|
||||
@ -45,7 +45,7 @@ void Lock::lock()
|
||||
SignalGroup::SignalGroup(): event_group(nullptr)
|
||||
{
|
||||
event_group = xEventGroupCreate();
|
||||
throw_if_false(event_group != nullptr, "create signal event group failed");
|
||||
ESP_MODEM_THROW_IF_FALSE(event_group != nullptr, "create signal event group failed");
|
||||
}
|
||||
|
||||
void SignalGroup::set(uint32_t bits)
|
||||
@ -86,7 +86,7 @@ Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t
|
||||
: task_handle(nullptr)
|
||||
{
|
||||
BaseType_t ret = xTaskCreate(task_function, "vfs_task", stack_size, task_param, priority, &task_handle);
|
||||
throw_if_false(ret == pdTRUE, "create vfs task failed");
|
||||
ESP_MODEM_THROW_IF_FALSE(ret == pdTRUE, "create vfs task failed");
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
|
@ -40,7 +40,7 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
|
||||
: UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = config->source_clk;
|
||||
|
||||
throw_if_esp_fail(uart_param_config(config->port_num, &uart_config), "config uart parameter failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(uart_param_config(config->port_num, &uart_config), "config uart parameter failed");
|
||||
|
||||
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
res = uart_set_pin(config->port_num, config->tx_io_num, config->rx_io_num,
|
||||
@ -49,24 +49,24 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
|
||||
res = uart_set_pin(config->port_num, config->tx_io_num, config->rx_io_num,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
}
|
||||
throw_if_esp_fail(res, "config uart gpio failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(res, "config uart gpio failed");
|
||||
/* Set flow control threshold */
|
||||
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
|
||||
} else if (config->flow_control == ESP_MODEM_FLOW_CONTROL_SW) {
|
||||
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_FIFO_LEN - 8);
|
||||
}
|
||||
throw_if_esp_fail(res, "config uart flow control failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(res, "config uart flow control failed");
|
||||
|
||||
/* Install UART driver and get event queue used inside driver */
|
||||
res = uart_driver_install(config->port_num,
|
||||
config->rx_buffer_size, config->tx_buffer_size,
|
||||
config->event_queue_size, config->event_queue_size ? event_queue : nullptr,
|
||||
0);
|
||||
throw_if_esp_fail(res, "install uart driver failed");
|
||||
throw_if_esp_fail(uart_set_rx_timeout(config->port_num, 1), "set rx timeout failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(res, "install uart driver failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(uart_set_rx_timeout(config->port_num, 1), "set rx timeout failed");
|
||||
|
||||
throw_if_esp_fail(uart_set_rx_full_threshold(config->port_num, 64), "config rx full threshold failed");
|
||||
ESP_MODEM_THROW_IF_ERROR(uart_set_rx_full_threshold(config->port_num, 64), "config rx full threshold failed");
|
||||
|
||||
/* mark UART as initialized */
|
||||
port = config->port_num;
|
||||
|
@ -33,7 +33,7 @@ struct uart_task {
|
||||
task_handle(nullptr)
|
||||
{
|
||||
BaseType_t ret = xTaskCreate(task_function, "uart_task", stack_size, task_param, priority, &task_handle);
|
||||
throw_if_false(ret == pdTRUE, "create uart event task failed");
|
||||
ESP_MODEM_THROW_IF_FALSE(ret == pdTRUE, "create uart event task failed");
|
||||
}
|
||||
|
||||
~uart_task()
|
||||
|
@ -27,7 +27,7 @@ uart_resource::uart_resource(const esp_modem_uart_term_config *config, QueueHand
|
||||
{
|
||||
ESP_LOGD(TAG, "Creating uart resource" );
|
||||
struct termios tty = {};
|
||||
throw_if_false(tcgetattr(fd, &tty) == 0, "Failed to tcgetattr()");
|
||||
ESP_MODEM_THROW_IF_FALSE(tcgetattr(fd, &tty) == 0, "Failed to tcgetattr()");
|
||||
|
||||
tty.c_cflag &= ~PARENB;
|
||||
tty.c_cflag &= ~CSTOPB;
|
||||
|
@ -91,7 +91,7 @@ bool vfs_create_socket(struct esp_modem_vfs_socket_creator *config, struct esp_m
|
||||
}
|
||||
TRY_CATCH_OR_DO(
|
||||
int fd = -1;
|
||||
esp_modem::throw_if_esp_fail(hostname_to_fd(config->host_name, config->port, &fd));
|
||||
ESP_MODEM_THROW_IF_ERROR(hostname_to_fd(config->host_name, config->port, &fd));
|
||||
|
||||
// Set the FD to non-blocking mode
|
||||
int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK;
|
||||
|
@ -48,7 +48,7 @@ bool vfs_create_uart(struct esp_modem_vfs_uart_creator *config, struct esp_modem
|
||||
}
|
||||
TRY_CATCH_OR_DO(
|
||||
int fd = open(config->dev_name, O_RDWR);
|
||||
esp_modem::throw_if_false(fd >= 0, "Cannot open the fd");
|
||||
ESP_MODEM_THROW_IF_FALSE(fd >= 0, "Cannot open the fd");
|
||||
|
||||
created_config->resource = new esp_modem_vfs_resource(&config->uart, fd);
|
||||
created_config->fd = fd;
|
||||
|
@ -17,6 +17,7 @@ int LoopbackTerm::write(uint8_t *data, size_t len)
|
||||
{
|
||||
if (inject_by) { // injection test: ignore what we write, but respond with injected data
|
||||
auto ret = std::async(&LoopbackTerm::batch_read, this);
|
||||
async_results.push_back(std::move(ret));
|
||||
return len;
|
||||
}
|
||||
if (len > 2 && (data[len - 1] == '\r' || data[len - 1] == '+') ) { // Simple AT responder
|
||||
@ -99,7 +100,7 @@ LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(f
|
||||
|
||||
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false), inject_by(0) {}
|
||||
|
||||
int LoopbackTerm::inject(uint8_t *data, size_t len, size_t injected_by)
|
||||
int LoopbackTerm::inject(uint8_t *data, size_t len, size_t injected_by, size_t delay_before, size_t delay_after)
|
||||
{
|
||||
if (data == nullptr) {
|
||||
inject_by = 0;
|
||||
@ -110,14 +111,20 @@ int LoopbackTerm::inject(uint8_t *data, size_t len, size_t injected_by)
|
||||
memcpy(&loopback_data[0], data, len);
|
||||
data_len = len;
|
||||
inject_by = injected_by;
|
||||
delay_after_inject = delay_after;
|
||||
delay_before_inject = delay_before;
|
||||
return len;
|
||||
}
|
||||
|
||||
void LoopbackTerm::batch_read()
|
||||
{
|
||||
while (data_len > 0) {
|
||||
on_read(nullptr, std::min(inject_by, data_len));
|
||||
Task::Delay(1);
|
||||
Task::Delay(delay_before_inject);
|
||||
{
|
||||
Scoped<Lock> lock(on_read_guard);
|
||||
on_read(nullptr, std::min(inject_by, data_len));
|
||||
}
|
||||
Task::Delay(delay_after_inject);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
* inject_by defines batch sizes: the read callback is called multiple times
|
||||
* with partial data of `inject_by` size
|
||||
*/
|
||||
int inject(uint8_t *data, size_t len, size_t inject_by);
|
||||
int inject(uint8_t *data, size_t len, size_t inject_by,size_t delay_before=0, size_t delay_after=1);
|
||||
|
||||
void start() override;
|
||||
void stop() override;
|
||||
@ -26,6 +26,12 @@ public:
|
||||
|
||||
int read(uint8_t *data, size_t len) override;
|
||||
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
|
||||
{
|
||||
Scoped<Lock> lock(on_read_guard);
|
||||
on_read = std::move(f);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class status_t {
|
||||
STARTED,
|
||||
@ -39,4 +45,9 @@ private:
|
||||
bool pin_ok;
|
||||
bool is_bg96;
|
||||
size_t inject_by;
|
||||
size_t delay_before_inject;
|
||||
size_t delay_after_inject;
|
||||
std::vector<std::future<void>> async_results;
|
||||
Lock on_read_guard;
|
||||
|
||||
};
|
||||
|
@ -7,6 +7,28 @@
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
TEST_CASE("DTE command races", "[esp_modem]") {
|
||||
auto term = std::make_unique<LoopbackTerm>(true);
|
||||
auto loopback = term.get();
|
||||
auto dte = std::make_shared<DTE>(std::move(term));
|
||||
CHECK(term == nullptr);
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
|
||||
esp_netif_t netif{};
|
||||
auto dce = create_BG96_dce(&dce_config, dte, &netif);
|
||||
CHECK(dce != nullptr);
|
||||
uint8_t resp[] = {'O', 'K', '\n'};
|
||||
// run many commands in succession with the timeout set exactly to the timespan of injected reply
|
||||
// (checks for potential exception, data races, recycled local variables, etc.)
|
||||
for (int i=0; i<1000; ++i) {
|
||||
loopback->inject(&resp[0], sizeof(resp), sizeof(resp), /* 1ms before injecting reply */1, 0);
|
||||
auto ret = dce->command("AT\n", [&](uint8_t *data, size_t len) {
|
||||
return command_result::OK;
|
||||
}, 1);
|
||||
// this command should either timeout or finish successfully
|
||||
CHECK((ret == command_result::TIMEOUT || ret == command_result::OK));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test polymorphic delete for custom device/dte", "[esp_modem]")
|
||||
{
|
||||
auto term = std::make_unique<LoopbackTerm>(true);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_websocket_client.h"
|
||||
#include "esp_event.h"
|
||||
#include <cJSON.h>
|
||||
|
||||
#define NO_DATA_TIMEOUT_SEC 5
|
||||
|
||||
@ -74,6 +75,19 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr);
|
||||
}
|
||||
|
||||
// If received data contains json structure it succeed to parse
|
||||
cJSON *root = cJSON_Parse(data->data_ptr);
|
||||
if (root) {
|
||||
for (int i = 0 ; i < cJSON_GetArraySize(root) ; i++) {
|
||||
cJSON *elem = cJSON_GetArrayItem(root, i);
|
||||
cJSON *id = cJSON_GetObjectItem(elem, "id");
|
||||
cJSON *name = cJSON_GetObjectItem(elem, "name");
|
||||
ESP_LOGW(TAG, "Json={'id': '%s', 'name': '%s'}", id->valuestring, name->valuestring);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset);
|
||||
|
||||
xTimerReset(shutdown_signal_timer, portMAX_DELAY);
|
||||
|
@ -6,6 +6,8 @@ import string
|
||||
from threading import Event, Thread
|
||||
import pytest
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
|
||||
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
|
||||
from pytest_embedded import Dut
|
||||
@ -81,8 +83,33 @@ def test_examples_protocol_websocket(dut):
|
||||
def test_close(dut):
|
||||
code = dut.expect(re.compile(b'WEBSOCKET: Received closed message with code=(\\d*)'))[0]
|
||||
print('Received close frame with code {}'.format(code))
|
||||
|
||||
def test_json(dut, websocket):
|
||||
json_string = """
|
||||
[
|
||||
{
|
||||
"id":"1",
|
||||
"name":"user1"
|
||||
},
|
||||
{
|
||||
"id":"2",
|
||||
"name":"user2"
|
||||
}
|
||||
]
|
||||
"""
|
||||
websocket.send_data(json_string)
|
||||
data = json.loads(json_string)
|
||||
|
||||
match = dut.expect(re.compile(b'Json=([a-zA-Z0-9]*).*')).group(0).decode()[5:]
|
||||
if match == str(data[0]):
|
||||
print('Sent message and received message are equal')
|
||||
else:
|
||||
raise ValueError('DUT received string do not match sent string, \nexpected: {}\nwith length {}\
|
||||
\nreceived: {}\nwith length {}'.format(data[0], len(data[0]), match, len(match)))
|
||||
|
||||
|
||||
def test_recv_long_msg(dut, websocket, msg_len, repeats):
|
||||
|
||||
send_msg = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(msg_len))
|
||||
|
||||
for _ in range(repeats):
|
||||
@ -121,6 +148,7 @@ def test_examples_protocol_websocket(dut):
|
||||
test_echo(dut)
|
||||
# Message length should exceed DUT's buffer size to test fragmentation, default is 1024 byte
|
||||
test_recv_long_msg(dut, ws, 2000, 3)
|
||||
test_json(dut, ws)
|
||||
test_close(dut)
|
||||
else:
|
||||
print('DUT connecting to {}'.format(uri))
|
||||
|
@ -2,7 +2,7 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
set(EXTRA_COMPONENT_DIRS "..")
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")
|
||||
|
||||
|
@ -332,10 +332,15 @@ static void query_mdns_host_with_getaddrinfo(char * host)
|
||||
|
||||
if (!getaddrinfo(host, NULL, &hints, &res)) {
|
||||
while (res) {
|
||||
ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host,
|
||||
res->ai_family == AF_INET?
|
||||
inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr):
|
||||
inet_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr));
|
||||
char *resolved_addr;
|
||||
#if CONFIG_LWIP_IPV6
|
||||
resolved_addr = res->ai_family == AF_INET ?
|
||||
inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr) :
|
||||
inet_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr);
|
||||
#else
|
||||
resolved_addr = inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr);
|
||||
#endif // CONFIG_LWIP_IPV6
|
||||
ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host, resolved_addr);
|
||||
res = res->ai_next;
|
||||
}
|
||||
}
|
||||
|
24
components/mdns/examples/pytest.ini
Normal file
24
components/mdns/examples/pytest.ini
Normal file
@ -0,0 +1,24 @@
|
||||
[pytest]
|
||||
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
|
||||
python_files = pytest_*.py
|
||||
|
||||
addopts =
|
||||
-s
|
||||
--embedded-services esp,idf
|
||||
-W ignore::_pytest.warning_types.PytestExperimentalApiWarning
|
||||
--tb short
|
||||
|
||||
# ignore DeprecationWarning
|
||||
filterwarnings =
|
||||
ignore:Call to deprecated create function (.*)\(\):DeprecationWarning
|
||||
|
||||
# log related
|
||||
log_cli = True
|
||||
log_cli_level = INFO
|
||||
log_cli_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_cli_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
log_file = test.log
|
||||
log_file_level = INFO
|
||||
log_file_format = %(asctime)s %(levelname)s %(message)s
|
||||
log_file_date_format = %Y-%m-%d %H:%M:%S
|
@ -5,19 +5,19 @@ import socket
|
||||
import struct
|
||||
import subprocess
|
||||
import time
|
||||
import pytest
|
||||
from threading import Event, Thread
|
||||
|
||||
import dpkt
|
||||
import dpkt.dns
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import DUT
|
||||
from tiny_test_fw.Utility import console_log
|
||||
from pytest_embedded import Dut
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_dns_query_for_esp(esp_host):
|
||||
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
|
||||
dns.qd[0].name = esp_host + u'.local'
|
||||
console_log('Created query for esp host: {} '.format(dns.__repr__()))
|
||||
print('Created query for esp host: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ def get_dns_answer_to_mdns(tester_host):
|
||||
arr.name = tester_host
|
||||
arr.ip = socket.inet_aton('127.0.0.1')
|
||||
dns.an.append(arr)
|
||||
console_log('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
print('Created answer to mdns query: {} '.format(dns.__repr__()))
|
||||
return dns.pack()
|
||||
|
||||
|
||||
@ -80,18 +80,18 @@ def mdns_server(esp_host, events):
|
||||
dns = dpkt.dns.DNS(data)
|
||||
if len(dns.qd) > 0 and dns.qd[0].type == dpkt.dns.DNS_A:
|
||||
if dns.qd[0].name == TESTER_NAME:
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
print('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), (MCAST_GRP, UDP_PORT))
|
||||
elif dns.qd[0].name == TESTER_NAME_LWIP:
|
||||
console_log('Received query: {} '.format(dns.__repr__()))
|
||||
print('Received query: {} '.format(dns.__repr__()))
|
||||
sock.sendto(get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), addr)
|
||||
if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A:
|
||||
console_log('Received answer from {}'.format(dns.an[0].name))
|
||||
print('Received answer from {}'.format(dns.an[0].name))
|
||||
if dns.an[0].name == esp_host + u'.local':
|
||||
console_log('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
print('Received answer to esp32-mdns query: {}'.format(dns.__repr__()))
|
||||
events['esp_answered'].set()
|
||||
if dns.an[0].name == esp_host + u'-delegated.local':
|
||||
console_log('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
|
||||
print('Received answer to esp32-mdns-delegate query: {}'.format(dns.__repr__()))
|
||||
events['esp_delegated_answered'].set()
|
||||
except socket.timeout:
|
||||
break
|
||||
@ -99,7 +99,7 @@ def mdns_server(esp_host, events):
|
||||
continue
|
||||
|
||||
|
||||
def test_examples_protocol_mdns(env, config):
|
||||
def test_examples_protocol_mdns(dut):
|
||||
"""
|
||||
steps: |
|
||||
1. obtain IP address + init mdns example
|
||||
@ -107,60 +107,31 @@ def test_examples_protocol_mdns(env, config):
|
||||
3. check the mdns name is accessible
|
||||
4. check DUT output if mdns advertized host is resolved
|
||||
"""
|
||||
dut1 = env.get_dut('mdns-test', 'examples/protocols/mdns', dut_class=ttfw_idf.ESP32DUT, app_config_name=config)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'mdns_test.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('mdns-test_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start mdns application
|
||||
dut1.start_app()
|
||||
# 2. get the dut host name (and IP address)
|
||||
specific_host = dut1.expect(re.compile(r'mdns hostname set to: \[([^\]]+)\]'), timeout=30)[0]
|
||||
|
||||
specific_host = dut.expect(re.compile(b'mdns hostname set to: \[(.*?)\]')).group(1).decode()
|
||||
|
||||
mdns_server_events = {'stop': Event(), 'esp_answered': Event(), 'esp_delegated_answered': Event()}
|
||||
mdns_responder = Thread(target=mdns_server, args=(str(specific_host), mdns_server_events))
|
||||
ip_address = dut.expect(re.compile(b'IPv4 address:([a-zA-Z0-9]*).*')).group(1).decode()
|
||||
print('Connected to AP with IP: {}'.format(ip_address))
|
||||
try:
|
||||
ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)[0]
|
||||
console_log('Connected to AP with IP: {}'.format(ip_address))
|
||||
except DUT.ExpectTimeout:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
|
||||
try:
|
||||
# 3. check the mdns name is accessible
|
||||
# 3. check the mdns name is accessible.
|
||||
mdns_responder.start()
|
||||
if not mdns_server_events['esp_answered'].wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer within timeout')
|
||||
if not mdns_server_events['esp_delegated_answered'].wait(timeout=30):
|
||||
raise ValueError('Test has failed: did not receive mdns answer for delegated host within timeout')
|
||||
# 4. check DUT output if mdns advertized host is resolved
|
||||
dut1.expect(re.compile(r'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut1.expect(re.compile(r'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut1.expect(re.compile(r'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut.expect(re.compile(b'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut.expect(re.compile(b'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
|
||||
dut.expect(re.compile(b'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1'), timeout=30)
|
||||
# 5. check the DUT answers to `dig` command
|
||||
dig_output = subprocess.check_output(['dig', '+short', '-p', '5353', '@224.0.0.251',
|
||||
'{}.local'.format(specific_host)])
|
||||
console_log('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
|
||||
print('Resolving {} using "dig" succeeded with:\n{}'.format(specific_host, dig_output))
|
||||
if not ip_address.encode('utf-8') in dig_output:
|
||||
raise ValueError('Test has failed: Incorrectly resolved DUT hostname using dig'
|
||||
"Output should've contained DUT's IP address:{}".format(ip_address))
|
||||
finally:
|
||||
mdns_server_events['stop'].set()
|
||||
mdns_responder.join()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_default(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_def')
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_socket(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_socket')
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_EthKitV1')
|
||||
def test_examples_protocol_mdns_custom_netif(env, _):
|
||||
test_examples_protocol_mdns(env, 'eth_custom_netif')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_mdns_default()
|
16
components/mdns/examples/sdkconfig.ci.eth_no_ipv6
Normal file
16
components/mdns/examples/sdkconfig.ci.eth_no_ipv6
Normal file
@ -0,0 +1,16 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_MDNS_RESOLVE_TEST_SERVICES=y
|
||||
CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y
|
||||
CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y
|
||||
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
||||
CONFIG_LWIP_IPV6=n
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_MDNS_BUTTON_GPIO=32
|
@ -1,4 +1,4 @@
|
||||
version: "1.0.5"
|
||||
version: "1.0.7"
|
||||
description: mDNS
|
||||
dependencies:
|
||||
idf:
|
||||
|
@ -232,7 +232,7 @@ static char * _mdns_mangle_name(char* in) {
|
||||
static bool _mdns_service_match(const mdns_service_t * srv, const char * service, const char * proto,
|
||||
const char * hostname)
|
||||
{
|
||||
if (!service || !proto) {
|
||||
if (!service || !proto || !srv->hostname) {
|
||||
return false;
|
||||
}
|
||||
return !strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) &&
|
||||
@ -1686,7 +1686,7 @@ static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_
|
||||
if (!packet) {
|
||||
return;
|
||||
}
|
||||
packet->flags = MDNS_FLAGS_AUTHORITATIVE;
|
||||
packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE;
|
||||
packet->distributed = parsed_packet->distributed;
|
||||
packet->id = parsed_packet->id;
|
||||
|
||||
@ -1935,7 +1935,7 @@ static mdns_tx_packet_t * _mdns_create_announce_packet(mdns_if_t tcpip_if, mdns_
|
||||
if (!packet) {
|
||||
return NULL;
|
||||
}
|
||||
packet->flags = MDNS_FLAGS_AUTHORITATIVE;
|
||||
packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE;
|
||||
|
||||
uint8_t i;
|
||||
for (i=0; i<len; i++) {
|
||||
@ -1965,7 +1965,7 @@ static mdns_tx_packet_t * _mdns_create_announce_from_probe(mdns_tx_packet_t * pr
|
||||
if (!packet) {
|
||||
return NULL;
|
||||
}
|
||||
packet->flags = MDNS_FLAGS_AUTHORITATIVE;
|
||||
packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE;
|
||||
|
||||
mdns_out_answer_t * s = probe->servers;
|
||||
while (s) {
|
||||
@ -2005,7 +2005,7 @@ static void _mdns_pcb_send_bye(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protoco
|
||||
if (!packet) {
|
||||
return;
|
||||
}
|
||||
packet->flags = MDNS_FLAGS_AUTHORITATIVE;
|
||||
packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE;
|
||||
size_t i;
|
||||
for (i=0; i<len; i++) {
|
||||
if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, true, true)) {
|
||||
@ -3342,13 +3342,13 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
|
||||
memset(name, 0, sizeof(mdns_name_t));
|
||||
|
||||
header.id = _mdns_read_u16(data, MDNS_HEAD_ID_OFFSET);
|
||||
header.flags.value = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
|
||||
header.flags = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
|
||||
header.questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET);
|
||||
header.answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET);
|
||||
header.servers = _mdns_read_u16(data, MDNS_HEAD_SERVERS_OFFSET);
|
||||
header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
|
||||
|
||||
if (header.flags.value == MDNS_FLAGS_AUTHORITATIVE && packet->src_port != MDNS_SERVICE_PORT) {
|
||||
if (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE && packet->src_port != MDNS_SERVICE_PORT) {
|
||||
free(parsed_packet);
|
||||
return;
|
||||
}
|
||||
@ -3362,8 +3362,8 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
|
||||
parsed_packet->tcpip_if = packet->tcpip_if;
|
||||
parsed_packet->ip_protocol = packet->ip_protocol;
|
||||
parsed_packet->multicast = packet->multicast;
|
||||
parsed_packet->authoritative = header.flags.value == MDNS_FLAGS_AUTHORITATIVE;
|
||||
parsed_packet->distributed = header.flags.value == MDNS_FLAGS_DISTRIBUTED;
|
||||
parsed_packet->authoritative = (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE);
|
||||
parsed_packet->distributed = header.flags == MDNS_FLAGS_DISTRIBUTED;
|
||||
parsed_packet->id = header.id;
|
||||
esp_netif_ip_addr_copy(&parsed_packet->src, &packet->src);
|
||||
parsed_packet->src_port = packet->src_port;
|
||||
@ -3499,7 +3499,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
|
||||
service = _mdns_get_service_item(name->service, name->proto, NULL);
|
||||
}
|
||||
} else {
|
||||
if (!header.flags.qr || record_type == MDNS_NS) {
|
||||
if ((header.flags & MDNS_FLAGS_QUERY_REPSONSE) == 0 || record_type == MDNS_NS) {
|
||||
//skip this record
|
||||
continue;
|
||||
}
|
||||
@ -3520,7 +3520,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
|
||||
_mdns_remove_parsed_question(parsed_packet, type, service);
|
||||
} else if (service) {
|
||||
//check if TTL is more than half of the full TTL value (4500)
|
||||
if (ttl > 2250) {
|
||||
if (ttl > (MDNS_ANSWER_PTR_TTL/2)) {
|
||||
_mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
||||
}
|
||||
}
|
||||
@ -3675,7 +3675,7 @@ void mdns_parse_packet(mdns_rx_packet_t * packet)
|
||||
if (col && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running && service) {
|
||||
do_not_reply = true;
|
||||
_mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, true);
|
||||
} else if (ttl > 2250 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
|
||||
} else if (ttl > (MDNS_ANSWER_TXT_TTL/2) && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) {
|
||||
_mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service);
|
||||
}
|
||||
}
|
||||
@ -4558,7 +4558,8 @@ static void _mdns_remap_self_service_hostname(const char * old_hostname, const c
|
||||
mdns_srv_item_t * service = _mdns_server->services;
|
||||
|
||||
while (service) {
|
||||
if (strcmp(service->service->hostname, old_hostname) == 0) {
|
||||
if (service->service->hostname &&
|
||||
strcmp(service->service->hostname, old_hostname) == 0) {
|
||||
free((char *)service->service->hostname);
|
||||
service->service->hostname = strdup(new_hostname);
|
||||
}
|
||||
@ -5430,7 +5431,7 @@ esp_err_t mdns_instance_name_set(const char * instance)
|
||||
esp_err_t mdns_service_add_for_host(const char * instance, const char * service, const char * proto, const char * hostname,
|
||||
uint16_t port, mdns_txt_item_t txt[], size_t num_items)
|
||||
{
|
||||
if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port) {
|
||||
if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || !port || !hostname) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@ -6075,11 +6076,11 @@ void mdns_debug_packet(const uint8_t * data, size_t len)
|
||||
header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
|
||||
|
||||
_mdns_dbg_printf("%s",
|
||||
(header.flags.value == MDNS_FLAGS_AUTHORITATIVE)?"AUTHORITATIVE\n":
|
||||
(header.flags.value == MDNS_FLAGS_QR_AUTHORITATIVE)?"AUTHORITATIVE\n":
|
||||
(header.flags.value == MDNS_FLAGS_DISTRIBUTED)?"DISTRIBUTED\n":
|
||||
(header.flags.value == 0)?"\n":" "
|
||||
);
|
||||
if (header.flags.value && header.flags.value != MDNS_FLAGS_AUTHORITATIVE) {
|
||||
if (header.flags.value && header.flags.value != MDNS_FLAGS_QR_AUTHORITATIVE) {
|
||||
_mdns_dbg_printf("0x%04X\n", header.flags.value);
|
||||
}
|
||||
|
||||
|
@ -339,8 +339,12 @@ size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, c
|
||||
memcpy((uint8_t *)pbt->payload, data, len);
|
||||
|
||||
ip_addr_t ip_add_copy;
|
||||
#if CONFIG_LWIP_IPV6
|
||||
ip_add_copy.type = ip->type;
|
||||
memcpy(&(ip_add_copy.u_addr),&(ip->u_addr),sizeof(ip_add_copy.u_addr));
|
||||
memcpy(&(ip_add_copy.u_addr), &(ip->u_addr), sizeof(ip_add_copy.u_addr));
|
||||
#else
|
||||
memcpy(&(ip_add_copy.addr), &(ip->u_addr), sizeof(ip_add_copy.addr));
|
||||
#endif // CONFIG_LWIP_IPV6
|
||||
|
||||
mdns_api_call_t msg = {
|
||||
.tcpip_if = tcpip_if,
|
||||
|
@ -71,7 +71,9 @@
|
||||
#define MDNS_ANSWER_A_TTL 120
|
||||
#define MDNS_ANSWER_AAAA_TTL 120
|
||||
|
||||
#define MDNS_FLAGS_AUTHORITATIVE 0x8400
|
||||
#define MDNS_FLAGS_QUERY_REPSONSE 0x8000
|
||||
#define MDNS_FLAGS_AUTHORITATIVE 0x0400
|
||||
#define MDNS_FLAGS_QR_AUTHORITATIVE (MDNS_FLAGS_QUERY_REPSONSE | MDNS_FLAGS_AUTHORITATIVE)
|
||||
#define MDNS_FLAGS_DISTRIBUTED 0x0200
|
||||
|
||||
#define MDNS_NAME_REF 0xC000
|
||||
@ -211,21 +213,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
uint16_t id;
|
||||
union {
|
||||
struct {
|
||||
uint16_t qr :1;
|
||||
uint16_t opCode :4;
|
||||
uint16_t aa :1;
|
||||
uint16_t tc :1;
|
||||
uint16_t rd :1;
|
||||
uint16_t ra :1;
|
||||
uint16_t z :1;
|
||||
uint16_t ad :1;
|
||||
uint16_t cd :1;
|
||||
uint16_t rCode :4;//response/error code
|
||||
};
|
||||
uint16_t value;
|
||||
} flags;
|
||||
uint16_t flags;
|
||||
uint16_t questions; //QDCOUNT
|
||||
uint16_t answers; //ANCOUNT
|
||||
uint16_t servers; //NSCOUNT
|
||||
|
@ -1,2 +1,3 @@
|
||||
idf_component_register(SRCS "main.c" "mdns_test.c"
|
||||
INCLUDE_DIRS ".")
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
6
examples/esp_netif/slip_custom_netif/CMakeLists.txt
Normal file
6
examples/esp_netif/slip_custom_netif/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(slip_client)
|
88
examples/esp_netif/slip_custom_netif/README.md
Normal file
88
examples/esp_netif/slip_custom_netif/README.md
Normal file
@ -0,0 +1,88 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# SLIP device client
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This provides SLIP support for connection to Contiki gateway devices, allowing the ESP platform board to be used to bridge between low-power networks and IP (Wifi / Ethernet).
|
||||
|
||||
This example also demonstrates creating custom network interfaces, including UART drivers and lwIP netif layers, and attaching them to the standard `esp_netif` component, so the generic system interfaces can still use the common approach of listing all interfaces, updating states, posting events and handling routing priorities. Please refer to the implementation of [slip_modem](components/slip_modem) component for more details.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC).
|
||||
For test purpose, you also need a SLIP capable gateway device, such as anything running [Contiki](https://github.com/contiki-os/contiki) gateway firmware.
|
||||
You can also try other modules as long as they implement the SLIP protocol (e.g. linux device with slip module loaded)
|
||||
|
||||
#### Setup a test SLIP device
|
||||
|
||||
It is possible to configure any device with linux and a serial interface
|
||||
(e.g. raspberry PI or a PC with USB to serial bridge) to enable SLIP interface.
|
||||
|
||||
To test this example with such device, please follow these steps:
|
||||
|
||||
- Configure IPv4 mode in the example configuration menu
|
||||
|
||||
- Setup SLIP interface
|
||||
```
|
||||
slattach -v -L -s 115200 -p slip /dev/ttyAMA0
|
||||
```
|
||||
where the `/dev/ttyAMA0` is the device's serial port
|
||||
|
||||
- Configure IP addresses
|
||||
```
|
||||
ifconfig sl0 10.0.0.1 dstaddr 10.0.0.2
|
||||
```
|
||||
where the `10.0.0.2` is IPv4 address of the ESP platform board
|
||||
|
||||
- Send and receive back UDP packets, as the example implements UDP echo server
|
||||
```
|
||||
nc -u 10.0.0.2 5678
|
||||
```
|
||||
|
||||
#### Pin Assignment
|
||||
|
||||
**Note:** The following pin assignments are used by default which can be changed in menuconfig.
|
||||
|
||||
| ESP32 | Gateway |
|
||||
| ------ | -------------- |
|
||||
| GPIO25 | RX |
|
||||
| GPIO26 | TX |
|
||||
| GND | GND |
|
||||
| 3v3 | VCC |
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
|
||||
|
||||
- Choose the RX and TX pins
|
||||
- Choose port number and IP protocol for socket udp server
|
||||
For use in external projects `SLIP support` must be enabled under the `components/lwip` menu.
|
||||
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build and flash the project..
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. Invalid slip packets
|
||||
|
||||
Many slip devices use additional messages for things like ipv6 prefix configuration (or sending log messages over the SLIP serial port). This is supported in the driver through the use of an `rx_filter` function that is called on receipt of all packets and can be used to filter packets prior to passing them to the stack.
|
||||
|
||||
2. No packets received
|
||||
|
||||
The first layer to check is the serial port, you can enable debugging of the SLIP component by setting the global log level to `DEBUG`, or changing the slip component log levbel with `esp_log_level_set("esp-netif_lwip-slip", ESP_LOG_DEBUG);`
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
@ -0,0 +1,8 @@
|
||||
# SLIP Modem Component
|
||||
|
||||
idf_component_register(
|
||||
SRCS "library/slip_modem.c" "library/slip_modem_netif.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_netif driver
|
||||
)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
/** @brief Configuration of SLIP network interface
|
||||
*
|
||||
*/
|
||||
#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \
|
||||
{ \
|
||||
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \
|
||||
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \
|
||||
.get_ip_event = 0, \
|
||||
.lost_ip_event = 0, \
|
||||
.if_key = "SLP_DEF", \
|
||||
.if_desc = "slip", \
|
||||
.route_prio = 16, \
|
||||
.bridge_info = NULL \
|
||||
};
|
||||
|
||||
extern esp_netif_netstack_config_t *netstack_default_slip;
|
||||
|
||||
typedef struct slip_modem* slip_modem_handle;
|
||||
|
||||
// Filter callbacks for handling application specific slip messages
|
||||
typedef bool slip_rx_filter_cb_t(slip_modem_handle slip, uint8_t *data, uint32_t len);
|
||||
|
||||
|
||||
/** @brief Configuration structure for SLIP modem interface
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uart_port_t uart_dev; /* UART device for reading and writing SLIP information, this must be initialised externally */
|
||||
|
||||
int uart_tx_pin; /* UART TX pin number */
|
||||
int uart_rx_pin; /* UART TX pin number */
|
||||
|
||||
uint32_t uart_baud; /* UART baud rate */
|
||||
|
||||
uint32_t rx_buffer_len; /* Length of buffer for RX messages */
|
||||
|
||||
slip_rx_filter_cb_t *rx_filter; /* Filter for parsing out non-SLIP messages from incoming SLIP stream */
|
||||
esp_ip6_addr_t *ipv6_addr;
|
||||
|
||||
} slip_modem_config_t;
|
||||
|
||||
|
||||
|
||||
/** @brief Create a slip modem
|
||||
*
|
||||
* @param[in] slip configured esp netif
|
||||
* @param[in] configuration for the slip modem
|
||||
*
|
||||
* @returns
|
||||
* - slip modem driver glue object
|
||||
*/
|
||||
slip_modem_handle slip_modem_create(esp_netif_t *slip_netif, const slip_modem_config_t *modem_config);
|
||||
|
||||
/** @brief Destroy a slip modem
|
||||
*
|
||||
* @param[in] slip modem handle for destruction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t slip_modem_destroy(slip_modem_handle slip);
|
||||
|
||||
/**
|
||||
* @brief Getter for the internally configured IPv6 address
|
||||
*
|
||||
* @param[in] slip modem handle
|
||||
*
|
||||
* @returns
|
||||
* - ipv6 address
|
||||
*/
|
||||
esp_ip6_addr_t slip_modem_get_ipv6_address(slip_modem_handle slip);
|
||||
|
||||
/**
|
||||
* @brief Data path API that forward the supplied data to the attached network interface
|
||||
*
|
||||
* @param[in] slip modem object
|
||||
* @param[in] buffer pointer to the outgoing data
|
||||
* @param[in] len length of the data
|
||||
*
|
||||
*/
|
||||
void slip_modem_raw_write(slip_modem_handle slip, void *buffer, size_t len);
|
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "slip_modem.h"
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "slip_modem_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define SLIP_RX_TASK_PRIORITY 10
|
||||
#define SLIP_RX_TASK_STACK_SIZE (4 * 1024)
|
||||
|
||||
static const char *TAG = "slip-modem";
|
||||
|
||||
|
||||
// UART container object
|
||||
typedef struct {
|
||||
// UART device number for SIO use
|
||||
uart_port_t uart_dev;
|
||||
|
||||
// UART baud rate for configuration
|
||||
uint32_t uart_baud;
|
||||
|
||||
// UART TX pin for configuration
|
||||
int uart_tx_pin;
|
||||
|
||||
// UART RX pin for configuration
|
||||
int uart_rx_pin;
|
||||
|
||||
// QueueHandle for uart driver
|
||||
QueueHandle_t uart_queue;
|
||||
|
||||
// TaskHandle for receive task
|
||||
TaskHandle_t uart_rx_task;
|
||||
} esp_slip_uart_t;
|
||||
|
||||
|
||||
// Modem object, implements glue logic for slip_driver and esp_netif
|
||||
struct slip_modem {
|
||||
// ESP base netif driver
|
||||
esp_netif_driver_base_t base;
|
||||
|
||||
// Uart for use with slip
|
||||
esp_slip_uart_t uart;
|
||||
|
||||
// Buffer for incoming messages
|
||||
uint8_t *buffer;
|
||||
uint32_t buffer_len;
|
||||
|
||||
// Filter callbacks for application-specific slip message handling
|
||||
slip_rx_filter_cb_t *rx_filter;
|
||||
|
||||
// Running flag
|
||||
bool running;
|
||||
|
||||
// esp_netif related: SLIP interface IP6 address
|
||||
esp_ip6_addr_t addr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
static void slip_modem_uart_rx_task(void *arg);
|
||||
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args);
|
||||
|
||||
// Create a new slip modem
|
||||
slip_modem_handle slip_modem_create(esp_netif_t *slip_netif, const slip_modem_config_t *modem_config)
|
||||
{
|
||||
if (slip_netif == NULL || modem_config == NULL) {
|
||||
ESP_LOGE(TAG, "invalid parameters");
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: Creating slip modem (netif: %p)", __func__, slip_netif);
|
||||
|
||||
ESP_LOGD(TAG, "%s (netif: %p)", __func__, slip_netif);
|
||||
|
||||
slip_modem_handle slip_modem = calloc(1, sizeof(struct slip_modem));
|
||||
if (!slip_modem) {
|
||||
ESP_LOGE(TAG, "create netif glue failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Attach driver and post_attach callbacks
|
||||
slip_modem->base.post_attach = slip_modem_post_attach;
|
||||
slip_modem->base.netif = slip_netif;
|
||||
|
||||
// Attach config
|
||||
slip_modem->buffer_len = modem_config->rx_buffer_len;
|
||||
|
||||
slip_modem->rx_filter = modem_config->rx_filter;
|
||||
|
||||
slip_modem->uart.uart_dev = modem_config->uart_dev;
|
||||
slip_modem->uart.uart_baud = modem_config->uart_baud;
|
||||
slip_modem->uart.uart_rx_pin = modem_config->uart_rx_pin;
|
||||
slip_modem->uart.uart_tx_pin = modem_config->uart_tx_pin;
|
||||
slip_modem->addr = *modem_config->ipv6_addr;
|
||||
|
||||
// Return the new modem
|
||||
return slip_modem;
|
||||
}
|
||||
|
||||
// Internal handler called on driver start
|
||||
static esp_err_t esp_slip_driver_start(slip_modem_handle slip_modem)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s: Starting SLIP modem (modem %p)", __func__, slip_modem);
|
||||
|
||||
// Allocate RX buffer if one does not exist
|
||||
if (slip_modem->buffer == NULL) {
|
||||
slip_modem->buffer = malloc(slip_modem->buffer_len);
|
||||
}
|
||||
if (slip_modem->buffer == NULL) {
|
||||
ESP_LOGE(TAG, "error allocating rx buffer");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Build configuration
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = slip_modem->uart.uart_baud,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
};
|
||||
|
||||
// Initialise uart
|
||||
ESP_ERROR_CHECK(uart_param_config(slip_modem->uart.uart_dev, &uart_config));
|
||||
|
||||
// Set UART pins
|
||||
ESP_ERROR_CHECK(uart_set_pin(slip_modem->uart.uart_dev, slip_modem->uart.uart_tx_pin, slip_modem->uart.uart_rx_pin, 0, 0));
|
||||
|
||||
// Install UART driver
|
||||
ESP_ERROR_CHECK(uart_driver_install(slip_modem->uart.uart_dev, slip_modem->buffer_len, slip_modem->buffer_len, 10, &slip_modem->uart.uart_queue, 0));
|
||||
|
||||
// Start slip RX task
|
||||
slip_modem->running = true;
|
||||
xTaskCreate(slip_modem_uart_rx_task, "slip_modem_uart_rx_task", SLIP_RX_TASK_STACK_SIZE, slip_modem, SLIP_RX_TASK_PRIORITY, &slip_modem->uart.uart_rx_task);
|
||||
|
||||
// Finally, initialise slip network interface
|
||||
esp_netif_action_start(slip_modem->base.netif, 0, 0, 0);
|
||||
ESP_ERROR_CHECK(slip_modem_netif_start(slip_modem->base.netif, &slip_modem->addr));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t slip_modem_destroy(slip_modem_handle slip)
|
||||
{
|
||||
if (slip != NULL) {
|
||||
// Stop slip driver
|
||||
esp_netif_action_stop(slip->base.netif, 0, 0, 0);
|
||||
ESP_ERROR_CHECK(slip_modem_netif_stop(slip->base.netif));
|
||||
|
||||
// Stop uart rx task
|
||||
vTaskDelete(slip->uart.uart_rx_task);
|
||||
|
||||
// Delete driver
|
||||
uart_driver_delete(slip->uart.uart_dev);
|
||||
|
||||
// Free slip interface
|
||||
free(slip);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modem transmit for glue logic
|
||||
static esp_err_t slip_modem_transmit(void *slip_driver, void *buffer, size_t len)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
|
||||
slip_modem_handle slip_modem = (slip_modem_handle)slip_driver;
|
||||
|
||||
int32_t res = uart_write_bytes(slip_modem->uart.uart_dev, (char *)buffer, len);
|
||||
if (res < 0) {
|
||||
// Handle errors
|
||||
ESP_LOGE(TAG, "%s: uart_write_bytes error %i", __func__, res);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Post-attach handler for netif
|
||||
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
slip_modem_handle slip_modem = (slip_modem_handle) args;
|
||||
|
||||
ESP_LOGD(TAG, "%s (netif: %p args: %p)", __func__, esp_netif, args);
|
||||
|
||||
const esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.driver_free_rx_buffer = NULL,
|
||||
.transmit = slip_modem_transmit,
|
||||
.handle = slip_modem,
|
||||
};
|
||||
|
||||
slip_modem->base.netif = esp_netif;
|
||||
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
|
||||
|
||||
esp_slip_driver_start(slip_modem);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void slip_modem_uart_rx_task(void *arg)
|
||||
{
|
||||
if (arg == NULL) {
|
||||
ESP_LOGE(TAG, "Starting a task with invalid parameters, deleting");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
slip_modem_handle slip_modem = (slip_modem_handle) arg;
|
||||
|
||||
ESP_LOGD(TAG, "Start SLIP modem RX task (slip_modem %p filter: %p)", slip_modem, slip_modem->rx_filter);
|
||||
ESP_LOGD(TAG, "Uart: %d, buffer: %p (%d bytes)", slip_modem->uart.uart_dev, slip_modem->buffer, slip_modem->buffer_len);
|
||||
|
||||
while (slip_modem->running == true) {
|
||||
// Read data from the UART
|
||||
int len = uart_read_bytes(slip_modem->uart.uart_dev, slip_modem->buffer, slip_modem->buffer_len, 1 / portTICK_PERIOD_MS);
|
||||
|
||||
if (len > 0) {
|
||||
|
||||
// Log slip RX data
|
||||
ESP_LOGD(TAG, "rx %d bytes", len);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, slip_modem->buffer, len, ESP_LOG_DEBUG);
|
||||
|
||||
// Ensure null termination
|
||||
slip_modem->buffer[len] = '\0';
|
||||
|
||||
// Filter if provided
|
||||
if ((slip_modem->rx_filter != NULL) && slip_modem->rx_filter(slip_modem, slip_modem->buffer, len)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pass received bytes in to slip interface
|
||||
ESP_LOGI(TAG, "esp_netif %p", slip_modem->base.netif);
|
||||
esp_netif_receive(slip_modem->base.netif, slip_modem->buffer, len, NULL);
|
||||
}
|
||||
|
||||
// Yield to allow other tasks to progress
|
||||
vTaskDelay(1 * portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the internally configured ipv6 address
|
||||
*/
|
||||
esp_ip6_addr_t slip_modem_get_ipv6_address(slip_modem_handle slip)
|
||||
{
|
||||
return slip->addr;
|
||||
}
|
||||
|
||||
void slip_modem_raw_write(slip_modem_handle slip, void *buffer, size_t len)
|
||||
{
|
||||
slip_modem_netif_raw_write(slip->base.netif, buffer, len);
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "lwip/esp_netif_net_stack.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "netif/slipif.h"
|
||||
#include "lwip/sio.h"
|
||||
|
||||
static const char *TAG = "slip-modem-netif";
|
||||
|
||||
/**
|
||||
* @brief Stops the SLIP interface
|
||||
*/
|
||||
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif)
|
||||
{
|
||||
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
|
||||
|
||||
ESP_LOGI(TAG, "%s: Stopped SLIP connection: lwip netif:%p", __func__, netif);
|
||||
|
||||
// Stop interface
|
||||
netif_set_link_down(netif);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the SLIP interface
|
||||
*/
|
||||
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr)
|
||||
{
|
||||
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
|
||||
|
||||
ESP_LOGI(TAG, "%s: Starting SLIP interface: lwip netif:%p", __func__, netif);
|
||||
|
||||
// Set the netif up
|
||||
netif_set_up(netif);
|
||||
netif_set_link_up(netif);
|
||||
#if CONFIG_LWIP_IPV6
|
||||
int8_t addr_index = 0;
|
||||
|
||||
netif_ip6_addr_set(netif, addr_index, (ip6_addr_t *)addr);
|
||||
netif_ip6_addr_set_state(netif, addr_index, IP6_ADDR_VALID);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write incoming serial data to the SLIP interface
|
||||
*/
|
||||
void esp_netif_lwip_slip_input(void *h, void *buffer, unsigned int len, void *eb)
|
||||
{
|
||||
struct netif *netif = h;
|
||||
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
|
||||
|
||||
// Update slip netif with data
|
||||
const int max_batch = 255;
|
||||
int sent = 0;
|
||||
while(sent < len) {
|
||||
int batch = (len - sent) > max_batch ? max_batch : (len - sent);
|
||||
slipif_received_bytes(netif, buffer+sent, batch);
|
||||
sent += batch;
|
||||
}
|
||||
|
||||
// Process incoming bytes
|
||||
for (int i = 0; i < len; i++) {
|
||||
slipif_process_rxqueue(netif);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write raw data out the SLIP interface
|
||||
*/
|
||||
void slip_modem_netif_raw_write(esp_netif_t *netif, void *buffer, size_t len)
|
||||
{
|
||||
struct netif *lwip_netif = esp_netif_get_netif_impl(netif);
|
||||
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
|
||||
struct pbuf p = {
|
||||
.next = NULL,
|
||||
.payload = buffer,
|
||||
.tot_len = len,
|
||||
.len = len,
|
||||
};
|
||||
|
||||
// Call slip if output function to feed data out slip interface
|
||||
#if CONFIG_LWIP_IPV6
|
||||
lwip_netif->output_ip6(lwip_netif, &p, NULL);
|
||||
#else
|
||||
lwip_netif->output(lwip_netif, &p, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/** @brief Get esp-netif object corresponding to registration index
|
||||
*/
|
||||
static esp_netif_t * get_netif_with_esp_index(int index)
|
||||
{
|
||||
esp_netif_t *netif = NULL;
|
||||
int counter = 0;
|
||||
while ((netif = esp_netif_next(netif)) != NULL) {
|
||||
if (counter == index) {
|
||||
return netif;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @brief Return list registration index of the supplied netif ptr
|
||||
*/
|
||||
static int get_esp_netif_index(esp_netif_t * esp_netif)
|
||||
{
|
||||
esp_netif_t *netif = NULL;
|
||||
int counter = 0;
|
||||
while ((netif = esp_netif_next(netif)) != NULL) {
|
||||
if (esp_netif == netif) {
|
||||
return counter;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static err_t esp_slipif_init(struct netif *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return ERR_IF;
|
||||
}
|
||||
esp_netif_t *esp_netif = netif->state;
|
||||
int esp_index = get_esp_netif_index(esp_netif);
|
||||
if (esp_index < 0) {
|
||||
return ERR_IF;
|
||||
}
|
||||
|
||||
// Store netif index in net interface for SIO open command to abstract the dev
|
||||
netif->state = (void *)esp_index;
|
||||
|
||||
return slipif_init(netif);
|
||||
}
|
||||
|
||||
const struct esp_netif_netstack_config s_netif_config_slip = {
|
||||
.lwip = {
|
||||
.init_fn = esp_slipif_init,
|
||||
.input_fn = esp_netif_lwip_slip_input,
|
||||
}
|
||||
};
|
||||
|
||||
const esp_netif_netstack_config_t *netstack_default_slip = &s_netif_config_slip;
|
||||
|
||||
|
||||
/***
|
||||
* @brief Open a serial device for communication
|
||||
*/
|
||||
sio_fd_t sio_open(uint8_t devnum)
|
||||
{
|
||||
ESP_LOGD(TAG, "Opening device: %d\r\n", devnum);
|
||||
|
||||
esp_netif_t *esp_netif = get_netif_with_esp_index(devnum);
|
||||
if (!esp_netif) {
|
||||
ESP_LOGE(TAG, "didn't find esp-netif with index=%d\n", devnum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return SIO handle
|
||||
return esp_netif;
|
||||
}
|
||||
|
||||
/***
|
||||
* @brief Send a single character to the serial device (blocking)
|
||||
*/
|
||||
void sio_send(uint8_t c, sio_fd_t fd)
|
||||
{
|
||||
esp_netif_t *esp_netif = fd;
|
||||
|
||||
ESP_LOGD(TAG, "%s", __func__);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &c, 1, ESP_LOG_DEBUG);
|
||||
|
||||
esp_err_t ret = esp_netif_transmit(esp_netif, &c, 1);
|
||||
if (ret != ESP_OK) {
|
||||
// Handle errors
|
||||
ESP_LOGD(TAG, "%s: uart_write_bytes error %i", __func__, ret);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Stop the esp slip netif
|
||||
*
|
||||
* @param[in] esp_netif handle to slip esp-netif instance
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif);
|
||||
|
||||
/**
|
||||
* @brief Start the esp slip netif
|
||||
*
|
||||
* @param[in] esp_netif handle to slip esp-netif instance
|
||||
* @param[in] addr IPv6 address associated with this SLIP interface
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Data path API to write raw packet ous the SLIP interface
|
||||
*
|
||||
* This API is typically used when implementing user defined methods
|
||||
*
|
||||
* @param[in] esp_netif handle to slip esp-netif instance
|
||||
* @param[in] buffer pointer to the outgoing data
|
||||
* @param[in] len length of the data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
void slip_modem_netif_raw_write(esp_netif_t *netif, void *buffer, size_t len);
|
8
examples/esp_netif/slip_custom_netif/main/CMakeLists.txt
Normal file
8
examples/esp_netif/slip_custom_netif/main/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# Slip client example
|
||||
|
||||
idf_component_register(
|
||||
SRCS "slip_client_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_netif slip_modem driver
|
||||
)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
38
examples/esp_netif/slip_custom_netif/main/Kconfig.projbuild
Normal file
38
examples/esp_netif/slip_custom_netif/main/Kconfig.projbuild
Normal file
@ -0,0 +1,38 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
menu "UART Configuration"
|
||||
config EXAMPLE_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
default 25
|
||||
range 0 36
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config EXAMPLE_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
default 26
|
||||
range 0 36
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_UART_BAUD
|
||||
int "UART baud rate"
|
||||
default 115200
|
||||
help
|
||||
Baud rate for UART communication
|
||||
|
||||
endmenu
|
||||
|
||||
config EXAMPLE_UDP_PORT
|
||||
int "Port for UDP echo server"
|
||||
default 5678
|
||||
help
|
||||
Port for UDP echo server in example
|
||||
|
||||
config EXAMPLE_IPV4
|
||||
bool "Test with IPv4 address"
|
||||
default n
|
||||
help
|
||||
Test interface using IPv4
|
||||
|
||||
endmenu
|
237
examples/esp_netif/slip_custom_netif/main/slip_client_main.c
Normal file
237
examples/esp_netif/slip_custom_netif/main/slip_client_main.c
Normal file
@ -0,0 +1,237 @@
|
||||
/* SLIP Client Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "slip_modem.h"
|
||||
|
||||
static const char *TAG = "slip-example";
|
||||
|
||||
#define STACK_SIZE (10 * 1024)
|
||||
#define PRIORITY 10
|
||||
|
||||
static void udp_rx_tx_task(void *arg)
|
||||
{
|
||||
char addr_str[128];
|
||||
uint8_t rx_buff[1024];
|
||||
|
||||
int sock = (int)arg;
|
||||
|
||||
struct sockaddr_storage source_addr;
|
||||
socklen_t socklen = sizeof(source_addr);
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Starting node manager UDP task");
|
||||
|
||||
while (1) {
|
||||
// Receive data
|
||||
int len = recvfrom(sock, rx_buff, sizeof(rx_buff) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse out address to string
|
||||
if (source_addr.ss_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
|
||||
} else if (source_addr.ss_family == PF_INET6) {
|
||||
inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
}
|
||||
|
||||
// Force null termination of received data and print
|
||||
rx_buff[len] = 0;
|
||||
ESP_LOGI(TAG, "Received '%s' from '%s'", rx_buff, addr_str);
|
||||
|
||||
// Send data back
|
||||
int err = sendto(sock, rx_buff, len, 0, (struct sockaddr *)&source_addr, socklen);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "sendto failed: errno %d", errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static esp_err_t udp_rx_tx_start(void)
|
||||
{
|
||||
// Setup bind address
|
||||
struct sockaddr_in6 dest_addr;
|
||||
#if CONFIG_EXAMPLE_IPV4
|
||||
sa_family_t family = AF_INET;
|
||||
int ip_protocol = IPPROTO_IP;
|
||||
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
|
||||
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
dest_addr_ip4->sin_family = AF_INET;
|
||||
dest_addr_ip4->sin_port = htons(CONFIG_EXAMPLE_UDP_PORT);
|
||||
ip_protocol = IPPROTO_IP;
|
||||
#else
|
||||
sa_family_t family = AF_INET6;
|
||||
int ip_protocol = IPPROTO_IPV6;
|
||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
||||
dest_addr.sin6_family = family;
|
||||
dest_addr.sin6_port = htons(CONFIG_EXAMPLE_UDP_PORT);
|
||||
#endif
|
||||
|
||||
// Create socket
|
||||
int sock = socket(family, SOCK_DGRAM, ip_protocol);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Disable IPv4 and reuse address
|
||||
int opt = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
#if !CONFIG_EXAMPLE_IPV4
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
|
||||
#endif
|
||||
|
||||
// Bind socket
|
||||
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", CONFIG_EXAMPLE_UDP_PORT);
|
||||
|
||||
|
||||
// Start UDP rx thread
|
||||
xTaskCreate(udp_rx_tx_task, "udp_rx_tx", STACK_SIZE, (void *)sock, PRIORITY, NULL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Write a prefix to the contiki slip device
|
||||
static void slip_set_prefix(slip_modem_handle slip)
|
||||
{
|
||||
uint8_t buff[10] = {0};
|
||||
const esp_ip6_addr_t addr = slip_modem_get_ipv6_address(slip);
|
||||
ESP_LOGI(TAG, "%s: prefix set (%08x:%08x)", __func__,
|
||||
lwip_ntohl(addr.addr[0]), lwip_ntohl(addr.addr[1]));
|
||||
|
||||
// Build slip set message
|
||||
buff[0] = '!';
|
||||
buff[1] = 'P';
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
buff[2 + i * 4 + j] = addr.addr[i] >> (j * 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Write raw data out the slip interface
|
||||
slip_modem_raw_write(slip, buff, 2 + 8);
|
||||
}
|
||||
|
||||
// slip_rx_filter filters incoming commands from the slip interface
|
||||
// this implementation is designed for use with contiki slip devices
|
||||
static bool slip_rx_filter(slip_modem_handle slip, uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (data[1] == '?') {
|
||||
switch (data[2]) {
|
||||
case 'P':
|
||||
ESP_LOGI(TAG, "Prefix request");
|
||||
slip_set_prefix(slip);
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
ESP_LOGI(TAG, "Unhandled request '%c'", data[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if (data[1] == '!') {
|
||||
switch (data[2]) {
|
||||
default:
|
||||
ESP_LOGI(TAG, "Unhandled command '%c'", data[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_IPV4
|
||||
static const esp_netif_ip_info_t s_slip_ip4 = {
|
||||
.ip = { .addr = ESP_IP4TOADDR( 10, 0, 0, 2) },
|
||||
};
|
||||
#endif
|
||||
|
||||
// Initialise the SLIP interface
|
||||
esp_netif_t *slip_if_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initialising SLIP interface");
|
||||
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_SLIP()
|
||||
#if CONFIG_EXAMPLE_IPV4
|
||||
base_cfg.ip_info = &s_slip_ip4;
|
||||
#endif
|
||||
esp_netif_config_t cfg = { .base = &base_cfg,
|
||||
.driver = NULL,
|
||||
.stack = netstack_default_slip };
|
||||
|
||||
esp_netif_t *slip_netif = esp_netif_new(&cfg);
|
||||
|
||||
esp_ip6_addr_t local_addr; /* Local IP6 address */
|
||||
IP6_ADDR(&local_addr,
|
||||
lwip_htonl(0xfd0000),
|
||||
lwip_htonl(0x00000000),
|
||||
lwip_htonl(0x00000000),
|
||||
lwip_htonl(0x00000001)
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "Initialising SLIP modem");
|
||||
|
||||
slip_modem_config_t modem_cfg = {
|
||||
.uart_dev = UART_NUM_1,
|
||||
|
||||
.uart_tx_pin = CONFIG_EXAMPLE_UART_TX_PIN,
|
||||
.uart_rx_pin = CONFIG_EXAMPLE_UART_RX_PIN,
|
||||
.uart_baud = CONFIG_EXAMPLE_UART_BAUD,
|
||||
|
||||
.rx_buffer_len = 1024,
|
||||
.rx_filter = slip_rx_filter,
|
||||
.ipv6_addr = &local_addr
|
||||
};
|
||||
|
||||
void *slip_modem = slip_modem_create(slip_netif, &modem_cfg);
|
||||
assert(slip_modem);
|
||||
ESP_ERROR_CHECK(esp_netif_attach(slip_netif, slip_modem));
|
||||
|
||||
ESP_LOGI(TAG, "SLIP init complete");
|
||||
|
||||
return slip_netif;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Setup networking
|
||||
esp_netif_init();
|
||||
|
||||
esp_log_level_set("*", ESP_LOG_DEBUG);
|
||||
|
||||
// Create event loop
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
// Setup slip interface
|
||||
esp_netif_t* esp_netif = slip_if_init();
|
||||
assert(esp_netif);
|
||||
|
||||
// Start the UDP user application
|
||||
udp_rx_tx_start();
|
||||
}
|
2
examples/esp_netif/slip_custom_netif/sdkconfig.defaults
Normal file
2
examples/esp_netif/slip_custom_netif/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
# Override some defaults to enable SLIP
|
||||
CONFIG_LWIP_SLIP_SUPPORT=y
|
Reference in New Issue
Block a user