mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-28 01:37:29 +02:00
Compare commits
34 Commits
mdns-v1.2.
...
mdns-v1.2.
Author | SHA1 | Date | |
---|---|---|---|
13b29c2291 | |||
0cb1156480 | |||
6c7259fa7a | |||
24f7031012 | |||
62d809ac60 | |||
b992ef6914 | |||
9cc594afaa | |||
4cf9e505e1 | |||
c8c05075fb | |||
cb6e03ac62 | |||
2e42b9bb49 | |||
86f7a8bbe3 | |||
7b5a41deea | |||
38d50eede0 | |||
27303d28b2 | |||
d273e10819 | |||
60c87ddf26 | |||
a95891e211 | |||
411dced3e2 | |||
d4925f2bd6 | |||
ae629ed3a9 | |||
42fe60828f | |||
36899f1135 | |||
0714e100ee | |||
cbfee945a0 | |||
71bb461ed8 | |||
5143f5ac01 | |||
7e83741615 | |||
b880fc0367 | |||
f6d5186e5b | |||
8eb3a0feea | |||
28cd898eca | |||
a22391ae2c | |||
f5a0d5fb40 |
19
.github/workflows/modem__build-host-tests.yml
vendored
19
.github/workflows/modem__build-host-tests.yml
vendored
@ -22,6 +22,8 @@ jobs:
|
||||
example: modem_tcp_client
|
||||
- idf_ver: "release-v4.3"
|
||||
example: modem_tcp_client
|
||||
- idf_ver: "release-v4.4"
|
||||
example: modem_tcp_client
|
||||
include:
|
||||
- idf_ver: "release-v4.2"
|
||||
skip_config: usb
|
||||
@ -109,7 +111,6 @@ jobs:
|
||||
- name: Build and Test
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get update && apt-get install -y gcc-8 g++-8 python3-pip
|
||||
apt-get install -y rsync
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
|
||||
@ -129,9 +130,9 @@ jobs:
|
||||
cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }}
|
||||
gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/*
|
||||
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml
|
||||
mkdir docs_gcovr
|
||||
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr
|
||||
cp -rf docs_gcovr $GITHUB_WORKSPACE
|
||||
mkdir modem_coverage_report
|
||||
cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report
|
||||
cp -rf modem_coverage_report $GITHUB_WORKSPACE
|
||||
- name: Code Coverage Summary Report
|
||||
uses: irongut/CodeCoverageSummary@v1.3.0
|
||||
with:
|
||||
@ -151,13 +152,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: docs_gcovr
|
||||
name: modem_coverage_report
|
||||
path: |
|
||||
${{ env.COMP_DIR }}/docs_gcovr
|
||||
${{ env.COMP_DIR }}/modem_coverage_report
|
||||
if-no-files-found: error
|
||||
- name: Deploy code coverage results
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./docs_gcovr
|
||||
|
45
.github/workflows/publish-coverage-report.yml
vendored
Normal file
45
.github/workflows/publish-coverage-report.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Publish coverage report to Github Pages
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"]
|
||||
types:
|
||||
- completed
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
publish_github_pages:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'espressif/esp-protocols'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Download Websocket Artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: websocket__build-host-tests.yml
|
||||
workflow_conclusion: success
|
||||
name: websocket_coverage_report
|
||||
path: websocket_coverage_report_artifact
|
||||
- name: Download Modem Artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: modem__build-host-tests.yml
|
||||
workflow_conclusion: success
|
||||
name: modem_coverage_report
|
||||
path: modem_coverage_report_artifact
|
||||
- name: Merge HTML files
|
||||
run: |
|
||||
echo "<html><body>" > index.html
|
||||
cat modem_coverage_report_artifact/index.html >> index.html
|
||||
cat websocket_coverage_report_artifact/index.html >> index.html
|
||||
echo "</body></html>" >> index.html
|
||||
mkdir coverage_report
|
||||
mv index.html coverage_report
|
||||
|
||||
- name: Deploy generated docs
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.5
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: coverage_report
|
86
.github/workflows/run-host-tests.yml
vendored
Normal file
86
.github/workflows/run-host-tests.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
name: Run on host
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
idf_version:
|
||||
required: true
|
||||
type: string
|
||||
app_name:
|
||||
type: string
|
||||
required: true
|
||||
app_path:
|
||||
type: string
|
||||
required: true
|
||||
component_path:
|
||||
type: string
|
||||
required: true
|
||||
upload_artifacts:
|
||||
type: boolean
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build App
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: write
|
||||
container: espressif/idf:${{inputs.idf_version}}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
|
||||
rm -rf sdkconfig sdkconfig.defaults build
|
||||
cp sdkconfig.ci.linux sdkconfig.defaults
|
||||
idf.py build
|
||||
./build/${{inputs.app_name}}.elf
|
||||
- name: Build with Coverage Enabled
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/${{inputs.app_path}}
|
||||
rm -rf build sdkconfig sdkconfig.defaults
|
||||
cp sdkconfig.ci.coverage sdkconfig.defaults
|
||||
idf.py fullclean
|
||||
idf.py build
|
||||
./build/${{inputs.app_name}}.elf
|
||||
- name: Run Coverage
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update && apt-get install -y python3-pip rsync
|
||||
python -m pip install gcovr
|
||||
cd $GITHUB_WORKSPACE/${{inputs.component_path}}
|
||||
gcov `find . -name "*gcda"`
|
||||
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x ${{inputs.app_name}}_coverage.xml
|
||||
mkdir ${{inputs.app_name}}_coverage_report
|
||||
touch ${{inputs.app_name}}_coverage_report/.nojekyll
|
||||
cp index.html ${{inputs.app_name}}_coverage_report
|
||||
cp -rf ${{inputs.app_name}}_coverage_report ${{inputs.app_name}}_coverage.xml $GITHUB_WORKSPACE
|
||||
- name: Code Coverage Summary Report
|
||||
uses: irongut/CodeCoverageSummary@v1.3.0
|
||||
with:
|
||||
filename: esp-protocols/**/${{inputs.app_name}}_coverage.xml
|
||||
badge: true
|
||||
fail_below_min: false
|
||||
format: markdown
|
||||
hide_branch_rate: false
|
||||
hide_complexity: false
|
||||
indicators: true
|
||||
output: both
|
||||
thresholds: '60 80'
|
||||
- name: Write to Job Summary
|
||||
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
|
||||
- name: Upload files to artifacts for run-target job
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{inputs.upload_artifacts}}
|
||||
with:
|
||||
name: ${{inputs.app_name}}_coverage_report
|
||||
path: |
|
||||
${{inputs.component_path}}/${{inputs.app_name}}_coverage_report
|
||||
if-no-files-found: error
|
20
.github/workflows/websocket__build-host-tests.yml
vendored
Normal file
20
.github/workflows/websocket__build-host-tests.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: "websocket: build/host-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
|
||||
jobs:
|
||||
host_test_websocket:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push'
|
||||
uses: "./.github/workflows/run-host-tests.yml"
|
||||
with:
|
||||
idf_version: "latest"
|
||||
app_name: "websocket"
|
||||
app_path: "esp-protocols/components/esp_websocket_client/examples/linux"
|
||||
component_path: "esp-protocols/components/esp_websocket_client"
|
||||
upload_artifacts: true
|
@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
|
||||
test: [ { app: example, path: "examples" }, { app: unit_test, path: "test" } ]
|
||||
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
@ -53,7 +53,7 @@ jobs:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "latest"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "examples" }, { app: unit_test, path: "test" } ]
|
||||
test: [ { app: example, path: "examples/target" }, { app: unit_test, path: "test" } ]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
|
@ -43,7 +43,7 @@ repos:
|
||||
hooks:
|
||||
- id: eradicate
|
||||
- repo: https://github.com/espressif/check-copyright/
|
||||
rev: v1.0.1
|
||||
rev: v1.0.3
|
||||
hooks:
|
||||
- id: check-copyright
|
||||
args: ['--ignore', 'ci/check_copyright_ignore.txt', '--config', 'ci/check_copyright_config.yaml']
|
||||
|
@ -1,5 +1,11 @@
|
||||
# Collection of protocol components for ESP-IDF
|
||||
|
||||
## How to use
|
||||
|
||||
The [ESP-Protocols](https://github.com/espressif/esp-protocols) repository contains a collection of protocol components for [ESP-IDF](https://github.com/espressif/esp-idf).
|
||||
Additionally, each component is available in [IDF Component Registry](https://components.espressif.com).
|
||||
Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
|
||||
## Components
|
||||
|
||||
### esp_modem
|
||||
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
4
components/asio/examples/socks4/main/idf_component.yml
Normal file
4
components/asio/examples/socks4/main/idf_component.yml
Normal file
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/asio: ">=1.0.1"
|
@ -16,6 +16,17 @@ menu "esp-modem"
|
||||
in command mode might come fragmented in rare cases so might need to retry
|
||||
AT commands.
|
||||
|
||||
config ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
bool "Use inflatable buffer in DCE"
|
||||
default n
|
||||
help
|
||||
If enabled we will process the ongoing AT command by growing the current
|
||||
buffer (if we've run out the preconfigured buffer).
|
||||
If disabled, we simply report a failure.
|
||||
Use this if additional allocation is not a problem and you need to reliably process
|
||||
all commands, usually with sporadically longer responses than the configured buffer.
|
||||
Could be also used to defragment AT replies in CMUX mode if CMUX_DEFRAGMENT_PAYLOAD=n
|
||||
|
||||
config ESP_MODEM_CMUX_DELAY_AFTER_DLCI_SETUP
|
||||
int "Delay in ms to wait before creating another virtual terminal"
|
||||
default 0
|
||||
|
@ -1,7 +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.8)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
|
||||
|
@ -5,6 +5,10 @@
|
||||
## Overview
|
||||
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)
|
||||
|
||||
This example could be used in two different configurations:
|
||||
1) Custom TCP transport: Implements a TCP transport in form of AT commands and uses it as custom transport for mqtt client.
|
||||
2) Localhost listener: Uses standard transports to connect and forwards socket layer data from the client to the modem using AT commands.
|
||||
|
||||
### Supported IDF versions
|
||||
|
||||
This example is supported from IDF `v4.4`.
|
||||
This example is supported from IDF `v5.0`.
|
||||
|
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS mbedtls_wrap.cpp
|
||||
tls_transport.cpp
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES tcp_transport)
|
@ -0,0 +1,9 @@
|
||||
# Custom transports
|
||||
|
||||
This component is a placeholder of custom transports. It contains mbedTLS cxx wrapper which could be used to create a custom TLS layer on any kind of IO function.
|
||||
|
||||
# List of Transports
|
||||
|
||||
## TLS Transport
|
||||
|
||||
TLS layer on top of any custom transport. Very similar to `ssl_transport` (standard IDF transport), but this is customizable and could work on any kind of transport, not only the BSD socket based tcp transport (like `ssl_transport`).
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <span>
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
|
||||
using const_buf = std::span<const unsigned char>;
|
||||
|
||||
class Tls {
|
||||
public:
|
||||
enum class is_server : bool {};
|
||||
enum class do_verify : bool {};
|
||||
|
||||
Tls();
|
||||
virtual ~Tls();
|
||||
bool init(is_server server, do_verify verify);
|
||||
int handshake();
|
||||
int write(const unsigned char *buf, size_t len);
|
||||
int read(unsigned char *buf, size_t len);
|
||||
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
|
||||
[[nodiscard]] bool set_ca_cert(const_buf crt);
|
||||
virtual int send(const unsigned char *buf, size_t len) = 0;
|
||||
virtual int recv(unsigned char *buf, size_t len) = 0;
|
||||
size_t get_available_bytes();
|
||||
|
||||
protected:
|
||||
mbedtls_ssl_context ssl_{};
|
||||
mbedtls_x509_crt public_cert_{};
|
||||
mbedtls_pk_context pk_key_{};
|
||||
mbedtls_x509_crt ca_cert_{};
|
||||
mbedtls_ssl_config conf_{};
|
||||
mbedtls_ctr_drbg_context ctr_drbg_{};
|
||||
mbedtls_entropy_context entropy_{};
|
||||
virtual void delay() {}
|
||||
|
||||
private:
|
||||
static void print_error(const char *function, int error_code);
|
||||
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
|
||||
static int bio_read(void *ctx, unsigned char *buf, size_t len);
|
||||
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
|
||||
const unsigned char *key, size_t keylen,
|
||||
const unsigned char *pwd, size_t pwdlen);
|
||||
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_transport.h"
|
||||
|
||||
/**
|
||||
* @brief Initializes TLS transport based on mbetls wrapper
|
||||
*
|
||||
* @param parent Transport on top of which we run TLS layer
|
||||
* @return Transport handle on success
|
||||
*/
|
||||
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent);
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls_wrap.hpp"
|
||||
|
||||
bool Tls::init(is_server server, do_verify verify)
|
||||
{
|
||||
const char pers[] = "mbedtls_wrapper";
|
||||
mbedtls_entropy_init(&entropy_);
|
||||
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
|
||||
int ret = mbedtls_ssl_config_defaults(&conf_, server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_config_defaults", ret);
|
||||
return false;
|
||||
}
|
||||
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
|
||||
mbedtls_ssl_conf_authmode(&conf_, verify == do_verify{true} ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
|
||||
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_conf_own_cert", ret);
|
||||
return false;
|
||||
}
|
||||
if (verify == do_verify{true}) {
|
||||
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
|
||||
}
|
||||
ret = mbedtls_ssl_setup(&ssl_, &conf_);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_setup", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tls::print_error(const char *function, int error_code)
|
||||
{
|
||||
static char error_buf[100];
|
||||
mbedtls_strerror(error_code, error_buf, sizeof(error_buf));
|
||||
|
||||
printf("%s() returned -0x%04X\n", function, -error_code);
|
||||
printf("-0x%04X: %s\n", -error_code, error_buf);
|
||||
}
|
||||
|
||||
int Tls::handshake()
|
||||
{
|
||||
int ret = 0;
|
||||
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, nullptr);
|
||||
|
||||
while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
|
||||
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
|
||||
print_error( "mbedtls_ssl_handshake returned", ret );
|
||||
return -1;
|
||||
}
|
||||
delay();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Tls::bio_write(void *ctx, const unsigned char *buf, size_t len)
|
||||
{
|
||||
auto s = static_cast<Tls *>(ctx);
|
||||
return s->send(buf, len);
|
||||
}
|
||||
|
||||
int Tls::bio_read(void *ctx, unsigned char *buf, size_t len)
|
||||
{
|
||||
auto s = static_cast<Tls *>(ctx);
|
||||
return s->recv(buf, len);
|
||||
}
|
||||
|
||||
int Tls::write(const unsigned char *buf, size_t len)
|
||||
{
|
||||
return mbedtls_ssl_write( &ssl_, buf, len );
|
||||
}
|
||||
|
||||
int Tls::read(unsigned char *buf, size_t len)
|
||||
{
|
||||
return mbedtls_ssl_read( &ssl_, buf, len );
|
||||
}
|
||||
|
||||
bool Tls::set_own_cert(const_buf crt, const_buf key)
|
||||
{
|
||||
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.data(), crt.size());
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_x509_crt_parse", ret);
|
||||
return false;
|
||||
}
|
||||
ret = mbedtls_pk_parse_key(&pk_key_, key.data(), key.size(), nullptr, 0);
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_pk_parse_keyfile", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Tls::set_ca_cert(const_buf crt)
|
||||
{
|
||||
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.data(), crt.size());
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_x509_crt_parse", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Tls::Tls()
|
||||
{
|
||||
mbedtls_x509_crt_init(&public_cert_);
|
||||
mbedtls_pk_init(&pk_key_);
|
||||
mbedtls_x509_crt_init(&ca_cert_);
|
||||
}
|
||||
|
||||
int Tls::mbedtls_pk_parse_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen, const unsigned char *pwd, size_t pwdlen)
|
||||
{
|
||||
|
||||
return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen, nullptr, nullptr);
|
||||
}
|
||||
|
||||
size_t Tls::get_available_bytes()
|
||||
{
|
||||
return ::mbedtls_ssl_get_bytes_avail(&ssl_);
|
||||
}
|
||||
|
||||
Tls::~Tls()
|
||||
{
|
||||
::mbedtls_ssl_config_free(&conf_);
|
||||
::mbedtls_ssl_free(&ssl_);
|
||||
::mbedtls_pk_free(&pk_key_);
|
||||
::mbedtls_x509_crt_free(&public_cert_);
|
||||
::mbedtls_x509_crt_free(&ca_cert_);
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_transport.h"
|
||||
#include "mbedtls_wrap.hpp"
|
||||
|
||||
static const char *TAG = "tls_transport";
|
||||
|
||||
class TlsTransport: public Tls {
|
||||
public:
|
||||
explicit TlsTransport(esp_transport_handle_t parent) : Tls(), transport_(parent) {}
|
||||
int send(const unsigned char *buf, size_t len) override;
|
||||
int recv(unsigned char *buf, size_t len) override;
|
||||
static bool set_func(esp_transport_handle_t tls_transport);
|
||||
|
||||
private:
|
||||
esp_transport_handle_t transport_{};
|
||||
int connect(const char *host, int port, int timeout_ms);
|
||||
void delay() override;
|
||||
|
||||
struct transport {
|
||||
static int connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms);
|
||||
static int read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms);
|
||||
static int write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms);
|
||||
static int close(esp_transport_handle_t t);
|
||||
static int poll_read(esp_transport_handle_t t, int timeout_ms);
|
||||
static int poll_write(esp_transport_handle_t t, int timeout_ms);
|
||||
static int destroy(esp_transport_handle_t t);
|
||||
};
|
||||
};
|
||||
|
||||
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent)
|
||||
{
|
||||
esp_transport_handle_t ssl = esp_transport_init();
|
||||
auto *tls = new TlsTransport(parent);
|
||||
esp_transport_set_context_data(ssl, tls);
|
||||
TlsTransport::set_func(ssl);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
int TlsTransport::send(const unsigned char *buf, size_t len)
|
||||
{
|
||||
return esp_transport_write(transport_, reinterpret_cast<const char *>(buf), len, 0);
|
||||
}
|
||||
|
||||
int TlsTransport::recv(unsigned char *buf, size_t len)
|
||||
{
|
||||
int ret = esp_transport_read(transport_, reinterpret_cast<char *>(buf), len, 0);
|
||||
|
||||
if (ret == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT) {
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
return ret == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN ? 0 : ret;
|
||||
}
|
||||
|
||||
bool TlsTransport::set_func(esp_transport_handle_t tls_transport)
|
||||
{
|
||||
return esp_transport_set_func(tls_transport, TlsTransport::transport::connect, TlsTransport::transport::read, TlsTransport::transport::write, TlsTransport::transport::close, TlsTransport::transport::poll_read, TlsTransport::transport::poll_write, TlsTransport::transport::destroy) == ESP_OK;
|
||||
}
|
||||
|
||||
int TlsTransport::connect(const char *host, int port, int timeout_ms)
|
||||
{
|
||||
return esp_transport_connect(transport_, host, port, timeout_ms);
|
||||
}
|
||||
|
||||
void TlsTransport::delay()
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
int TlsTransport::transport::connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
tls->init(is_server{false}, do_verify{false});
|
||||
|
||||
auto ret = tls->connect(host, port, timeout_ms);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return tls->handshake();
|
||||
}
|
||||
|
||||
int TlsTransport::transport::read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
if (tls->get_available_bytes() <= 0) {
|
||||
int poll = esp_transport_poll_read(t, timeout_ms);
|
||||
if (poll == -1) {
|
||||
return ERR_TCP_TRANSPORT_CONNECTION_FAILED;
|
||||
}
|
||||
if (poll == 0) {
|
||||
return ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return tls->read(reinterpret_cast<unsigned char *>(buffer), len);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
int poll;
|
||||
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
|
||||
ESP_LOGW(TAG, "Poll timeout or error timeout_ms=%d", timeout_ms);
|
||||
return poll;
|
||||
}
|
||||
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return tls->write(reinterpret_cast<const unsigned char *>(buffer), len);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::close(esp_transport_handle_t t)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_close(tls->transport_);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::poll_read(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_poll_read(tls->transport_, timeout_ms);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::poll_write(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_poll_write(tls->transport_, timeout_ms);
|
||||
}
|
||||
|
||||
int TlsTransport::transport::destroy(esp_transport_handle_t t)
|
||||
{
|
||||
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
|
||||
return esp_transport_destroy(tls->transport_);
|
||||
}
|
@ -6,6 +6,7 @@ endif()
|
||||
|
||||
idf_component_register(SRCS "modem_client.cpp"
|
||||
"sock_dce.cpp"
|
||||
"tcp_transport_at.cpp"
|
||||
"${device_srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
@ -1,5 +1,10 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
bool "Custom TCP transport"
|
||||
help
|
||||
Use custom TCP transport to connect to MQTT broker
|
||||
|
||||
choice EXAMPLE_MODEM_DEVICE
|
||||
prompt "Choose supported modem device (DCE)"
|
||||
default EXAMPLE_MODEM_DEVICE_BG96
|
||||
|
@ -15,14 +15,17 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "tcp_transport_mbedtls.h"
|
||||
#include "tcp_transport_at.h"
|
||||
|
||||
#define BROKER_URL "mqtt.eclipseprojects.io"
|
||||
#define BROKER_PORT 8883
|
||||
|
||||
|
||||
static const char *TAG = "modem_client";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
@ -70,6 +73,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
|
||||
@ -101,24 +105,29 @@ extern "C" void app_main(void)
|
||||
|
||||
/* create the DCE and initialize network manually (using AT commands) */
|
||||
auto dce = sock_dce::create(&dce_config, std::move(dte));
|
||||
if (!dce->init_network()) {
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
|
||||
dce->init_sock(8883);
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
|
||||
mqtt_config.broker.address.port = BROKER_PORT;
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
|
||||
dce->start_listening(BROKER_PORT);
|
||||
#else
|
||||
mqtt_config.uri = "mqtt://127.0.0.1";
|
||||
mqtt_config.message_retransmit_timeout = 10000;
|
||||
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
|
||||
esp_transport_handle_t at = esp_transport_at_init(dce.get());
|
||||
esp_transport_handle_t ssl = esp_transport_tls_init(at);
|
||||
|
||||
mqtt_config.network.transport = ssl;
|
||||
#endif
|
||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
||||
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
if (!dce->start(BROKER_URL, 8883)) {
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
ESP_LOGE(TAG, "Failed to start DCE");
|
||||
return;
|
||||
}
|
||||
@ -128,13 +137,16 @@ extern "C" void app_main(void)
|
||||
}
|
||||
ESP_LOGE(TAG, "Loop exit.. retrying");
|
||||
// handle disconnections errors
|
||||
if (!dce->init_network()) {
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to reinit network");
|
||||
return;
|
||||
}
|
||||
if (!dce->start("test.mosquitto.org", 1883)) {
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
ESP_LOGI(TAG, "Network reinitialized, retrying");
|
||||
}
|
||||
}
|
||||
#else
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
//using namespace esp_modem;
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include "sock_commands.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
@ -132,7 +131,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+QIRD: ";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == nullptr) {
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
@ -160,17 +159,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
recv_data = next_nl + 1;
|
||||
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
|
||||
if (actual_len > first_data_len) {
|
||||
::send(sock, recv_data, first_data_len, 0);
|
||||
on_read(recv_data, first_data_len);
|
||||
data_to_recv = actual_len - first_data_len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
::send(sock, recv_data, actual_len, 0);
|
||||
on_read(recv_data, actual_len);
|
||||
} else if (data_to_recv > len) { // continue sending
|
||||
::send(sock, recv_data, len, 0);
|
||||
on_read(recv_data, len);
|
||||
data_to_recv -= len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
||||
::send(sock, recv_data, data_to_recv, 0);
|
||||
on_read(recv_data, data_to_recv);
|
||||
actual_len = data_to_recv;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include <charconv>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include "sock_commands.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "sock_dce.hpp"
|
||||
@ -218,7 +217,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
if (data_to_recv == 0) {
|
||||
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == nullptr) {
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
@ -246,7 +245,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
ESP_LOGE(TAG, "TOO BIG");
|
||||
return ret::FAIL;
|
||||
}
|
||||
size_t total_len = 0;
|
||||
total_len = 0;
|
||||
if (std::from_chars(next_comma + 1, next_nl - 1, total_len).ec == std::errc::invalid_argument) {
|
||||
ESP_LOGE(TAG, "cannot convert");
|
||||
return ret::FAIL;
|
||||
@ -255,17 +254,17 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
recv_data = next_nl + 1;
|
||||
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
|
||||
if (actual_len > first_data_len) {
|
||||
::send(sock, recv_data, first_data_len, 0);
|
||||
on_read(recv_data, first_data_len);
|
||||
data_to_recv = actual_len - first_data_len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
}
|
||||
::send(sock, recv_data, actual_len, 0);
|
||||
on_read(recv_data, actual_len);
|
||||
} else if (data_to_recv > len) { // continue sending
|
||||
::send(sock, recv_data, len, 0);
|
||||
on_read(recv_data, len);
|
||||
data_to_recv -= len;
|
||||
return ret::NEED_MORE_DATA;
|
||||
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
||||
::send(sock, recv_data, data_to_recv, 0);
|
||||
on_read(recv_data, data_to_recv);
|
||||
actual_len = data_to_recv;
|
||||
}
|
||||
|
||||
|
@ -192,14 +192,8 @@ bool DCE::accept_sock()
|
||||
return false;
|
||||
}
|
||||
|
||||
void DCE::init_sock(int port)
|
||||
void DCE::start_listening(int port)
|
||||
{
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
if (listen_sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||
@ -228,7 +222,7 @@ void DCE::init_sock(int port)
|
||||
|
||||
}
|
||||
|
||||
bool DCE::start(std::string host, int port)
|
||||
bool DCE::connect(std::string host, int port)
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
tcp_close();
|
||||
@ -245,8 +239,14 @@ bool DCE::start(std::string host, int port)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::init_network()
|
||||
bool DCE::init()
|
||||
{
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "socket_commands.inc"
|
||||
#include "sock_commands.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -46,9 +47,36 @@ public:
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void clear_offsets()
|
||||
{
|
||||
actual_read = 0;
|
||||
}
|
||||
|
||||
size_t data_available()
|
||||
{
|
||||
return actual_read;
|
||||
}
|
||||
|
||||
size_t has_data()
|
||||
{
|
||||
return total_len;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
ret recv(uint8_t *data, size_t len);
|
||||
ret send(uint8_t *data, size_t len);
|
||||
ret send(std::string_view response);
|
||||
@ -59,6 +87,9 @@ private:
|
||||
}
|
||||
std::array<uint8_t, buffer_size> buffer;
|
||||
size_t data_to_recv = 0;
|
||||
size_t actual_read = 0;
|
||||
size_t total_len = 0;
|
||||
|
||||
bool read_again = false;
|
||||
int &sock;
|
||||
int &data_ready_fd;
|
||||
@ -78,13 +109,101 @@ esp_modem::return_type name(__VA_ARGS__);
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
bool init_network();
|
||||
bool start(std::string host, int port);
|
||||
bool init();
|
||||
bool connect(std::string host, int port);
|
||||
|
||||
void init_sock(int port);
|
||||
void start_listening(int port);
|
||||
|
||||
bool perform_sock();
|
||||
|
||||
void set_idle()
|
||||
{
|
||||
signal.set(IDLE);
|
||||
}
|
||||
|
||||
bool wait_to_idle(uint32_t ms)
|
||||
{
|
||||
if (!signal.wait(IDLE, ms)) {
|
||||
ESP_LOGE("dce", "Failed to get idle");
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE("dce", "Unexpected state %d", state);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int sync_recv(char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
int max_len = std::min(len, (int)at.get_buf_len());
|
||||
at.start_receiving(max_len);
|
||||
if (!signal.wait(IDLE, 500 + timeout_ms)) {
|
||||
return 0;
|
||||
}
|
||||
int ret = at.data_available();
|
||||
if (ret > 0) {
|
||||
memcpy(buffer, at.get_buf(), ret);
|
||||
}
|
||||
set_idle();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sync_send(const char *buffer, size_t len, int timeout_ms)
|
||||
{
|
||||
int len_to_send = std::min(len, at.get_buf_len());
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
at.start_sending(len_to_send);
|
||||
if (!signal.wait(IDLE, timeout_ms + 1000)) {
|
||||
if (state == status::PENDING) {
|
||||
state = status::IDLE;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
set_idle();
|
||||
return len_to_send;
|
||||
}
|
||||
|
||||
int wait_to_read(uint32_t ms)
|
||||
{
|
||||
if (at.has_data() > 0) {
|
||||
ESP_LOGD("dce", "Data buffered in modem (len=%d)", at.has_data());
|
||||
return 1;
|
||||
}
|
||||
struct timeval tv = {
|
||||
.tv_sec = static_cast<time_t>(ms / 1000),
|
||||
.tv_usec = 0,
|
||||
};
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(data_ready_fd, &fdset);
|
||||
int s = select(data_ready_fd + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s == 0) {
|
||||
return 0;
|
||||
} else if (s < 0) {
|
||||
ESP_LOGE("dce", "select error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
if (FD_ISSET(data_ready_fd, &fdset)) {
|
||||
ESP_LOGD("dce", "select read: modem data available");
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "sock_dce.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "esp_transport.h"
|
||||
|
||||
|
||||
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
|
||||
{
|
||||
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
|
||||
if (!dce->connect(host, port)) {
|
||||
return -1;
|
||||
}
|
||||
if (dce->wait_to_idle(timeout_ms)) {
|
||||
dce->set_idle();
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int tcp_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
|
||||
auto ret = dce->wait_to_read(1000);
|
||||
if (ret > 0) {
|
||||
return dce->sync_recv(buffer, len, timeout_ms);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcp_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
|
||||
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
|
||||
return dce->sync_send(buffer, len, timeout_ms);
|
||||
|
||||
}
|
||||
|
||||
static int base_close(esp_transport_handle_t t)
|
||||
{
|
||||
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
|
||||
return dce->tcp_close() == esp_modem::command_result::OK ? 0 : -1;
|
||||
}
|
||||
|
||||
static int base_poll_read(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
auto *dce = (sock_dce::DCE *)esp_transport_get_context_data(t);
|
||||
return dce->wait_to_read(timeout_ms);
|
||||
}
|
||||
|
||||
static int base_poll_write(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
// expect the socket is always writable
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int base_destroy(esp_transport_handle_t transport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce)
|
||||
{
|
||||
esp_transport_handle_t at = esp_transport_init();
|
||||
if (!at) {
|
||||
return nullptr;
|
||||
}
|
||||
esp_transport_set_context_data(at, (void *)dce);
|
||||
esp_transport_set_func(at, tcp_connect, tcp_read, tcp_write, base_close, base_poll_read, base_poll_write, base_destroy);
|
||||
return at;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_transport.h"
|
||||
#include "sock_dce.hpp"
|
||||
|
||||
/**
|
||||
* @brief Initializes TCP transport based on AT commands
|
||||
*
|
||||
* @param DCE
|
||||
* @return Transport handle on success
|
||||
*/
|
||||
esp_transport_handle_t esp_transport_at_init(sock_dce::DCE *dce);
|
@ -175,6 +175,9 @@ extern "C" void app_main(void)
|
||||
#endif
|
||||
assert(dce);
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
StatusHandler handler;
|
||||
|
||||
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
if (command_result::OK != dce->set_flow_control(2, 2)) {
|
||||
ESP_LOGE(TAG, "Failed to set the set_flow_control mode");
|
||||
@ -215,8 +218,6 @@ extern "C" void app_main(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
StatusHandler handler;
|
||||
if (!handler.wait_for(StatusHandler::IP_Event, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
|
||||
return;
|
||||
|
@ -236,6 +236,14 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T_Module>
|
||||
static std::unique_ptr<DCE> create_unique_dce_from(const esp_modem::dce_config *config,
|
||||
std::shared_ptr<esp_modem::DTE> dte,
|
||||
esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<T_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
private:
|
||||
ModemType m;
|
||||
|
||||
|
@ -79,6 +79,13 @@ public:
|
||||
*/
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f);
|
||||
|
||||
/**
|
||||
* @brief Sets read callback for manual command processing
|
||||
* Note that this API also locks the command API, which can only be used
|
||||
* after you remove the callback by dte->on_read(nullptr)
|
||||
*
|
||||
* @param on_data Function to be called when a command response is available
|
||||
*/
|
||||
void on_read(got_line_cb on_data) override;
|
||||
|
||||
/**
|
||||
@ -122,10 +129,10 @@ protected:
|
||||
}
|
||||
friend class Scoped<DTE>; /*!< Declaring "Scoped<DTE> lock(dte)" locks this instance */
|
||||
private:
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
|
||||
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode */
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
|
||||
void exit_cmux_internal(); /*!< Cleanup CMUX */
|
||||
|
||||
Lock internal_lock{}; /*!< Locks DTE operations */
|
||||
unique_buffer buffer; /*!< DTE buffer */
|
||||
@ -133,9 +140,73 @@ private:
|
||||
std::shared_ptr<Terminal> primary_term; /*!< Reference to the primary terminal (mostly for sending commands) */
|
||||
std::shared_ptr<Terminal> secondary_term; /*!< Secondary terminal for this DTE */
|
||||
modem_mode mode; /*!< DTE operation mode */
|
||||
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||
command_result result; /*!< Command result of the currently exectuted command */
|
||||
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
/**
|
||||
* @brief Implements an extra buffer that is used to capture partial reads from underlying terminals
|
||||
* when we run out of the standard buffer
|
||||
*/
|
||||
struct extra_buffer {
|
||||
extra_buffer(): buffer(nullptr) {}
|
||||
~extra_buffer()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
std::vector<uint8_t> *buffer;
|
||||
size_t consumed{0};
|
||||
void grow(size_t need_size);
|
||||
void deflate()
|
||||
{
|
||||
grow(0);
|
||||
consumed = 0;
|
||||
}
|
||||
[[nodiscard]] uint8_t *begin() const
|
||||
{
|
||||
return &buffer->at(0);
|
||||
}
|
||||
[[nodiscard]] uint8_t *current() const
|
||||
{
|
||||
return &buffer->at(0) + consumed;
|
||||
}
|
||||
} inflatable;
|
||||
#endif // CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
|
||||
/**
|
||||
* @brief Set internal command callbacks to the underlying terminal.
|
||||
* Here we capture command replies to be processed by supplied command callbacks in struct command_cb.
|
||||
*/
|
||||
void set_command_callbacks();
|
||||
|
||||
/**
|
||||
* @brief This abstracts command callback processing and implements its locking, signaling of completion and timeouts.
|
||||
*/
|
||||
struct command_cb {
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
got_line_cb got_line; /*!< Supplied command callback */
|
||||
Lock line_lock{}; /*!< Command callback locking mechanism */
|
||||
char separator{}; /*!< Command reply separator (end of line/processing unit) */
|
||||
command_result result{}; /*!< Command return code */
|
||||
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
|
||||
bool wait_for_line(uint32_t time_ms); /*!< Waiting for command processing */
|
||||
void set(got_line_cb l, char s = '\n') /*!< Sets the command callback atomically */
|
||||
{
|
||||
Scoped<Lock> lock(line_lock);
|
||||
if (l) {
|
||||
// if we set the line callback, we have to reset the signal and the result
|
||||
signal.clear(GOT_LINE);
|
||||
result = command_result::TIMEOUT;
|
||||
}
|
||||
got_line = std::move(l);
|
||||
separator = s;
|
||||
}
|
||||
void give_up() /*!< Reports other than timeout error when processing replies (out of buffer) */
|
||||
{
|
||||
result = command_result::FAIL;
|
||||
signal.set(GOT_LINE);
|
||||
}
|
||||
} command_cb; /*!< Command callback utility class */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -54,9 +54,10 @@ public:
|
||||
*/
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void receive(uint8_t *data, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len);
|
||||
|
||||
static esp_err_t esp_modem_post_attach(esp_netif_t *esp_netif, void *args);
|
||||
|
@ -21,10 +21,17 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
|
||||
netif.stop();
|
||||
auto signal = std::make_shared<SignalGroup>();
|
||||
std::weak_ptr<SignalGroup> weak_signal = signal;
|
||||
dte.set_read_cb([weak_signal](uint8_t *data, size_t len) -> bool {
|
||||
dte.set_read_cb([&netif, weak_signal](uint8_t *data, size_t len) -> bool {
|
||||
// post the transitioning data to the network layers if it contains PPP SOF marker
|
||||
if (memchr(data, 0x7E, len))
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (PPP)", data, len, ESP_LOG_DEBUG);
|
||||
netif.receive(data, len);
|
||||
}
|
||||
// treat the transitioning data as a textual message if it contains a newline char
|
||||
if (memchr(data, '\n', len))
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", data, len, ESP_LOG_DEBUG);
|
||||
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data (CMD)", data, len, ESP_LOG_DEBUG);
|
||||
const auto pass = std::list<std::string_view>({"NO CARRIER", "DISCONNECTED"});
|
||||
std::string_view response((char *) data, len);
|
||||
for (auto &it : pass)
|
||||
@ -39,6 +46,7 @@ static bool exit_data(DTE &dte, ModuleIf &device, Netif &netif)
|
||||
});
|
||||
netif.wait_until_ppp_exits();
|
||||
if (!signal->wait(1, 2000)) {
|
||||
dte.set_read_cb(nullptr);
|
||||
if (!device.set_mode(modem_mode::COMMAND_MODE)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -17,53 +17,118 @@ static const size_t dte_default_buffer_size = 1000;
|
||||
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
|
||||
buffer(config->dte_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(std::unique_ptr<Terminal> terminal):
|
||||
buffer(dte_default_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
|
||||
mode(modem_mode::UNDEF) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
|
||||
buffer(config->dte_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
|
||||
mode(modem_mode::DUAL_MODE) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
|
||||
buffer(dte_default_buffer_size),
|
||||
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
|
||||
mode(modem_mode::DUAL_MODE) {}
|
||||
mode(modem_mode::UNDEF)
|
||||
{
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
void DTE::set_command_callbacks()
|
||||
{
|
||||
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
Scoped<Lock> l(command_cb.line_lock);
|
||||
if (command_cb.got_line == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (data) {
|
||||
// For terminals which post data directly with the callback (CMUX)
|
||||
// we cannot defragment unless we allocate, but
|
||||
// we'll try to process the data on the actual buffer
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
if (inflatable.consumed != 0) {
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
std::memcpy(inflatable.current(), data, len);
|
||||
data = inflatable.begin();
|
||||
}
|
||||
if (command_cb.process_line(data, inflatable.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
// at this point we're sure that the data processing hasn't finished,
|
||||
// and we have to grow the inflatable buffer (if enabled) or give up
|
||||
if (inflatable.consumed == 0) {
|
||||
inflatable.grow(len);
|
||||
std::memcpy(inflatable.begin(), data, len);
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
if (command_cb.process_line(data, 0, len)) {
|
||||
return true;
|
||||
}
|
||||
// cannot inflate and the processing hasn't finishes in the first iteration -> report a failure
|
||||
command_cb.give_up();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
// data == nullptr: Terminals which request users to read current data
|
||||
// we're able to use DTE's buffer to defragment it; as long as we consume less that the buffer size
|
||||
if (buffer.size > buffer.consumed) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
|
||||
if (command_cb.process_line(data, buffer.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
buffer.consumed += len;
|
||||
return false;
|
||||
}
|
||||
// we have used the entire DTE's buffer, need to use the inflatable buffer to continue
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
if (inflatable.consumed == 0) {
|
||||
inflatable.grow(buffer.size + len);
|
||||
std::memcpy(inflatable.begin(), buffer.get(), buffer.size);
|
||||
inflatable.consumed = buffer.size;
|
||||
} else {
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
}
|
||||
len = primary_term->read(inflatable.current(), len);
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
|
||||
return true;
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
// cannot inflate -> report a failure
|
||||
command_cb.give_up();
|
||||
return true;
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l(internal_lock);
|
||||
result = command_result::TIMEOUT;
|
||||
signal.clear(GOT_LINE);
|
||||
primary_term->set_read_cb([this, got_line, separator](uint8_t *data, size_t len) {
|
||||
if (!data) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
|
||||
} else {
|
||||
buffer.consumed = 0; // if the underlying terminal contains data, we cannot fragment
|
||||
}
|
||||
if (memchr(data + buffer.consumed, separator, len)) {
|
||||
result = got_line(data, buffer.consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
signal.set(GOT_LINE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
buffer.consumed += len;
|
||||
return false;
|
||||
});
|
||||
Scoped<Lock> l1(internal_lock);
|
||||
command_cb.set(got_line, separator);
|
||||
primary_term->write((uint8_t *)command.c_str(), command.length());
|
||||
auto got_lf = signal.wait(GOT_LINE, time_ms);
|
||||
if (got_lf && result == command_result::TIMEOUT) {
|
||||
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
command_cb.wait_for_line(time_ms);
|
||||
command_cb.set(nullptr);
|
||||
buffer.consumed = 0;
|
||||
primary_term->set_read_cb(nullptr);
|
||||
return result;
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
inflatable.deflate();
|
||||
#endif
|
||||
return command_cb.result;
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
|
||||
@ -76,12 +141,22 @@ bool DTE::exit_cmux()
|
||||
if (!cmux_term->deinit()) {
|
||||
return false;
|
||||
}
|
||||
exit_cmux_internal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DTE::exit_cmux_internal()
|
||||
{
|
||||
if (!cmux_term) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ejected = cmux_term->detach();
|
||||
// return the ejected terminal and buffer back to this DTE
|
||||
primary_term = std::move(ejected.first);
|
||||
buffer = std::move(ejected.second);
|
||||
secondary_term = primary_term;
|
||||
return true;
|
||||
set_command_callbacks();
|
||||
}
|
||||
|
||||
bool DTE::setup_cmux()
|
||||
@ -90,14 +165,21 @@ bool DTE::setup_cmux()
|
||||
if (cmux_term == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cmux_term->init()) {
|
||||
exit_cmux_internal();
|
||||
cmux_term = nullptr;
|
||||
return false;
|
||||
}
|
||||
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
|
||||
if (primary_term == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
primary_term = std::make_unique<CMuxInstance>(cmux_term, 0);
|
||||
secondary_term = std::make_unique<CMuxInstance>(cmux_term, 1);
|
||||
if (primary_term == nullptr || secondary_term == nullptr) {
|
||||
exit_cmux_internal();
|
||||
cmux_term = nullptr;
|
||||
return false;
|
||||
}
|
||||
set_command_callbacks();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -119,6 +201,7 @@ bool DTE::set_mode(modem_mode m)
|
||||
if (mode == modem_mode::CMUX_MODE || mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::DUAL_MODE) {
|
||||
// mode stays the same, but need to swap terminals (as command has been switched)
|
||||
secondary_term.swap(primary_term);
|
||||
set_command_callbacks();
|
||||
} else {
|
||||
mode = m;
|
||||
}
|
||||
@ -161,6 +244,7 @@ bool DTE::set_mode(modem_mode m)
|
||||
// manual CMUX transitions: Swap terminals
|
||||
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
|
||||
secondary_term.swap(primary_term);
|
||||
set_command_callbacks();
|
||||
return true;
|
||||
}
|
||||
mode = modem_mode::UNDEF;
|
||||
@ -169,6 +253,10 @@ bool DTE::set_mode(modem_mode m)
|
||||
|
||||
void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
|
||||
{
|
||||
if (f == nullptr) {
|
||||
set_command_callbacks();
|
||||
return;
|
||||
}
|
||||
on_data = std::move(f);
|
||||
secondary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
if (!data) { // if no data available from terminal callback -> need to explicitly read some
|
||||
@ -230,6 +318,41 @@ void DTE::on_read(got_line_cb on_read_cb)
|
||||
});
|
||||
}
|
||||
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
|
||||
{
|
||||
if (memchr(data + consumed, separator, len)) {
|
||||
result = got_line(data, consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
signal.set(GOT_LINE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DTE::command_cb::wait_for_line(uint32_t time_ms)
|
||||
{
|
||||
auto got_lf = signal.wait(command_cb::GOT_LINE, time_ms);
|
||||
if (got_lf && result == command_result::TIMEOUT) {
|
||||
ESP_MODEM_THROW_IF_ERROR(ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
return got_lf;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
void DTE::extra_buffer::grow(size_t need_size)
|
||||
{
|
||||
if (need_size == 0) {
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
} else if (buffer == nullptr) {
|
||||
buffer = new std::vector<uint8_t>(need_size);
|
||||
} else {
|
||||
buffer->resize(need_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implemented here to keep all headers C++11 compliant
|
||||
*/
|
||||
|
@ -65,9 +65,7 @@ esp_err_t Netif::esp_modem_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
|
||||
void Netif::receive(uint8_t *data, size_t len)
|
||||
{
|
||||
if (signal.is_any(PPP_STARTED)) {
|
||||
esp_netif_receive(driver.base.netif, data, len, nullptr);
|
||||
}
|
||||
esp_netif_receive(driver.base.netif, data, len, nullptr);
|
||||
}
|
||||
|
||||
Netif::Netif(std::shared_ptr<DTE> e, esp_netif_t *ppp_netif) :
|
||||
@ -89,8 +87,8 @@ void Netif::start()
|
||||
receive(data, len);
|
||||
return true;
|
||||
});
|
||||
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
|
||||
signal.set(PPP_STARTED);
|
||||
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void Netif::stop()
|
||||
|
@ -64,7 +64,6 @@ public:
|
||||
|
||||
void set_read_cb(std::function<bool(uint8_t *data, size_t len)> f) override
|
||||
{
|
||||
ESP_MODEM_THROW_IF_FALSE(signal.wait(TASK_PARAMS, 1000), "Failed to set UART task params");
|
||||
on_read = std::move(f);
|
||||
}
|
||||
|
||||
@ -91,7 +90,6 @@ private:
|
||||
static const size_t TASK_INIT = BIT0;
|
||||
static const size_t TASK_START = BIT1;
|
||||
static const size_t TASK_STOP = BIT2;
|
||||
static const size_t TASK_PARAMS = BIT3;
|
||||
|
||||
QueueHandle_t event_queue;
|
||||
uart_resource uart;
|
||||
@ -118,9 +116,7 @@ void UartTerminal::task()
|
||||
return; // exits to the static method where the task gets deleted
|
||||
}
|
||||
while (signal.is_any(TASK_START)) {
|
||||
signal.set(TASK_PARAMS);
|
||||
if (get_event(event, 100)) {
|
||||
signal.clear(TASK_PARAMS);
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
uart_get_buffered_data_len(uart.port, &len);
|
||||
|
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/esp_mqtt_cxx:
|
||||
version: "^0.1.0"
|
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/esp_mqtt_cxx:
|
||||
version: "^0.1.0"
|
8
components/esp_websocket_client/.cz.yaml
Normal file
8
components/esp_websocket_client/.cz.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(websocket): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_websocket_client
|
||||
tag_format: websocket-v$version
|
||||
version: 1.1.0
|
||||
version_files:
|
||||
- idf_component.yml
|
@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Added linux port for websocket ([a22391a](https://github.com/espressif/esp-protocols/commit/a22391a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- added idf_component.yml for examples ([d273e10](https://github.com/espressif/esp-protocols/commit/d273e10))
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/websocket-v1.0.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- esp_websocket_client client allow sending 0 byte packets ([b5177cb](https://github.com/espressif/esp-protocols/commit/b5177cb))
|
||||
- Cleaned up printf/format warnings (-Wno-format) ([e085826](https://github.com/espressif/esp-protocols/commit/e085826))
|
||||
- Added unit tests to CI + minor fix to pass it ([c974c14](https://github.com/espressif/esp-protocols/commit/c974c14))
|
||||
- Reintroduce missing CHANGELOGs ([200cbb3](https://github.com/espressif/esp-protocols/commit/200cbb3), [#235](https://github.com/espressif/esp-protocols/issues/235))
|
||||
|
||||
### Updated
|
||||
|
||||
- docs(common): updated component and example links ([f48d9b2](https://github.com/espressif/esp-protocols/commit/f48d9b2))
|
||||
- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0))
|
||||
- Fix weird error message spacings ([8bb207e](https://github.com/espressif/esp-protocols/commit/8bb207e))
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/996fef7)
|
||||
|
||||
### Updated
|
||||
|
@ -1,3 +1,5 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
message(STATUS "Websocket transport is disabled so the esp_websocket_client component will not be built")
|
||||
# note: the component is still included in the build so it can become visible again in config
|
||||
@ -6,7 +8,14 @@ if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "esp_websocket_client.c"
|
||||
if(${IDF_TARGET} STREQUAL "linux")
|
||||
idf_component_register(SRCS "esp_websocket_client.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp-tls tcp_transport http_parser esp_event nvs_flash esp_stubs json
|
||||
PRIV_REQUIRES esp_timer)
|
||||
else()
|
||||
idf_component_register(SRCS "esp_websocket_client.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES lwip esp-tls tcp_transport http_parser
|
||||
PRIV_REQUIRES esp_timer esp_event)
|
||||
endif()
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_tls_crypto.h"
|
||||
#include "esp_system.h"
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static const char *TAG = "websocket_client";
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(COMPONENTS esp_websocket_client main)
|
||||
set(common_component_dir ../../../../common_components)
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
../../..
|
||||
"${common_component_dir}/linux_compat/esp_timer"
|
||||
"${common_component_dir}/linux_compat"
|
||||
"${common_component_dir}/linux_compat/freertos")
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(websocket)
|
@ -0,0 +1,14 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES esp_websocket_client protocol_examples_common)
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
|
||||
idf_component_get_property(esp_websocket_client esp_websocket_client COMPONENT_LIB)
|
||||
|
||||
target_compile_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
endif()
|
@ -0,0 +1,15 @@
|
||||
menu "Host-test config"
|
||||
|
||||
config GCOV_ENABLED
|
||||
bool "Coverage analyzer"
|
||||
default n
|
||||
help
|
||||
Enables coverage analyzing for host tests.
|
||||
|
||||
config WEBSOCKET_URI
|
||||
string "Websocket endpoint URI"
|
||||
default "ws://echo.websocket.events"
|
||||
help
|
||||
URL of websocket endpoint this example connects to and sends echo
|
||||
|
||||
endmenu
|
122
components/esp_websocket_client/examples/linux/main/main.c
Normal file
122
components/esp_websocket_client/examples/linux/main/main.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_websocket_client.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
static const char *TAG = "websocket";
|
||||
|
||||
static void log_error_if_nonzero(const char *message, int error_code)
|
||||
{
|
||||
if (error_code != 0) {
|
||||
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
||||
switch (event_id) {
|
||||
case WEBSOCKET_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
|
||||
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
|
||||
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
|
||||
}
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
|
||||
ESP_LOGI(TAG, "Received opcode=%d", data->op_code);
|
||||
if (data->op_code == 0x08 && data->data_len == 2) {
|
||||
ESP_LOGW(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr);
|
||||
}
|
||||
|
||||
// If received data contains json structure it succeed to parse
|
||||
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);
|
||||
|
||||
break;
|
||||
case WEBSOCKET_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
|
||||
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
|
||||
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void websocket_app_start(void)
|
||||
{
|
||||
esp_websocket_client_config_t websocket_cfg = {};
|
||||
|
||||
websocket_cfg.uri = CONFIG_WEBSOCKET_URI;
|
||||
|
||||
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);
|
||||
|
||||
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
|
||||
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client);
|
||||
|
||||
esp_websocket_client_start(client);
|
||||
char data[32];
|
||||
int i = 0;
|
||||
while (i < 1) {
|
||||
if (esp_websocket_client_is_connected(client)) {
|
||||
int len = sprintf(data, "hello %04d", i++);
|
||||
ESP_LOGI(TAG, "Sending %s", data);
|
||||
esp_websocket_client_send_text(client, data, len, portMAX_DELAY);
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
esp_websocket_client_destroy(client);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
ESP_LOGI(TAG, "[APP] Startup..");
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
|
||||
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
esp_log_level_set("websocket_client", ESP_LOG_DEBUG);
|
||||
esp_log_level_set("transport_ws", ESP_LOG_DEBUG);
|
||||
esp_log_level_set("trans_tcp", ESP_LOG_DEBUG);
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
websocket_app_start();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
CONFIG_GCOV_ENABLED=y
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_IDF_TARGET_LINUX=y
|
||||
CONFIG_ESP_EVENT_POST_FROM_ISR=n
|
||||
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n
|
||||
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
|
||||
CONFIG_WEBSOCKET_URI_FROM_STRING=y
|
||||
CONFIG_WEBSOCKET_URI_FROM_STDIN=n
|
@ -0,0 +1,8 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_IDF_TARGET_LINUX=y
|
||||
CONFIG_ESP_EVENT_POST_FROM_ISR=n
|
||||
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n
|
||||
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
|
||||
CONFIG_WEBSOCKET_URI_FROM_STRING=y
|
||||
CONFIG_WEBSOCKET_URI_FROM_STDIN=n
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
@ -2,9 +2,9 @@
|
||||
# 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")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "../../../../common_components/protocol_examples_common")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(websocket_example)
|
@ -20,4 +20,11 @@ menu "Example Configuration"
|
||||
help
|
||||
URL of websocket endpoint this example connects to and sends echo
|
||||
|
||||
if CONFIG_IDF_TARGET = "linux"
|
||||
config GCOV_ENABLED
|
||||
bool "Coverage analyzer"
|
||||
default n
|
||||
help
|
||||
Enables coverage analyzing for host tests.
|
||||
endif
|
||||
endmenu
|
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/esp_websocket_client:
|
||||
version: "^1.0.0"
|
@ -1,3 +1,5 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_IDF_TARGET_LINUX=n
|
||||
CONFIG_WEBSOCKET_URI_FROM_STDIN=y
|
||||
CONFIG_WEBSOCKET_URI_FROM_STRING=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
@ -1,4 +1,4 @@
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
description: esp websocket client
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client
|
||||
dependencies:
|
||||
|
@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(mdns): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py mdns
|
||||
tag_format: mdns-v$version
|
||||
version: 1.2.0
|
||||
version: 1.2.1
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## [1.2.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.1)
|
||||
|
||||
### Features
|
||||
|
||||
- Allow setting length of mDNS action queue in menuconfig ([28cd898](https://github.com/espressif/esp-protocols/commit/28cd898))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix build issue if CONFIG_ESP_WIFI_ENABLED disabled ([24f7031](https://github.com/espressif/esp-protocols/commit/24f7031))
|
||||
- added idf_component.yml for examples ([d273e10](https://github.com/espressif/esp-protocols/commit/d273e10))
|
||||
- added guard check for null pointer ([71bb461](https://github.com/espressif/esp-protocols/commit/71bb461))
|
||||
|
||||
## [1.2.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.0)
|
||||
|
||||
### Features
|
||||
|
@ -27,6 +27,13 @@ menu "mDNS"
|
||||
higher than priorities of system tasks. Compile time warning/error
|
||||
would be emitted if the chosen task priority were too high.
|
||||
|
||||
config MDNS_ACTION_QUEUE_LEN
|
||||
int "Maximum actions pending to the server"
|
||||
range 8 64
|
||||
default 16
|
||||
help
|
||||
Allows setting the length of mDNS action queue.
|
||||
|
||||
config MDNS_TASK_STACK_SIZE
|
||||
int "mDNS task stack size"
|
||||
default 4096
|
||||
|
5
components/mdns/examples/main/idf_component.yml
Normal file
5
components/mdns/examples/main/idf_component.yml
Normal file
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=5.0"
|
||||
espressif/mdns:
|
||||
version: "^1.0.0"
|
@ -1,4 +1,4 @@
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
description: mDNS
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
|
||||
dependencies:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -4157,6 +4157,7 @@ void mdns_preset_if_handle_system_event(void *arg, esp_event_base_t event_base,
|
||||
}
|
||||
|
||||
esp_netif_dhcp_status_t dcst;
|
||||
#if CONFIG_ESP_WIFI_ENABLED
|
||||
if (event_base == WIFI_EVENT) {
|
||||
switch (event_id) {
|
||||
case WIFI_EVENT_STA_CONNECTED:
|
||||
@ -4180,51 +4181,52 @@ void mdns_preset_if_handle_system_event(void *arg, esp_event_base_t event_base,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#if CONFIG_ETH_ENABLED
|
||||
else if (event_base == ETH_EVENT) {
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_ETH), &dcst)) {
|
||||
if (dcst == ESP_NETIF_DHCP_STOPPED) {
|
||||
if (event_base == ETH_EVENT) {
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_ETH), &dcst)) {
|
||||
if (dcst == ESP_NETIF_DHCP_STOPPED) {
|
||||
post_mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (event_base == IP_EVENT) {
|
||||
switch (event_id) {
|
||||
case IP_EVENT_STA_GOT_IP:
|
||||
post_mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
|
||||
post_mdns_announce_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
#if CONFIG_ETH_ENABLED
|
||||
case IP_EVENT_ETH_GOT_IP:
|
||||
post_mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
break;
|
||||
#endif
|
||||
case IP_EVENT_GOT_IP6: {
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data;
|
||||
mdns_if_t mdns_if = _mdns_get_if_from_esp_netif(event->esp_netif);
|
||||
if (mdns_if < MDNS_MAX_INTERFACES) {
|
||||
post_mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6);
|
||||
post_mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (event_base == IP_EVENT) {
|
||||
switch (event_id) {
|
||||
case IP_EVENT_STA_GOT_IP:
|
||||
post_mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
|
||||
post_mdns_announce_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
#if CONFIG_ETH_ENABLED
|
||||
case IP_EVENT_ETH_GOT_IP:
|
||||
post_mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
break;
|
||||
#endif
|
||||
case IP_EVENT_GOT_IP6: {
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data;
|
||||
mdns_if_t mdns_if = _mdns_get_if_from_esp_netif(event->esp_netif);
|
||||
if (mdns_if < MDNS_MAX_INTERFACES) {
|
||||
post_mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6);
|
||||
post_mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH */
|
||||
|
||||
@ -5357,7 +5359,7 @@ static inline void set_default_duplicated_interfaces(void)
|
||||
|
||||
static inline void unregister_predefined_handlers(void)
|
||||
{
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP
|
||||
#if defined(CONFIG_ESP_WIFI_ENABLED) && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event);
|
||||
#endif
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
@ -5454,7 +5456,7 @@ esp_err_t mdns_init(void)
|
||||
goto free_queue;
|
||||
}
|
||||
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP
|
||||
#if defined(CONFIG_ESP_WIFI_ENABLED) && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event, NULL)) != ESP_OK) {
|
||||
goto free_event_handlers;
|
||||
}
|
||||
@ -5850,7 +5852,7 @@ static mdns_txt_item_t *_copy_mdns_txt_items(mdns_txt_linked_item_t *items, uint
|
||||
return ret;
|
||||
|
||||
handle_error:
|
||||
for (size_t y = 0; y < ret_index + 1; y++) {
|
||||
for (size_t y = 0; y < ret_index + 1 && ret != NULL; y++) {
|
||||
mdns_txt_item_t *t = &ret[y];
|
||||
free((char *)t->key);
|
||||
free((char *)t->value);
|
||||
|
@ -93,7 +93,7 @@
|
||||
#define MDNS_SERVICE_ADD_TIMEOUT_MS CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS
|
||||
|
||||
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
|
||||
#define MDNS_ACTION_QUEUE_LEN 16 // Maximum actions pending to the server
|
||||
#define MDNS_ACTION_QUEUE_LEN CONFIG_MDNS_ACTION_QUEUE_LEN // Maximum actions pending to the server
|
||||
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
|
||||
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
|
||||
#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" )
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -290,6 +290,7 @@
|
||||
#define CONFIG_MDNS_MAX_SERVICES 25
|
||||
#define CONFIG_MDNS_MAX_INTERFACES 3
|
||||
#define CONFIG_MDNS_TASK_PRIORITY 1
|
||||
#define CONFIG_MDNS_ACTION_QUEUE_LEN 16
|
||||
#define CONFIG_MDNS_TASK_STACK_SIZE 4096
|
||||
#define CONFIG_MDNS_TASK_AFFINITY_CPU0 1
|
||||
#define CONFIG_MDNS_TASK_AFFINITY 0x0
|
||||
|
@ -828,7 +828,9 @@ INPUT = \
|
||||
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_types.hpp \
|
||||
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_terminal.hpp \
|
||||
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_cmux.hpp \
|
||||
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce.hpp
|
||||
$(PROJECT_PATH)/../components/esp_modem/include/cxx_include/esp_modem_dce.hpp \
|
||||
$(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_api_commands.h \
|
||||
$(PROJECT_PATH)/../docs/esp_modem/en/esp_modem_dce.hpp
|
||||
|
||||
|
||||
# The last two are generated
|
||||
|
Reference in New Issue
Block a user