Compare commits

...

7 Commits

15 changed files with 164 additions and 49 deletions

View File

@ -42,7 +42,7 @@ idf_component_register(SRCS "${srcs}"
set_target_properties(${COMPONENT_LIB} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)

View File

@ -28,9 +28,9 @@ namespace dce_commands {
* @param fail_phrase String to be present in the reply to fail this command
* @param timeout_ms Timeout in ms
*/
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms);
command_result generic_command(CommandableIf *t, std::string_view command,
std::string_view pass_phrase,
std::string_view fail_phrase, uint32_t timeout_ms);
/**
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc
*/

View File

@ -496,8 +496,8 @@ command_result SQNGM02S::connect(PdpContext &pdp)
return res;
}
//wait for +CEREG: 5 or +CEREG: 1.
const auto pass = std::list<std::string_view>({"+CEREG: 1", "+CEREG: 5"});
const auto fail = std::list<std::string_view>({"ERROR"});
const std::string_view pass[] {"+CEREG: 1", "+CEREG: 5"};
const std::string_view fail[] {"ERROR"};
res = esp_modem::dce_commands::generic_command(dte.get(), "", pass, fail, 1200000);
if (res != command_result::OK) {
config_network_registration_urc(0);

View File

@ -31,9 +31,9 @@ namespace dce_commands {
* @param fail_phrase String to be present in the reply to fail this command
* @param timeout_ms Timeout in ms
*/
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms);
command_result generic_command(CommandableIf *t, std::string_view command,
std::string_view pass_phrase,
std::string_view fail_phrase, uint32_t timeout_ms);
/**
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc

View File

@ -10,8 +10,8 @@
#include "cxx_include/esp_modem_dce_module.hpp"
namespace esp_modem::dce_commands {
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
command_result generic_command(CommandableIf *t, std::string_view command,
std::span<const std::string_view> pass_phrase,
std::span<const std::string_view> fail_phrase,
uint32_t timeout_ms);
}

View File

@ -96,6 +96,8 @@ public:
*/
bool recover();
bool isInBullshitState() { if (m_isInBullshitState) { m_isInBullshitState = false; return true; } return false; }
private:
enum class protocol_mismatch_reason {
@ -128,6 +130,9 @@ private:
bool on_footer(CMuxFrame &frame);
void recover_protocol(protocol_mismatch_reason reason);
//! When espressif again fucks up the recovering of the MUX
bool m_isInBullshitState{};
std::function<bool(uint8_t *data, size_t len)> read_cb[MAX_TERMINALS_NUM]; /*!< Function pointers to read callbacks */
std::shared_ptr<Terminal> term; /*!< The original terminal */
cmux_state state; /*!< CMux protocol state */

View File

@ -6,6 +6,9 @@
#pragma once
#include <string_view>
#include <span>
namespace esp_modem::dce_commands {
/**
@ -17,9 +20,9 @@ namespace esp_modem::dce_commands {
* @param timeout_ms Command timeout in ms
* @return Generic command return type (OK, FAIL, TIMEOUT)
*/
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms);
command_result generic_command(CommandableIf *t, std::string_view command,
std::string_view pass_phrase,
std::string_view fail_phrase, uint32_t timeout_ms);
/**
* @brief Utility command to send command and return reply (after DCE says OK)
@ -29,7 +32,7 @@ command_result generic_command(CommandableIf *t, const std::string &command,
* @param timeout_ms Command timeout in ms
* @return Generic command return type (OK, FAIL, TIMEOUT)
*/
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms = 500);
template <typename T> command_result generic_get_string(CommandableIf *t, std::string_view command, T &output, uint32_t timeout_ms = 500);
/**
* @brief Generic command that passes on "OK" and fails on "ERROR"
@ -38,6 +41,6 @@ template <typename T> command_result generic_get_string(CommandableIf *t, const
* @param timeout_ms Command timeout in ms
* @return Generic command return type (OK, FAIL, TIMEOUT)
*/
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms = 500);
command_result generic_command_common(CommandableIf *t, std::string_view command, uint32_t timeout_ms = 500);
} // esp_modem::dce_commands

View File

@ -131,12 +131,12 @@ public:
* @param time_ms Time in ms to wait for the answer
* @return OK, FAIL, TIMEOUT
*/
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) override;
command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms) override;
/**
* @brief Sends the command (same as above) but with a specific separator
*/
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms, char separator) override;
/**
* @brief Allows this DTE to recover from a generic connection issue
@ -151,6 +151,8 @@ public:
*/
void set_command_callbacks();
bool isInBullshitState();
protected:
/**
* @brief Allows for locking the DTE

View File

@ -90,8 +90,8 @@ public:
* @param separator Character treated as a line separator, typically '\n'
* @return OK, FAIL or TIMEOUT
*/
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) = 0;
virtual command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
virtual command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms) = 0;
virtual int write(uint8_t *data, size_t len) = 0;
virtual void on_read(got_line_cb on_data) = 0;

