Compare commits

..

7 Commits

Author SHA1 Message Date
13b29c2291 Merge pull request #349 from gabsuren/mdns/update_versioin_1_2_1
bump(mdns): 1.2.0 -> 1.2.1
2023-09-07 13:24:52 +04:00
0cb1156480 bump(mdns): 1.2.0 -> 1.2.1
1.2.1
Features
- Allow setting length of mDNS action queue in menuconfig (28cd898)
Bug Fixes
- fix build issue if CONFIG_ESP_WIFI_ENABLED disabled (24f7031)
- added idf_component.yml for examples (d273e10)
- added guard check for null pointer (71bb461)
2023-08-29 16:57:18 +04:00
6c7259fa7a Merge pull request #336 from gabsuren/mdns/fix_build_issue
fix(mdns): fix build issue if CONFIG_ESP_WIFI_ENABLED disabled (IDFGH-9699)
2023-08-29 14:15:01 +04:00
24f7031012 fix(mdns): fix build issue if CONFIG_ESP_WIFI_ENABLED disabled 2023-08-29 13:41:26 +04:00
62d809ac60 Merge pull request #309 from david-cermak/feat/modem_tcp_client_work
feat(modem): Support custom transport in AT TCP client example
2023-08-28 13:43:46 +02:00
d4925f2bd6 fix(modem): Per review comments 2023-08-17 13:17:20 +02:00
ae629ed3a9 feat(modem): Support custom transport in AT TCP client example 2023-08-17 13:11:51 +02:00
23 changed files with 680 additions and 85 deletions

View File

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

View File

@ -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 "../..")

View File

@ -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`.

View File

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

View File

@ -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`).

View File

@ -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);
};

View File

@ -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);

View File

@ -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_);
}

View File

@ -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_);
}

View File

@ -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")

View File

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

View File

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

View File

@ -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__);

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

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

View File

@ -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;
}

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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;
}