forked from espressif/esp-protocols
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
e11e1136e1 | |||
1c2ee55459 | |||
3be87562ad | |||
a6fe9525e1 | |||
bd6bf6df26 | |||
5187d3f94e | |||
1d49013fc6 |
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 ||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user