Compare commits

..

10 Commits

Author SHA1 Message Date
b78569860d Merge pull request #438 from espressif-abhikroy/components/console_cmd_ping
bump(console): First version [1.0.0]
2023-11-30 08:19:43 +01:00
27f955abfa bump(console): First version [1.0.0]
1.0.0
Features
- Added ping command to console component (7babdeb9)
2023-11-30 18:14:59 +11:00
909e8d9494 Merge pull request #441 from david-cermak/fix/apply_examples_idf_component_refs
fix(common): Re-applied refs to common comps idf_component.yml
2023-11-28 17:44:13 +01:00
0c6bb4bedc Merge pull request #360 from david-cermak/fix/modem_example_mbedtls_wrapper
fix(modem): TLS example: Add restore session support in mbedtls wrapper
2023-11-28 17:12:10 +01:00
afbca5343e Merge pull request #416 from david-cermak/fix/modem_undef_mode
fix(modem): Fixed mode transitions between any state and UNDEF
2023-11-28 17:11:43 +01:00
9fe44a4504 fix(common): Re-applie refs to common comps idf_component.yml
This reverts commit 74fc228cdf.
This reverts commit b176d3abbb.
This reverts commit f9e0281a04.
2023-11-28 09:32:52 +01:00
79d38e54f2 fix(modem): TLS example: Added restore session support in mbedtls-wrap
Reusable component in modem_tcp_client example implements a simple
mbedtls wrapper. This update add support for mbedtls deinit() and for
saving and restoring TLS session.
2023-11-23 09:56:01 +01:00
5396de42d0 docs(modem): Added documentation about mode transitions
Closes https://github.com/espressif/esp-protocols/issues/318
2023-11-07 12:35:06 +01:00
aff571df2c fix(modem): Added test-cases that exercise mode transitions 2023-11-07 12:35:06 +01:00
93cb2caadb fix(modem): Fixed mode transitions between any state and UNDEF
Closes https://github.com/espressif/esp-protocols/issues/320
2023-11-07 12:35:00 +01:00
26 changed files with 265 additions and 42 deletions

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS ../.. $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(asio_chat)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(async_http_request)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(asio_sock4)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,9 +2,6 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
set(EXCLUDE_COMPONENTS openssl)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(asio_tcp_echo_server)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,9 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(asio_udp_echo_server)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/asio:
version: "^1.14.1"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

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

View File