View File

@ -202,6 +202,7 @@ bool CMux::on_recovery(CMuxFrame &frame)
frame.ptr = recover_ptr;
state = cmux_state::INIT;
ESP_LOGI("CMUX", "Protocol recovered");
m_isInBullshitState = true;
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
// empty frame
frame.advance();

View File

@ -5,7 +5,8 @@
*/
#include <charconv>
#include <list>
#include <span>
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/esp_modem_dce_module.hpp"
@ -18,12 +19,12 @@ namespace esp_modem::dce_commands {
static const char *TAG = "command_lib";
command_result generic_command(CommandableIf *t, const std::string &command,
const std::list<std::string_view> &pass_phrase,
const std::list<std::string_view> &fail_phrase,
command_result generic_command(CommandableIf *t, std::string_view command,
std::span<const std::string_view> pass_phrase,
std::span<const std::string_view> fail_phrase,
uint32_t timeout_ms)
{
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
ESP_LOGD(TAG, "%s command %.*s\n", __func__, command.size(), command.data());
return t->command(command, [&](uint8_t *data, size_t len) {
std::string_view response((char *)data, len);
if (data == nullptr || len == 0 || response.empty()) {
@ -43,13 +44,13 @@ command_result generic_command(CommandableIf *t, const std::string &command,
}
command_result generic_command(CommandableIf *t, const std::string &command,
const std::string &pass_phrase,
const std::string &fail_phrase, uint32_t timeout_ms)
command_result generic_command(CommandableIf *t, std::string_view command,
std::string_view pass_phrase,
std::string_view fail_phrase, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__);
const auto pass = std::list<std::string_view>({pass_phrase});
const auto fail = std::list<std::string_view>({fail_phrase});
ESP_LOGV(TAG, "%s", __func__ );
const std::string_view pass[] {pass_phrase};
const std::string_view fail[] {fail_phrase};
return generic_command(t, command, pass, fail, timeout_ms);
}
@ -81,7 +82,7 @@ bool set(std::span<char> &dest, std::string_view &src)
} // str_copy
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms)
template <typename T> command_result generic_get_string(CommandableIf *t, std::string_view command, T &output, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__);
return t->command(command, [&](uint8_t *data, size_t len) {
@ -110,7 +111,7 @@ template <typename T> command_result generic_get_string(CommandableIf *t, const
}, timeout_ms);
}
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms)
command_result generic_command_common(CommandableIf *t, std::string_view command, uint32_t timeout_ms)
{
ESP_LOGV(TAG, "%s", __func__);
return generic_command(t, command, "OK", "ERROR", timeout_ms);
@ -245,7 +246,7 @@ command_result get_operator_name(CommandableIf *t, std::string &operator_name, i
{
ESP_LOGV(TAG, "%s", __func__);
std::string out;
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
auto ret = generic_get_string(t, "AT+COPS?\r", out, 1000);
if (ret != command_result::OK) {
return ret;
}
@ -314,9 +315,9 @@ command_result resume_data_mode(CommandableIf *t)
command_result set_command_mode(CommandableIf *t)
{
ESP_LOGV(TAG, "%s", __func__);
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
const auto fail = std::list<std::string_view>({"ERROR"});
ESP_LOGV(TAG, "%s", __func__ );
const std::string_view pass[] {"NO CARRIER", "OK"};
const std::string_view fail[] {"ERROR"};
return generic_command(t, "+++", pass, fail, 5000);
}

View File

@ -147,11 +147,11 @@ void DTE::set_command_callbacks()
}
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
command_result DTE::command(std::string_view command, got_line_cb got_line, uint32_t time_ms, const char separator)
{
Scoped<Lock> l1(internal_lock);
command_cb.set(got_line, separator);
primary_term->write((uint8_t *)command.c_str(), command.length());
primary_term->write((uint8_t *)command.data(), command.length());
command_cb.wait_for_line(time_ms);
command_cb.set(nullptr);
buffer.consumed = 0;
@ -161,7 +161,7 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui
return command_cb.result;
}
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
command_result DTE::command(std::string_view cmd, got_line_cb got_line, uint32_t time_ms)
{
return command(cmd, got_line, time_ms, '\n');
}
@ -394,6 +394,13 @@ bool DTE::recover()
return false;
}
bool DTE::isInBullshitState()
{
if (!cmux_term)
return false;
return cmux_term->isInBullshitState();
}
void DTE::handle_error(terminal_error err)
{
if (err == terminal_error::BUFFER_OVERFLOW ||

View File

@ -226,7 +226,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
return esp_event_loop_run(client->event_handle, 0);
}
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type)
esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
@ -619,16 +619,24 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand
}
while (widx < len || opcode) { // allow for sending "current_opcode" only message with len==0
if (need_write > client->buffer_size) {
need_write = client->buffer_size;
if (need_write > client->buffer_size - MAX_WEBSOCKET_HEADER_SIZE) {
need_write = client->buffer_size - MAX_WEBSOCKET_HEADER_SIZE;
opcode = opcode & ~WS_TRANSPORT_OPCODES_FIN;
} else if (contained_fin) {
opcode = opcode | WS_TRANSPORT_OPCODES_FIN;
}
memcpy(client->tx_buffer, data + widx, need_write);
memcpy(client->tx_buffer + MAX_WEBSOCKET_HEADER_SIZE, data + widx, need_write);
// send with ws specific way and specific opcode
wlen = esp_transport_ws_send_raw(client->transport, opcode, (char *)client->tx_buffer, need_write,
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
wlen = esp_transport_ws_send_raw_optimized(client->transport, opcode, (char *)client->tx_buffer, need_write + MAX_WEBSOCKET_HEADER_SIZE,
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
if (wlen >= MAX_WEBSOCKET_HEADER_SIZE)
wlen -= MAX_WEBSOCKET_HEADER_SIZE;
else if (wlen > 0)
{
ESP_LOGW(TAG, "couldnt write enough for ws header, wlen=%d", wlen);
wlen = 0;
}
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
ret = wlen;
esp_websocket_free_buf(client, true);
@ -1174,6 +1182,17 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
}
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
{
esp_err_t result = esp_websocket_client_initiate_stop(client);
if (result != ESP_OK)
return result;
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
}
esp_err_t esp_websocket_client_initiate_stop(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
@ -1184,7 +1203,35 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
return ESP_FAIL;
}
return stop_wait_task(client);
/* A running client cannot be stopped from the websocket task/event handler */
TaskHandle_t running_task = xTaskGetCurrentTaskHandle();
if (running_task == client->task_handle) {
ESP_LOGE(TAG, "Client cannot be stopped from websocket task");
return ESP_FAIL;
}
client->run = false;
return ESP_OK;
}
bool esp_websocket_client_is_already_stopped(esp_websocket_client_handle_t client)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (client->run)
return false;
EventBits_t bits = xEventGroupClearBits(client->status_bits, 0);
if (bits & STOPPED_BIT)
{
client->state = WEBSOCKET_STATE_UNKNOW;
return true;
}
return false;
}
static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout)

View File

@ -215,6 +215,48 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client);
*/
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client);
/**
* @brief Initiate an asynchronus stop of the WebSocket connection without
* websocket closing handshake
*
* This API initiates a non-blocking asynchronus stop of ws client. This will
* close TCP connection later directly without sending close frames. This is
* method is similar to esp_websocket_client_stop() but does not block the calling
* task. It is required to then later periodically call
* esp_websocket_client_is_already_stopped() to find out when its done and to
* finalize the closing of the websocket client.
*
* Notes:
* - Cannot be called from the websocket event handler
*
* @param[in] client The client
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_initiate_stop(esp_websocket_client_handle_t client);
/**
* @brief After an asynchronus stop was initiated, this method returns if it is done already.
* If this method returns true, it also performed some final cleanup. It should then not
* be called anymore unless esp_websocket_client_initiate_stop() is called again.
*
* @param[in] client The client
*
* @return
* - bool if it is done or not
*/
bool esp_websocket_client_is_already_stopped(esp_websocket_client_handle_t client);
/**
* @brief Closes a websocket connection and causes a reconnect to happen
*
* @param[in] client The client
* @param[in] error_type The error type
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type);
/**
* @brief Destroy the WebSocket connection and free all resources.
* This function must be the last function to call for an session.

View File

@ -4,6 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "mosquitto.h"
#include "esp_tls.h"
@ -17,7 +20,7 @@ typedef void (*mosq_message_cb_t)(char *client, char *topic, char *data, int len
* structure.
*/
struct mosq_broker_config {
char *host; /*!< Address on which the broker is listening for connections */
const char *host; /*!< Address on which the broker is listening for connections */
int port; /*!< Port number of the broker to listen to */
esp_tls_cfg_server_t *tls_cfg; /*!< ESP-TLS configuration (if TLS transport used)
* Please refer to the ESP-TLS official documentation
@ -48,3 +51,7 @@ int mosq_broker_run(struct mosq_broker_config *config);
* @note After calling this API, function mosq_broker_run() unblocks and returns.
*/
void mosq_broker_stop(void);
#ifdef __cplusplus
}
#endif