mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-16 20:12:13 +02:00
fix(modem): AT-only example: initial adaptation to BG96
Plus rework a little to support implementation of multiple devices
This commit is contained in:
committed by
David Cermak
parent
7547267336
commit
ceedcfca23
@ -1,5 +1,11 @@
|
|||||||
|
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||||
|
set(device_srcs sock_commands_bg96.cpp)
|
||||||
|
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
||||||
|
set(device_srcs sock_commands_sim7600.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS "modem_client.cpp"
|
idf_component_register(SRCS "modem_client.cpp"
|
||||||
"sock_dce.cpp"
|
"sock_dce.cpp"
|
||||||
"sock_commands.cpp"
|
"${device_srcs}"
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS ".")
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||||
|
@ -5,11 +5,6 @@ menu "Example Configuration"
|
|||||||
default EXAMPLE_MODEM_DEVICE_BG96
|
default EXAMPLE_MODEM_DEVICE_BG96
|
||||||
help
|
help
|
||||||
Select modem device connected to the ESP DTE.
|
Select modem device connected to the ESP DTE.
|
||||||
config EXAMPLE_MODEM_DEVICE_SIM800
|
|
||||||
bool "SIM800"
|
|
||||||
help
|
|
||||||
SIMCom SIM800L is a GSM/GPRS module.
|
|
||||||
It supports Quad-band 850/900/1800/1900MHz.
|
|
||||||
config EXAMPLE_MODEM_DEVICE_BG96
|
config EXAMPLE_MODEM_DEVICE_BG96
|
||||||
bool "BG96"
|
bool "BG96"
|
||||||
help
|
help
|
||||||
@ -26,53 +21,6 @@ menu "Example Configuration"
|
|||||||
help
|
help
|
||||||
Set APN (Access Point Name), a logical name to choose data network
|
Set APN (Access Point Name), a logical name to choose data network
|
||||||
|
|
||||||
config EXAMPLE_MODEM_PPP_AUTH_USERNAME
|
|
||||||
string "Set username for authentication"
|
|
||||||
default "espressif"
|
|
||||||
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
|
|
||||||
help
|
|
||||||
Set username for PPP Authentication.
|
|
||||||
|
|
||||||
config EXAMPLE_MODEM_PPP_AUTH_PASSWORD
|
|
||||||
string "Set password for authentication"
|
|
||||||
default "esp32"
|
|
||||||
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
|
|
||||||
help
|
|
||||||
Set password for PPP Authentication.
|
|
||||||
|
|
||||||
config EXAMPLE_MODEM_PPP_AUTH_NONE
|
|
||||||
bool "Skip PPP authentication"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Set to true for the PPP client to skip authentication
|
|
||||||
|
|
||||||
config EXAMPLE_SEND_MSG
|
|
||||||
bool "Short message (SMS)"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Select this, the modem will send a short message before power off.
|
|
||||||
|
|
||||||
if EXAMPLE_SEND_MSG
|
|
||||||
config EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER
|
|
||||||
string "Peer Phone Number (with area code)"
|
|
||||||
default "+8610086"
|
|
||||||
help
|
|
||||||
Enter the peer phone number that you want to send message to.
|
|
||||||
endif
|
|
||||||
|
|
||||||
config EXAMPLE_NEED_SIM_PIN
|
|
||||||
bool "SIM PIN needed"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Enable to set SIM PIN before starting the example
|
|
||||||
|
|
||||||
config EXAMPLE_SIM_PIN
|
|
||||||
string "Set SIM PIN"
|
|
||||||
default "1234"
|
|
||||||
depends on EXAMPLE_NEED_SIM_PIN
|
|
||||||
help
|
|
||||||
Pin to unlock the SIM
|
|
||||||
|
|
||||||
menu "UART Configuration"
|
menu "UART Configuration"
|
||||||
config EXAMPLE_MODEM_UART_TX_PIN
|
config EXAMPLE_MODEM_UART_TX_PIN
|
||||||
int "TXD Pin Number"
|
int "TXD Pin Number"
|
||||||
|
@ -97,7 +97,7 @@ extern "C" void app_main(void)
|
|||||||
assert(dte);
|
assert(dte);
|
||||||
|
|
||||||
/* Configure the DCE */
|
/* Configure the DCE */
|
||||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
|
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("lpwa.vodafone.com");
|
||||||
|
|
||||||
/* create the DCE and initialize network manually (using AT commands) */
|
/* create the DCE and initialize network manually (using AT commands) */
|
||||||
auto dce = sock_dce::create(&dce_config, std::move(dte));
|
auto dce = sock_dce::create(&dce_config, std::move(dte));
|
||||||
@ -106,10 +106,10 @@ extern "C" void app_main(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dce->init(8883);
|
dce->init(1883);
|
||||||
esp_mqtt_client_config_t mqtt_config = {};
|
esp_mqtt_client_config_t mqtt_config = {};
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
#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.uri = "mqtt://127.0.0.1";
|
||||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||||
#else
|
#else
|
||||||
mqtt_config.uri = "mqtt://127.0.0.1";
|
mqtt_config.uri = "mqtt://127.0.0.1";
|
||||||
@ -118,7 +118,7 @@ extern "C" void app_main(void)
|
|||||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
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_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);
|
esp_mqtt_client_start(mqtt_client);
|
||||||
if (!dce->start(BROKER_URL, 8883)) {
|
if (!dce->start(BROKER_URL, 1883)) {
|
||||||
ESP_LOGE(TAG, "Failed to start DCE");
|
ESP_LOGE(TAG, "Failed to start DCE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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"
|
||||||
|
|
||||||
|
static const char *TAG = "sock_commands";
|
||||||
|
|
||||||
|
namespace sock_commands {
|
||||||
|
|
||||||
|
using namespace esp_modem;
|
||||||
|
|
||||||
|
command_result net_open(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
std::string out;
|
||||||
|
auto ret = dce_commands::generic_get_string(t, "AT+QISTATE?\r", out, 1000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (out.find("+QISTATE: 0") != std::string::npos) {
|
||||||
|
ESP_LOGV(TAG, "%s", out.data() );
|
||||||
|
ESP_LOGD(TAG, "Already there");
|
||||||
|
return command_result::OK;
|
||||||
|
} else if (out.empty()) {
|
||||||
|
return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000);
|
||||||
|
}
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result net_close(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000);
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
std::string ip_open = R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
|
||||||
|
auto ret = dce_commands::generic_command(t, ip_open, "+QIOPEN: 0,0", "ERROR", timeout);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGE(TAG, "%s Failed", __func__ );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_close(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
return dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
assert(0); // Remove when fix done
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_recv(CommandableIf *t, uint8_t *data, size_t len, size_t &out_len)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
assert(0); // Remove when fix done
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result get_ip(CommandableIf *t, std::string &ip)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__ );
|
||||||
|
std::string out;
|
||||||
|
auto ret = dce_commands::generic_get_string(t, "AT+QIACT?\r", out, 5000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
auto pos = out.find("+QIACT: 1");
|
||||||
|
auto property = 0;
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
// Looking for: +QIACT: <contextID>,<context_state>,<context_type>,<IP_address>
|
||||||
|
if (property++ == 3) { // ip is after 3rd comma (as a 4rd property of QIACT string)
|
||||||
|
ip = out.substr(++pos);
|
||||||
|
// strip quotes if present
|
||||||
|
auto quote1 = ip.find('"');
|
||||||
|
auto quote2 = ip.rfind('"');
|
||||||
|
if (quote1 != std::string::npos && quote2 != std::string::npos) {
|
||||||
|
ip = ip.substr(quote1 + 1, quote2 - 1);
|
||||||
|
}
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
pos = out.find(',', ++pos);
|
||||||
|
}
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // sock_commands
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
void Listener::start_sending(size_t len)
|
||||||
|
{
|
||||||
|
data_to_send = len;
|
||||||
|
send_stat = 0;
|
||||||
|
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listener::start_receiving(size_t len)
|
||||||
|
{
|
||||||
|
send_cmd("AT+QIRD=0," + std::to_string(size) + "\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Listener::start_connecting(std::string host, int port)
|
||||||
|
{
|
||||||
|
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::recv(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
const size_t MIN_MESSAGE = 6;
|
||||||
|
const std::string_view head = "+QIRD: ";
|
||||||
|
auto head_pos = (char *)std::search(data, data + len, head.begin(), head.end());
|
||||||
|
if (head_pos == nullptr) {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
|
||||||
|
if (next_nl == nullptr) {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t actual_len;
|
||||||
|
if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) {
|
||||||
|
ESP_LOGE(TAG, "cannot convert");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
|
||||||
|
if (actual_len == 0) {
|
||||||
|
ESP_LOGD(TAG, "no data received");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO improve : compare *actual_len* & data size (to be sure that received data is equal to *actual_len*)
|
||||||
|
if (actual_len > size) {
|
||||||
|
ESP_LOGE(TAG, "TOO BIG");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
::send(sock, next_nl + 1, actual_len, 0);
|
||||||
|
|
||||||
|
// "OK" after the data
|
||||||
|
auto last_pos = (char *)memchr(next_nl + 1 + actual_len, 'O', MIN_MESSAGE);
|
||||||
|
if (last_pos == nullptr || last_pos[1] != 'K') {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
if ((char *)data + len - last_pos > MIN_MESSAGE) {
|
||||||
|
// check for async replies after the Recv header
|
||||||
|
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos);
|
||||||
|
check_async_replies(response);
|
||||||
|
}
|
||||||
|
return state::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Listener::state Listener::send(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (send_stat == 0) {
|
||||||
|
if (memchr(data, '>', len) == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Missed >");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
auto written = dte->write(&buffer[0], data_to_send);
|
||||||
|
if (written != data_to_send) {
|
||||||
|
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
data_to_send = 0;
|
||||||
|
send_stat++;
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::send(std::string_view response)
|
||||||
|
{
|
||||||
|
if (send_stat == 1) {
|
||||||
|
if (response.find("SEND OK") != std::string::npos) {
|
||||||
|
send_cmd("AT+QISEND=0,0\r");
|
||||||
|
send_stat++;
|
||||||
|
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Sending buffer full");
|
||||||
|
return state::FAIL;
|
||||||
|
} else if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to sent");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
} else if (send_stat == 2) {
|
||||||
|
constexpr std::string_view head = "+QISEND: ";
|
||||||
|
if (response.find(head) != std::string::npos) {
|
||||||
|
// Parsing +QISEND: <total_send_length>,<ackedbytes>,<unackedbytes>
|
||||||
|
size_t head_pos = response.find(head);
|
||||||
|
response = response.substr(head_pos + head.size());
|
||||||
|
int pos, property = 0;
|
||||||
|
int total = 0, ack = 0, unack = 0;
|
||||||
|
while ((pos = response.find(',')) != std::string::npos) {
|
||||||
|
auto next_comma = (char *)memchr(response.data(), ',', response.size());
|
||||||
|
|
||||||
|
// extract value
|
||||||
|
size_t value;
|
||||||
|
if (std::from_chars(response.data(), next_comma, value).ec == std::errc::invalid_argument) {
|
||||||
|
ESP_LOGE(TAG, "cannot convert");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (property++) {
|
||||||
|
case 0: total = value;
|
||||||
|
break;
|
||||||
|
case 1: ack = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
response = response.substr(pos + 1);
|
||||||
|
}
|
||||||
|
if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO improve : need check *total* & *ack* values, or loop (every 5 sec) with 90s or 120s timeout
|
||||||
|
if (ack < total) {
|
||||||
|
ESP_LOGE(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
|
||||||
|
}
|
||||||
|
return state::OK;
|
||||||
|
} else if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to check sending");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::connect(std::string_view response)
|
||||||
|
{
|
||||||
|
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
|
||||||
|
ESP_LOGI(TAG, "Connected!");
|
||||||
|
return state::OK;
|
||||||
|
}
|
||||||
|
if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listener::check_async_replies(std::string_view &response) const
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||||
|
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
|
||||||
|
uint64_t data_ready = 1;
|
||||||
|
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||||
|
ESP_LOGD(TAG, "Got data on modem!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // sock_dce
|
@ -8,6 +8,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "sock_commands.hpp"
|
#include "sock_commands.hpp"
|
||||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||||
|
#include "sock_dce.hpp"
|
||||||
|
|
||||||
namespace sock_commands {
|
namespace sock_commands {
|
||||||
|
|
||||||
@ -181,6 +182,175 @@ command_result set_rx_mode(CommandableIf *term, int mode)
|
|||||||
return dce_commands::generic_command(term, "AT+CIPRXGET=" + std::to_string(mode) + "\r", "OK", "ERROR", 5000);
|
return dce_commands::generic_command(term, "AT+CIPRXGET=" + std::to_string(mode) + "\r", "OK", "ERROR", 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // sock_commands
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
void Listener::start_sending(size_t len)
|
||||||
|
{
|
||||||
|
data_to_send = len;
|
||||||
|
send_stat = 0;
|
||||||
|
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listener::start_receiving(size_t len)
|
||||||
|
{
|
||||||
|
send_cmd("AT+CIPRXGET=2,0," + std::to_string(size) + "\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Listener::start_connecting(std::string host, int port)
|
||||||
|
{
|
||||||
|
if (esp_modem::dce_commands::generic_command(dte.get(), "AT+CIPRXGET=1\r", "OK", "ERROR", 5000) != esp_modem::command_result::OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::recv(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
const int MIN_MESSAGE = 6;
|
||||||
|
size_t actual_len = 0;
|
||||||
|
auto *recv_data = (char *)data;
|
||||||
|
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) {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
// state = status::RECEIVING_FAILED;
|
||||||
|
// signal.set(IDLE);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
if (head_pos - (char *)data > MIN_MESSAGE) {
|
||||||
|
// check for async replies before the Recv header
|
||||||
|
std::string_view response((char *)data, head_pos - (char *)data);
|
||||||
|
check_async_replies(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
||||||
|
if (next_comma == nullptr) {
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
|
||||||
|
ESP_LOGE(TAG, "cannot convert");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */);
|
||||||
|
if (next_nl == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "not found");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
if (actual_len > size) {
|
||||||
|
ESP_LOGE(TAG, "TOO BIG");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
size_t 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 state::FAIL;
|
||||||
|
}
|
||||||
|
read_again = (total_len > 0);
|
||||||
|
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);
|
||||||
|
data_to_recv = actual_len - first_data_len;
|
||||||
|
return state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
::send(sock, recv_data, actual_len, 0);
|
||||||
|
} else if (data_to_recv > len) { // continue sending
|
||||||
|
::send(sock, recv_data, len, 0);
|
||||||
|
data_to_recv -= len;
|
||||||
|
return state::IN_PROGRESS;
|
||||||
|
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
||||||
|
::send(sock, recv_data, data_to_recv, 0);
|
||||||
|
actual_len = data_to_recv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "OK" after the data
|
||||||
|
char *last_pos = nullptr;
|
||||||
|
if (actual_len + 1 + 2 /* OK */ > len) {
|
||||||
|
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
|
||||||
|
if (last_pos == nullptr || last_pos[1] != 'K') {
|
||||||
|
data_to_recv = 0;
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (last_pos != nullptr && (char *)data + len - last_pos > MIN_MESSAGE) {
|
||||||
|
// check for async replies after the Recv header
|
||||||
|
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2);
|
||||||
|
check_async_replies(response);
|
||||||
|
}
|
||||||
|
data_to_recv = 0;
|
||||||
|
if (read_again) {
|
||||||
|
uint64_t data_ready = 1;
|
||||||
|
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||||
|
}
|
||||||
|
return state::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::send(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (send_stat == 0) {
|
||||||
|
if (memchr(data, '>', len) == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Missed >");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
auto written = dte->write(&buffer[0], data_to_send);
|
||||||
|
if (written != data_to_send) {
|
||||||
|
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
data_to_send = 0;
|
||||||
|
uint8_t ctrl_z = '\x1A';
|
||||||
|
dte->write(&ctrl_z, 1);
|
||||||
|
send_stat++;
|
||||||
|
return state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::send(std::string_view response)
|
||||||
|
{
|
||||||
|
if (send_stat == 1) {
|
||||||
|
if (response.find("+CIPSEND:") != std::string::npos) {
|
||||||
|
send_stat = 0;
|
||||||
|
return state::OK;
|
||||||
|
}
|
||||||
|
if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to sent");
|
||||||
|
send_stat = 0;
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener::state Listener::connect(std::string_view response)
|
||||||
|
{
|
||||||
|
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
|
||||||
|
ESP_LOGI(TAG, "Connected!");
|
||||||
|
return state::OK;
|
||||||
|
}
|
||||||
|
if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open");
|
||||||
|
return state::FAIL;
|
||||||
|
}
|
||||||
|
return Listener::state::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listener::check_async_replies(std::string_view &response) const
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||||
|
if (response.find("+CIPRXGET: 1") != std::string::npos) {
|
||||||
|
uint64_t data_ready = 1;
|
||||||
|
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||||
|
ESP_LOGD(TAG, "Got data on modem!");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // sock_dce
|
@ -68,9 +68,10 @@ void DCE::forwarding(uint8_t *data, size_t len)
|
|||||||
signal.set(IDLE);
|
signal.set(IDLE);
|
||||||
return;
|
return;
|
||||||
case Listener::state::IN_PROGRESS:
|
case Listener::state::IN_PROGRESS:
|
||||||
return;
|
break;
|
||||||
|
// return;
|
||||||
}
|
}
|
||||||
} else if (state == status::RECEIVING || state == status::RECEIVING_1 ) {
|
} else if (state == status::RECEIVING) {
|
||||||
switch (at.recv(data, len)) {
|
switch (at.recv(data, len)) {
|
||||||
case Listener::state::OK:
|
case Listener::state::OK:
|
||||||
state = status::IDLE;
|
state = status::IDLE;
|
||||||
@ -81,6 +82,7 @@ void DCE::forwarding(uint8_t *data, size_t len)
|
|||||||
signal.set(IDLE);
|
signal.set(IDLE);
|
||||||
return;
|
return;
|
||||||
case Listener::state::IN_PROGRESS:
|
case Listener::state::IN_PROGRESS:
|
||||||
|
// break;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,6 +125,15 @@ void DCE::close_sock()
|
|||||||
close(sock);
|
close(sock);
|
||||||
sock = -1;
|
sock = -1;
|
||||||
}
|
}
|
||||||
|
const int retries = 5;
|
||||||
|
int i = 0;
|
||||||
|
while (net_close() != esp_modem::command_result::OK) {
|
||||||
|
if (i++ > retries) {
|
||||||
|
ESP_LOGE(TAG, "Failed to close network");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
esp_modem::Task::Delay(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DCE::at_to_sock()
|
bool DCE::at_to_sock()
|
||||||
@ -141,7 +152,7 @@ bool DCE::at_to_sock()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
state = status::RECEIVING;
|
state = status::RECEIVING;
|
||||||
send_cmd("AT+CIPRXGET=2,0," + std::to_string(size) + "\r");
|
at.start_receiving(size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,8 +181,7 @@ bool DCE::sock_to_at()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ESP_LOG_BUFFER_HEXDUMP(TAG, &buffer[0], len, ESP_LOG_VERBOSE);
|
ESP_LOG_BUFFER_HEXDUMP(TAG, &buffer[0], len, ESP_LOG_VERBOSE);
|
||||||
data_to_send = len;
|
at.start_sending(len);
|
||||||
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,30 +248,19 @@ void DCE::init(int port)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Listener::check_async_replies(std::string_view &response) const
|
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
|
||||||
if (response.find("+CIPRXGET: 1") != std::string::npos) {
|
|
||||||
uint64_t data_ready = 1;
|
|
||||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
|
||||||
ESP_LOGD(TAG, "Got data on modem!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DCE::start(std::string host, int port)
|
bool DCE::start(std::string host, int port)
|
||||||
{
|
{
|
||||||
dte->on_read(nullptr);
|
dte->on_read(nullptr);
|
||||||
tcp_close();
|
tcp_close();
|
||||||
if (set_rx_mode(1) != esp_modem::command_result::OK) {
|
|
||||||
ESP_LOGE(TAG, "Unable to set Rx mode");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dte->on_read([this](uint8_t *data, size_t len) {
|
dte->on_read([this](uint8_t *data, size_t len) {
|
||||||
this->forwarding(data, len);
|
this->forwarding(data, len);
|
||||||
return esp_modem::command_result::TIMEOUT;
|
return esp_modem::command_result::TIMEOUT;
|
||||||
});
|
});
|
||||||
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
if (!at.start_connecting(host, port)) {
|
||||||
|
ESP_LOGE(TAG, "Unable to start connecting");
|
||||||
|
dte->on_read(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
state = status::CONNECTING;
|
state = status::CONNECTING;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -345,139 +344,6 @@ DECLARE_SOCK_COMMANDS(return_type name(...) )
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Listener::state Listener::recv(uint8_t *data, size_t len)
|
|
||||||
{
|
|
||||||
const int MIN_MESSAGE = 6;
|
|
||||||
size_t actual_len = 0;
|
|
||||||
auto *recv_data = (char *)data;
|
|
||||||
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) {
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
// state = status::RECEIVING_FAILED;
|
|
||||||
// signal.set(IDLE);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
if (head_pos - (char *)data > MIN_MESSAGE) {
|
|
||||||
// check for async replies before the Recv header
|
|
||||||
std::string_view response((char *)data, head_pos - (char *)data);
|
|
||||||
check_async_replies(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
|
||||||
if (next_comma == nullptr) {
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
|
|
||||||
ESP_LOGE(TAG, "cannot convert");
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto next_nl = (char *)memchr(next_comma, '\n', 8 /* total_len size (~4) + markers */);
|
|
||||||
if (next_nl == nullptr) {
|
|
||||||
ESP_LOGE(TAG, "not found");
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
if (actual_len > size) {
|
|
||||||
ESP_LOGE(TAG, "TOO BIG");
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
size_t 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 state::FAIL;
|
|
||||||
}
|
|
||||||
read_again = (total_len > 0);
|
|
||||||
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);
|
|
||||||
data_to_recv = actual_len - first_data_len;
|
|
||||||
return state::IN_PROGRESS;
|
|
||||||
}
|
|
||||||
::send(sock, recv_data, actual_len, 0);
|
|
||||||
} else if (data_to_recv > len) { // continue sending
|
|
||||||
::send(sock, recv_data, len, 0);
|
|
||||||
data_to_recv -= len;
|
|
||||||
return state::IN_PROGRESS;
|
|
||||||
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
|
|
||||||
::send(sock, recv_data, data_to_recv, 0);
|
|
||||||
actual_len = data_to_recv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// "OK" after the data
|
|
||||||
char *last_pos = nullptr;
|
|
||||||
if (actual_len + 1 + 2 /* OK */ > len) {
|
|
||||||
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
|
|
||||||
if (last_pos == nullptr || last_pos[1] != 'K') {
|
|
||||||
data_to_recv = 0;
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (last_pos != nullptr && (char *)data + len - last_pos > MIN_MESSAGE) {
|
|
||||||
// check for async replies after the Recv header
|
|
||||||
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos - 2);
|
|
||||||
check_async_replies(response);
|
|
||||||
}
|
|
||||||
data_to_recv = 0;
|
|
||||||
if (read_again) {
|
|
||||||
uint64_t data_ready = 1;
|
|
||||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
|
||||||
}
|
|
||||||
return state::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Listener::state Listener::send(uint8_t *data, size_t len)
|
|
||||||
{
|
|
||||||
if (send_stat == 0) {
|
|
||||||
if (memchr(data, '>', len) == NULL) {
|
|
||||||
ESP_LOGE(TAG, "Missed >");
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
auto written = dte->write(&buffer[0], data_to_send);
|
|
||||||
if (written != data_to_send) {
|
|
||||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
data_to_send = 0;
|
|
||||||
uint8_t ctrl_z = '\x1A';
|
|
||||||
dte->write(&ctrl_z, 1);
|
|
||||||
send_stat++;
|
|
||||||
return state::IN_PROGRESS;
|
|
||||||
}
|
|
||||||
return Listener::state::IN_PROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Listener::state Listener::send(std::string_view response)
|
|
||||||
{
|
|
||||||
if (send_stat == 1) {
|
|
||||||
if (response.find("+CIPSEND:") != std::string::npos) {
|
|
||||||
send_stat = 0;
|
|
||||||
return state::OK;
|
|
||||||
}
|
|
||||||
if (response.find("ERROR") != std::string::npos) {
|
|
||||||
ESP_LOGE(TAG, "Failed to sent");
|
|
||||||
send_stat = 0;
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Listener::state::IN_PROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Listener::state Listener::connect(std::string_view response)
|
|
||||||
{
|
|
||||||
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
|
|
||||||
ESP_LOGI(TAG, "Connected!");
|
|
||||||
return state::OK;
|
|
||||||
}
|
|
||||||
if (response.find("ERROR") != std::string::npos) {
|
|
||||||
ESP_LOGE(TAG, "Failed to open");
|
|
||||||
return state::FAIL;
|
|
||||||
}
|
|
||||||
return Listener::state::IN_PROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace sock_dce
|
} // namespace sock_dce
|
||||||
|
@ -28,7 +28,15 @@ public:
|
|||||||
state send(std::string_view response);
|
state send(std::string_view response);
|
||||||
state connect(std::string_view response);
|
state connect(std::string_view response);
|
||||||
void check_async_replies(std::string_view &response) const;
|
void check_async_replies(std::string_view &response) const;
|
||||||
|
|
||||||
|
void start_sending(size_t len);
|
||||||
|
void start_receiving(size_t len);
|
||||||
|
bool start_connecting(std::string host, int port);
|
||||||
private:
|
private:
|
||||||
|
void send_cmd(std::string_view command)
|
||||||
|
{
|
||||||
|
dte->write((uint8_t *) command.begin(), command.size());
|
||||||
|
}
|
||||||
std::array<uint8_t, size> &buffer;
|
std::array<uint8_t, size> &buffer;
|
||||||
size_t data_to_recv = 0;
|
size_t data_to_recv = 0;
|
||||||
bool read_again = false;
|
bool read_again = false;
|
||||||
@ -67,31 +75,19 @@ private:
|
|||||||
|
|
||||||
void forwarding(uint8_t *data, size_t len);
|
void forwarding(uint8_t *data, size_t len);
|
||||||
|
|
||||||
// void check_async_replies(std::string_view &response) const;
|
|
||||||
|
|
||||||
void send_cmd(std::string_view command)
|
|
||||||
{
|
|
||||||
dte->write((uint8_t *) command.begin(), command.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class status {
|
enum class status {
|
||||||
IDLE,
|
IDLE,
|
||||||
CONNECTING,
|
CONNECTING,
|
||||||
CONNECTION_FAILED,
|
CONNECTION_FAILED,
|
||||||
SENDING,
|
SENDING,
|
||||||
SENDING_1,
|
|
||||||
SENDING_FAILED,
|
SENDING_FAILED,
|
||||||
RECEIVING,
|
RECEIVING,
|
||||||
RECEIVING_1,
|
|
||||||
RECEIVING_FAILED
|
RECEIVING_FAILED
|
||||||
};
|
};
|
||||||
status state{status::IDLE};
|
status state{status::IDLE};
|
||||||
static constexpr uint8_t IDLE = 1;
|
static constexpr uint8_t IDLE = 1;
|
||||||
std::array<uint8_t, size> buffer;
|
std::array<uint8_t, size> buffer;
|
||||||
Listener at{buffer, sock, data_ready_fd, dte};
|
Listener at{buffer, sock, data_ready_fd, dte};
|
||||||
size_t data_to_send = 0;
|
|
||||||
// size_t data_to_recv = 0;
|
|
||||||
bool read_again = false;
|
|
||||||
int sock {-1};
|
int sock {-1};
|
||||||
int listen_sock {-1};
|
int listen_sock {-1};
|
||||||
int data_ready_fd {-1};
|
int data_ready_fd {-1};
|
||||||
|
Reference in New Issue
Block a user