@ -0,0 +1,7 @@
# Changelog
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.0.0)
### Features
- Added ping command to console component ([7babdeb9](https://github.com/espressif/esp-protocols/commit/7babdeb9))

View File

@ -1,4 +1,4 @@
version: 0.0.9
version: 1.0.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ping
description: The component provides a console where the 'ping' command can be executed.
dependencies:

View File

@ -235,7 +235,9 @@ extern "C" void app_main(void)
if (c->get_count_of(&SetModeArgs::mode)) {
auto mode = c->get_string_of(&SetModeArgs::mode);
modem_mode dev_mode;
if (mode == "CMUX1") {
if (mode == "UNDEF") {
dev_mode = esp_modem::modem_mode::UNDEF;
} else if (mode == "CMUX1") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_MODE;
} else if (mode == "CMUX2") {
dev_mode = esp_modem::modem_mode::CMUX_MANUAL_EXIT;

View File

@ -6,6 +6,7 @@
#pragma once
#include <utility>
#include <memory>
#include <span>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
@ -22,6 +23,7 @@ public:
Tls();
virtual ~Tls();
bool init(is_server server, do_verify verify);
bool deinit();
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
@ -41,6 +43,11 @@ protected:
mbedtls_entropy_context entropy_{};
virtual void delay() {}
bool set_session();
bool get_session();
void reset_session();
bool is_session_loaded();
private:
static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
@ -48,5 +55,21 @@ private:
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);
struct unique_session {
unique_session()
{
::mbedtls_ssl_session_init(&s);
}
~unique_session()
{
::mbedtls_ssl_session_free(&s);
}
mbedtls_ssl_session *ptr()
{
return &s;
}
mbedtls_ssl_session s;
};
std::unique_ptr<unique_session> session_;
};

View File

@ -35,6 +35,16 @@ bool Tls::init(is_server server, do_verify verify)
return true;
}
bool Tls::deinit()
{
::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_);
return true;
}
void Tls::print_error(const char *function, int error_code)
{
static char error_buf[100];
@ -132,3 +142,39 @@ Tls::~Tls()
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
}
bool Tls::get_session()
{
if (session_ == nullptr) {
session_ = std::make_unique<unique_session>();
}
int ret = ::mbedtls_ssl_get_session(&ssl_, session_->ptr());
if (ret != 0) {
print_error("mbedtls_ssl_get_session() failed", ret);
return false;
}
return true;
}
bool Tls::set_session()
{
if (session_ == nullptr) {
printf("session hasn't been initialized");
return false;
}
int ret = mbedtls_ssl_set_session(&ssl_, session_->ptr());
if (ret != 0) {
print_error("mbedtls_ssl_set_session() failed", ret);
return false;
}
return true;
}
void Tls::reset_session()
{
session_.reset(nullptr);
}
bool Tls::is_session_loaded()
{
return session_ != nullptr;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -96,6 +96,11 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
{
switch (m) {
case modem_mode::UNDEF:
if (!dte->set_mode(m)) {
return false;
}
mode = m;
return true;
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
break;
case modem_mode::COMMAND_MODE:
@ -151,7 +156,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
mode = modem_mode::CMUX_MANUAL_MODE;
return true;
case modem_mode::CMUX_MANUAL_EXIT:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
if (!dte->set_mode(m)) {
@ -160,7 +165,7 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
mode = modem_mode::COMMAND_MODE;
return true;
case modem_mode::CMUX_MANUAL_SWAP:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
if (!dte->set_mode(m)) {
@ -168,12 +173,12 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
}
return true;
case modem_mode::CMUX_MANUAL_DATA:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
return transitions::enter_data(*dte, *device, netif);
case modem_mode::CMUX_MANUAL_COMMAND:
if (mode != modem_mode::CMUX_MANUAL_MODE) {
if (mode != modem_mode::CMUX_MANUAL_MODE && mode != modem_mode::UNDEF) {
return false;
}
return transitions::exit_data(*dte, *device, netif);

View File

@ -151,10 +151,14 @@ command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32
bool DTE::exit_cmux()
{
if (!cmux_term) {
return false;
}
if (!cmux_term->deinit()) {
return false;
}
exit_cmux_internal();
cmux_term.reset();
return true;
}
@ -174,6 +178,10 @@ void DTE::exit_cmux_internal()
bool DTE::setup_cmux()
{
if (cmux_term) {
ESP_LOGE("esp_modem_dte", "Cannot setup_cmux(), cmux_term already exists");
return false;
}
cmux_term = std::make_shared<CMux>(primary_term, std::move(buffer));
if (cmux_term == nullptr) {
return false;
@ -198,6 +206,11 @@ bool DTE::setup_cmux()
bool DTE::set_mode(modem_mode m)
{
// transitions (any) -> UNDEF
if (m == modem_mode::UNDEF) {
mode = m;
return true;
}
// transitions (COMMAND|UNDEF) -> CMUX
if (m == modem_mode::CMUX_MODE) {
if (mode == modem_mode::UNDEF || mode == modem_mode::COMMAND_MODE) {
@ -246,7 +259,7 @@ bool DTE::set_mode(modem_mode m)
return false;
}
// manual CMUX transitions: Exit CMUX
if (m == modem_mode::CMUX_MANUAL_EXIT && mode == modem_mode::CMUX_MANUAL_MODE) {
if (m == modem_mode::CMUX_MANUAL_EXIT && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
if (exit_cmux()) {
mode = modem_mode::COMMAND_MODE;
return true;
@ -255,7 +268,7 @@ bool DTE::set_mode(modem_mode m)
return false;
}
// manual CMUX transitions: Swap terminals
if (m == modem_mode::CMUX_MANUAL_SWAP && mode == modem_mode::CMUX_MANUAL_MODE) {
if (m == modem_mode::CMUX_MANUAL_SWAP && (mode == modem_mode::CMUX_MANUAL_MODE || mode == modem_mode::UNDEF)) {
secondary_term.swap(primary_term);
set_command_callbacks();
return true;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -247,3 +247,88 @@ TEST_CASE("Test CMUX protocol by injecting payloads", "[esp_modem]")
CHECK(ret == command_result::OK);
}
}
TEST_CASE("Command and Data mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto loopback = term.get();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// UNDEF -> CMD (OK)
uint8_t resp[] = "DISCONNECTED\n";
loopback->inject(&resp[0], sizeof(resp), sizeof(resp), /* 10ms before injecting reply */100, 0);
loopback->write(nullptr, 0); /* this triggers sending the injected response */
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
loopback->inject(nullptr, 0, 0, 0, 0); /* reset injection, use synchronous replies now */
// CMD -> CMD (Fail)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == false);
// Forcing transition to CMD (via UNDEF)
// CMD -> UNDEF (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
// UNDEF -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
// CMD -> DATA (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == true);
// DATA -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("CMUX mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// UNDEF -> CMUX (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MODE) == true);
// CMUX -> DATA (Fail)
CHECK(dce->set_mode(esp_modem::modem_mode::DATA_MODE) == false);
// CMUX back -> CMD (OK)
CHECK(dce->set_mode(esp_modem::modem_mode::COMMAND_MODE) == true);
}
TEST_CASE("CMUX manual mode transitions", "[esp_modem][transitions]")
{
auto term = std::make_unique<LoopbackTerm>();
auto dte = std::make_shared<DTE>(std::move(term));
CHECK(term == nullptr);
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
esp_netif_t netif{};
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
CHECK(dce != nullptr);
// Happy flow transitions of Manual CMUX transitions
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true);
// Cannot test CMUX_MANUAL_DATA -> CMUX_MANUAL_COMMAND with our mocked terminal for now
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true);
// Check some out of order manual transitions, most of them are allowed,
// but some fail as modem layers report issues with specific steps
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == false); // cannot go directly to SWAP
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_SWAP) == true); // can go via UNDEF
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == false); // EXIT is allowed, but CMUX terms don't exist
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true);
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_MODE) == true); // Enter CMUX (via UNDEF)
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_DATA) == true); // Go directly to DATA mode
CHECK(dce->set_mode(esp_modem::modem_mode::CMUX_MANUAL_EXIT) == true); // Exit CMUX
CHECK(dce->set_mode(esp_modem::modem_mode::UNDEF) == true); // Succeeds from any state
}

