mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-09-25 14:20:54 +02:00
Merge pull request #837 from david-cermak/feat/modem_tcp_client_extend
[modem]: Extend tcp-client example
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
|
set(module_dir "generic_module")
|
||||||
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
if (CONFIG_EXAMPLE_MODEM_DEVICE_BG96)
|
||||||
set(device_srcs sock_commands_bg96.cpp)
|
set(device_srcs sock_commands_bg96.cpp)
|
||||||
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
||||||
set(device_srcs sock_commands_sim7600.cpp)
|
set(device_srcs sock_commands_sim7600.cpp)
|
||||||
|
elseif(CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT)
|
||||||
|
set(device_srcs sock_commands_espat.cpp)
|
||||||
|
set(module_dir "espat_module")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
|
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
|
||||||
@@ -14,4 +18,4 @@ idf_component_register(SRCS "modem_client.cpp"
|
|||||||
"${command_dir}/sock_dce.cpp"
|
"${command_dir}/sock_dce.cpp"
|
||||||
"tcp_transport_at.cpp"
|
"tcp_transport_at.cpp"
|
||||||
"${device_srcs}"
|
"${device_srcs}"
|
||||||
INCLUDE_DIRS "." "${command_dir}")
|
INCLUDE_DIRS "." "${command_dir}" "${module_dir}")
|
||||||
|
@@ -18,8 +18,26 @@ menu "Example Configuration"
|
|||||||
bool "SIM7600"
|
bool "SIM7600"
|
||||||
help
|
help
|
||||||
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
|
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
|
||||||
|
config EXAMPLE_MODEM_DEVICE_ESPAT
|
||||||
|
bool "ESP-AT"
|
||||||
|
help
|
||||||
|
ESP-AT firmware for ESP32 modules with WiFi connectivity
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
if EXAMPLE_MODEM_DEVICE_ESPAT
|
||||||
|
config EXAMPLE_WIFI_SSID
|
||||||
|
string "WiFi SSID"
|
||||||
|
default "myssid"
|
||||||
|
help
|
||||||
|
SSID (network name) to connect to.
|
||||||
|
|
||||||
|
config EXAMPLE_WIFI_PASSWORD
|
||||||
|
string "WiFi Password"
|
||||||
|
default "mypassword"
|
||||||
|
help
|
||||||
|
WiFi password (WPA or WPA2).
|
||||||
|
endif
|
||||||
|
|
||||||
config EXAMPLE_MODEM_APN
|
config EXAMPLE_MODEM_APN
|
||||||
string "Set MODEM APN"
|
string "Set MODEM APN"
|
||||||
default "internet"
|
default "internet"
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "cxx_include/esp_modem_api.hpp"
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||||
#include "sock_commands.hpp"
|
#include "sock_commands.hpp"
|
||||||
|
#include "sock_module.hpp"
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -97,8 +98,8 @@ private:
|
|||||||
std::shared_ptr<esp_modem::DTE> &dte;
|
std::shared_ptr<esp_modem::DTE> &dte;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DCE : public ::esp_modem::GenericModule {
|
class DCE : public Module {
|
||||||
using esp_modem::GenericModule::GenericModule;
|
using Module::Module;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "esp_modem_config.h"
|
||||||
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
class Module: public esp_modem::GenericModule {
|
||||||
|
using esp_modem::GenericModule::GenericModule;
|
||||||
|
public:
|
||||||
|
|
||||||
|
esp_modem::command_result sync() override;
|
||||||
|
esp_modem::command_result set_echo(bool on) override;
|
||||||
|
esp_modem::command_result set_pdp_context(esp_modem::PdpContext &pdp) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -8,6 +8,7 @@
|
|||||||
#include "cxx_include/esp_modem_api.hpp"
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||||
#include "sock_commands.hpp"
|
#include "sock_commands.hpp"
|
||||||
|
#include "sock_module.hpp"
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -97,8 +98,8 @@ private:
|
|||||||
std::shared_ptr<esp_modem::DTE> &dte;
|
std::shared_ptr<esp_modem::DTE> &dte;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DCE : public ::esp_modem::GenericModule {
|
class DCE : public Module {
|
||||||
using esp_modem::GenericModule::GenericModule;
|
using Module::Module;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// --- ESP-MODEM command module starts here ---
|
// --- ESP-MODEM command module starts here ---
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "esp_modem_config.h"
|
||||||
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
using Module = esp_modem::GenericModule;
|
||||||
|
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
#include "tcp_transport_mbedtls.h"
|
#include "tcp_transport_mbedtls.h"
|
||||||
#include "tcp_transport_at.h"
|
#include "tcp_transport_at.h"
|
||||||
|
|
||||||
#define BROKER_URL "mqtt.eclipseprojects.io"
|
#define BROKER_URL "test.mosquitto.org"
|
||||||
#define BROKER_PORT 8883
|
#define BROKER_PORT 1883
|
||||||
|
|
||||||
|
|
||||||
static const char *TAG = "modem_client";
|
static const char *TAG = "modem_client";
|
||||||
@@ -114,7 +114,7 @@ extern "C" void app_main(void)
|
|||||||
mqtt_config.broker.address.port = BROKER_PORT;
|
mqtt_config.broker.address.port = BROKER_PORT;
|
||||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||||
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
|
mqtt_config.broker.address.uri = "mqtt://127.0.0.1";
|
||||||
dce->start_listening(BROKER_PORT);
|
dce->start_listening(BROKER_PORT);
|
||||||
#else
|
#else
|
||||||
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
|
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
|
||||||
@@ -124,14 +124,14 @@ extern "C" void app_main(void)
|
|||||||
mqtt_config.network.transport = ssl;
|
mqtt_config.network.transport = ssl;
|
||||||
#endif
|
#endif
|
||||||
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, nullptr);
|
||||||
esp_mqtt_client_start(mqtt_client);
|
esp_mqtt_client_start(mqtt_client);
|
||||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||||
ESP_LOGE(TAG, "Failed to start DCE");
|
ESP_LOGE(TAG, "Failed to start DCE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (1) {
|
while (true) {
|
||||||
while (dce->perform_sock()) {
|
while (dce->perform_sock()) {
|
||||||
ESP_LOGV(TAG, "...performing");
|
ESP_LOGV(TAG, "...performing");
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstring>
|
||||||
|
#include "sock_commands.hpp"
|
||||||
|
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||||
|
#include "sock_dce.hpp"
|
||||||
|
#include "sock_module.hpp"
|
||||||
|
|
||||||
|
static const char *TAG = "sock_commands_espat";
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
using namespace esp_modem;
|
||||||
|
|
||||||
|
command_result Module::sync()
|
||||||
|
{
|
||||||
|
return dce_commands::generic_command_common(dte.get(), "AT\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result Module::set_echo(bool on)
|
||||||
|
{
|
||||||
|
return dce_commands::generic_command_common(dte.get(), "ATE0\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result Module::set_pdp_context(PdpContext &pdp)
|
||||||
|
{
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sock_commands {
|
||||||
|
|
||||||
|
using namespace esp_modem;
|
||||||
|
|
||||||
|
command_result net_open(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__);
|
||||||
|
|
||||||
|
// Set WiFi mode to station
|
||||||
|
auto ret = dce_commands::generic_command(t, "AT+CWMODE=1\r\n", "OK", "ERROR", 5000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set WiFi mode");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to WiFi network
|
||||||
|
std::string wifi_cmd = "AT+CWJAP=\"" CONFIG_EXAMPLE_WIFI_SSID "\",\"" CONFIG_EXAMPLE_WIFI_PASSWORD "\"\r\n";
|
||||||
|
ret = dce_commands::generic_command(t, wifi_cmd, "OK", "ERROR", 15000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to connect to WiFi");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "WiFi connected successfully");
|
||||||
|
|
||||||
|
// Set passive receive mode (1) for better control
|
||||||
|
ret = set_rx_mode(t, 1);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set preferred Rx mode");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result net_close(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__);
|
||||||
|
// Disconnect from WiFi
|
||||||
|
auto ret = dce_commands::generic_command(t, "AT+CWQAP\r\n", "OK", "ERROR", 5000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGW(TAG, "Failed to disconnect WiFi (may already be disconnected)");
|
||||||
|
}
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__);
|
||||||
|
|
||||||
|
// Set single connection mode (just in case)
|
||||||
|
auto ret = dce_commands::generic_command(t, "AT+CIPMUX=0\r\n", "OK", "ERROR", 1000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGW(TAG, "Failed to set single connection mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish TCP connection
|
||||||
|
std::string tcp_cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||||
|
ret = dce_commands::generic_command(t, tcp_cmd, "CONNECT", "ERROR", timeout);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to establish TCP connection to %s:%d", host.c_str(), port);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "TCP connection established to %s:%d", host.c_str(), port);
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_close(CommandableIf *t)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__);
|
||||||
|
return dce_commands::generic_command(t, "AT+CIPCLOSE\r\n", "CLOSED", "ERROR", 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "%s", __func__);
|
||||||
|
// This function is not used in the current implementation
|
||||||
|
// Data sending is handled by the DCE responder
|
||||||
|
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__);
|
||||||
|
// This function is not used in the current implementation
|
||||||
|
// Data receiving is handled by the DCE responder
|
||||||
|
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::at_raw(t, "AT+CIFSR\r\n", out, "OK", "ERROR", 5000);
|
||||||
|
if (ret != command_result::OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse station IP from response
|
||||||
|
// Expected format: +CIFSR:STAIP,"192.168.1.100"
|
||||||
|
auto pos = out.find("+CIFSR:STAIP,\"");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
pos += 14; // Move past "+CIFSR:STAIP,\""
|
||||||
|
auto end_pos = out.find("\"", pos);
|
||||||
|
if (end_pos != std::string::npos) {
|
||||||
|
ip = out.substr(pos, end_pos - pos);
|
||||||
|
ESP_LOGI(TAG, "Got IP address: %s", ip.c_str());
|
||||||
|
return command_result::OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "Failed to parse IP address from response");
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_result set_rx_mode(CommandableIf *t, int mode)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "%s", __func__);
|
||||||
|
// Active mode (0) sends data automatically, Passive mode (1) notifies about data for reading
|
||||||
|
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(mode) + "\r\n";
|
||||||
|
return dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // sock_commands
|
||||||
|
|
||||||
|
namespace sock_dce {
|
||||||
|
|
||||||
|
void Responder::start_sending(size_t len)
|
||||||
|
{
|
||||||
|
data_to_send = len;
|
||||||
|
send_stat = 0;
|
||||||
|
send_cmd("AT+CIPSEND=" + std::to_string(len) + "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Responder::start_receiving(size_t len)
|
||||||
|
{
|
||||||
|
send_cmd("AT+CIPRECVDATA=" + std::to_string(len) + "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Responder::start_connecting(std::string host, int port)
|
||||||
|
{
|
||||||
|
std::string cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||||
|
send_cmd(cmd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::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) {
|
||||||
|
const std::string_view head = "+CIPRECVDATA:";
|
||||||
|
|
||||||
|
// Find the response header
|
||||||
|
auto head_pos = std::search(recv_data, recv_data + len, head.data(), head.data() + head.size(), [](char a, char b) {
|
||||||
|
return a == b;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (head_pos == recv_data + len) {
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the end of the length field
|
||||||
|
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
||||||
|
if (next_comma == nullptr) {
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the actual length
|
||||||
|
if (std::from_chars(head_pos + head.size(), next_comma, actual_len).ec == std::errc::invalid_argument) {
|
||||||
|
ESP_LOGE(TAG, "Cannot convert length");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Received: actual len=%zu", actual_len);
|
||||||
|
if (actual_len == 0) {
|
||||||
|
ESP_LOGD(TAG, "No data received");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual_len > buffer_size) {
|
||||||
|
ESP_LOGE(TAG, "Data too large: %zu > %zu", actual_len, buffer_size);
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the actual data after the comma
|
||||||
|
recv_data = next_comma + 1;
|
||||||
|
auto first_data_len = len - (recv_data - (char *)data);
|
||||||
|
|
||||||
|
if (actual_len > first_data_len) {
|
||||||
|
on_read(recv_data, first_data_len);
|
||||||
|
data_to_recv = actual_len - first_data_len;
|
||||||
|
return ret::NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
on_read(recv_data, actual_len);
|
||||||
|
|
||||||
|
} else if (data_to_recv > len) { // Continue receiving
|
||||||
|
on_read(recv_data, len);
|
||||||
|
data_to_recv -= len;
|
||||||
|
return ret::NEED_MORE_DATA;
|
||||||
|
|
||||||
|
} else if (data_to_recv <= len) { // Last chunk
|
||||||
|
on_read(recv_data, data_to_recv);
|
||||||
|
actual_len = data_to_recv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for "OK" marker after the data
|
||||||
|
char *ok_pos = nullptr;
|
||||||
|
if (actual_len + 1 + 2 /* OK */ <= len) {
|
||||||
|
ok_pos = (char *)memchr(recv_data + actual_len + 1, 'O', MIN_MESSAGE);
|
||||||
|
if (ok_pos == nullptr || ok_pos[1] != 'K') {
|
||||||
|
data_to_recv = 0;
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset and prepare for next receive
|
||||||
|
data_to_recv = 0;
|
||||||
|
return ret::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::send(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (send_stat < 3) {
|
||||||
|
// Look for the '>' prompt
|
||||||
|
if (memchr(data, '>', len) == NULL) {
|
||||||
|
if (send_stat++ < 2) {
|
||||||
|
return ret::NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAG, "Missed '>' prompt");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the actual data
|
||||||
|
auto written = dte->write(&buffer[0], data_to_send);
|
||||||
|
if (written != data_to_send) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write data: %d/%zu", written, data_to_send);
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
data_to_send = 0;
|
||||||
|
send_stat = 3;
|
||||||
|
}
|
||||||
|
return ret::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::send(std::string_view response)
|
||||||
|
{
|
||||||
|
if (send_stat == 3) {
|
||||||
|
if (response.find("SEND OK") != std::string::npos) {
|
||||||
|
send_stat = 0;
|
||||||
|
return ret::OK;
|
||||||
|
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Send failed");
|
||||||
|
return ret::FAIL;
|
||||||
|
} else if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Send error");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::connect(std::string_view response)
|
||||||
|
{
|
||||||
|
if (response.find("CONNECT") != std::string::npos) {
|
||||||
|
ESP_LOGI(TAG, "TCP connected!");
|
||||||
|
return ret::OK;
|
||||||
|
}
|
||||||
|
if (response.find("ERROR") != std::string::npos) {
|
||||||
|
ESP_LOGE(TAG, "Failed to connect");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
return ret::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Response: %.*s", static_cast<int>(response.size()), response.data());
|
||||||
|
|
||||||
|
// Handle WiFi status messages
|
||||||
|
if (response.find("WIFI CONNECTED") != std::string::npos) {
|
||||||
|
ESP_LOGI(TAG, "WiFi connected");
|
||||||
|
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
|
||||||
|
ESP_LOGW(TAG, "WiFi disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle TCP status messages
|
||||||
|
if (response.find("CONNECT") != std::string::npos && state == status::CONNECTING) {
|
||||||
|
return connect(response);
|
||||||
|
} else if (response.find("CLOSED") != std::string::npos) {
|
||||||
|
ESP_LOGW(TAG, "TCP connection closed");
|
||||||
|
return ret::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle data notifications in active mode (if we switch to it later)
|
||||||
|
if (response.find("+IPD,") != std::string::npos) {
|
||||||
|
uint64_t data_ready = 1;
|
||||||
|
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||||
|
ESP_LOGD(TAG, "Data available notification");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == status::SENDING) {
|
||||||
|
return send(response);
|
||||||
|
} else if (state == status::CONNECTING) {
|
||||||
|
return connect(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (state == status::SENDING) {
|
||||||
|
return send(data, len);
|
||||||
|
}
|
||||||
|
if (state == status::RECEIVING) {
|
||||||
|
return recv(data, len);
|
||||||
|
}
|
||||||
|
return ret::IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
status Responder::pending()
|
||||||
|
{
|
||||||
|
// For ESP-AT, we don't need a pending check like BG96
|
||||||
|
// Just return current status
|
||||||
|
return status::SENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // sock_dce
|
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_IDF_TARGET="esp32"
|
||||||
|
CONFIG_EXAMPLE_MODEM_DEVICE_ESPAT=y
|
Reference in New Issue
Block a user