View File

@ -8,7 +8,6 @@ set(EXTRA_COMPONENT_DIRS
"${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)

View File

@ -0,0 +1,3 @@
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -2,8 +2,5 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(websocket_example)

View File

@ -4,3 +4,5 @@ dependencies:
espressif/esp_websocket_client:
version: "^1.0.0"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -81,16 +81,68 @@ Common use cases of the esp-modem are also listed as the examples:
- ``examples/modem_console`` is an example to exercise all possible module commands in a console application.
- ``examples/ap_to_pppos`` this example focuses on the network connectivity of the esp-modem and provides a WiFi AP that forwards packets (and uses NAT) to and from the PPPoS connection.
Working modes
~~~~~~~~~~~~~
Modem devices could work in multiple different modes, esp-modem library
uses these states to describe them:
- Standard modes:
- Command mode -- This mode is used for sending AT commands
- Data or PPP mode -- This mode is used for data communication (to create PPPoS tunnel between the device and the library)
- Multiplexing modes:
- CMUX mode -- This mode creates two virtual channels and uses one for sending AT commands and the other one for data communication.
- DUAL mode -- This mode uses two physical channels the same way as CMUX. This mode is supported only by certain devices, usually with USB interface.
- Manual CMUX modes -- These modes are designed for applications to have better control over CMUX mode transitions. It allows setting up the virtual channels,
switching between channels, transitioning between data and command modes for each channel separately, and exiting the CMUX.
Switching between common modes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The diagram below depicts allowed transitions between the most common modes
::
+---------+ +---------+
| COMMAND |<-->| DATA |
+---------+ +---------+
^
|
v
+-------+
| CMUX |
+-------+
Note that it is possible to switch from any mode to the "UNDEF" mode and vice-versa.
Switching between manual modes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The diagram below depicts allowed transitions between manual CMUX modes
::
+------------------------------------
| |
+----------+ +-------------+ +------------+ +----------+
| |<-->| MANUAL_DATA |<-->| MANUAL_CMD |<-->| COMMAND |
| CMUX | +-------------+ +------------+ | (CMUX |
| MANUAL | | | MANUAL |
| | +-------------+ | EXIT) |
| |<-->| MANUAL_SWAP |<-------------------->| |
+----------+ +-------------+ +----------+
| |
+-----------------------------------------------------+
Note that transitioning between "MANUAL_DATA" and "MANUAL_CMD" switches the secondary terminal (dedicated to PPP session) and could be used for recovering data communication if PPP session gets dropped.
Extensibility
-------------
CMUX
~~~~
Implementation of virtual terminals is an experimental feature, which
allows users to also issue commands in the data mode, after creating
multiple virtual terminals, designating some of them solely to data
mode, others solely to command mode.
Implements virtual terminals which allow users to also issue commands in the data mode;
after creating two virtual terminals, designating one of them solely to data mode, and
another one solely to command mode.
DTE
~~~