mirror of
https://github.com/espressif/esp-protocols.git
synced 2026-05-05 12:24:12 +02:00
fix(modem): Use generated AT command definitions for IDE navigation
BREAKING CHANGE: inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp)
This commit is contained in:
@@ -4,8 +4,14 @@ elseif(CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600)
|
||||
set(device_srcs sock_commands_sim7600.cpp)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_MODEM_ENABLE_DEVELOPMENT_MODE)
|
||||
set(command_dir "generate")
|
||||
else()
|
||||
set(command_dir "command")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "modem_client.cpp"
|
||||
"sock_dce.cpp"
|
||||
"${command_dir}/sock_dce.cpp"
|
||||
"tcp_transport_at.cpp"
|
||||
"${device_srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
INCLUDE_DIRS "." "${command_dir}")
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "cxx_include/esp_modem_types.hpp"
|
||||
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result net_open(esp_modem::CommandableIf *t);
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result net_close(esp_modem::CommandableIf *t);
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result tcp_open(esp_modem::CommandableIf *t, const std::string &host, int port, int timeout);
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result tcp_close(esp_modem::CommandableIf *t);
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result get_ip(esp_modem::CommandableIf *t, std::string &addr);
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result set_rx_mode(esp_modem::CommandableIf *t, int mode);
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
|
||||
#include "sock_dce.hpp"
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
{
|
||||
if (listen_sock == -1) {
|
||||
ESP_LOGE(TAG, "Listening socket not ready");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (sock == -1) { // no active socket, need to accept one first
|
||||
return accept_sock();
|
||||
}
|
||||
|
||||
// we have a socket, let's check the status
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 500000,
|
||||
};
|
||||
if (state == status::PENDING) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
state = at.pending();
|
||||
return true;
|
||||
}
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sock, &fdset);
|
||||
FD_SET(data_ready_fd, &fdset);
|
||||
int s = select(std::max(sock, data_ready_fd) + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s == 0) {
|
||||
ESP_LOGV(TAG, "perform select timeout...");
|
||||
return true;
|
||||
} else if (s < 0) {
|
||||
ESP_LOGE(TAG, "select error %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (FD_ISSET(sock, &fdset) && !sock_to_at()) {
|
||||
return false;
|
||||
}
|
||||
if (FD_ISSET(data_ready_fd, &fdset) && !at_to_sock()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_DATA:
|
||||
return;
|
||||
case Responder::ret::IN_PROGRESS:
|
||||
break;
|
||||
case Responder::ret::NEED_MORE_TIME:
|
||||
state = status::PENDING;
|
||||
return;
|
||||
}
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_TIME:
|
||||
state = status::PENDING;
|
||||
return;
|
||||
case Responder::ret::NEED_MORE_DATA:
|
||||
case Responder::ret::IN_PROGRESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DCE::close_sock()
|
||||
{
|
||||
if (sock > 0) {
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
dte->on_read(nullptr);
|
||||
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()
|
||||
{
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE(TAG, "Unexpected state %d", static_cast<int>(state));
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "read error %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
} else if (len == 0) {
|
||||
ESP_LOGE(TAG, "EOF %d", errno);
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
at.start_sending(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::accept_sock()
|
||||
{
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 500000,
|
||||
};
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(listen_sock, &fdset);
|
||||
int s = select(listen_sock + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s > 0 && FD_ISSET(listen_sock, &fdset)) {
|
||||
struct sockaddr_in source_addr = {};
|
||||
socklen_t addr_len = sizeof(source_addr);
|
||||
sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGD(TAG, "Socket accepted!");
|
||||
FD_ZERO(&fdset);
|
||||
return true;
|
||||
} else if (s == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DCE::start_listening(int port)
|
||||
{
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
if (listen_sock < 0) {
|
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
// inet_aton("127.0.0.1", &addr.sin_addr);
|
||||
|
||||
int err = bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DCE::connect(std::string host, int port)
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
tcp_close();
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->perform_at(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
return false;
|
||||
}
|
||||
state = status::CONNECTING;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DCE::init()
|
||||
{
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
while (sync() != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to sync up");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "Modem in sync");
|
||||
i = 0;
|
||||
while (setup_data_mode() != true) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to setup pdp/data");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "PDP configured");
|
||||
i = 0;
|
||||
while (net_open() != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed to open network");
|
||||
return false;
|
||||
}
|
||||
net_close();
|
||||
esp_modem::Task::Delay(1000);
|
||||
}
|
||||
ESP_LOGD(TAG, "Network opened");
|
||||
i = 0;
|
||||
std::string ip_addr;
|
||||
while (get_ip(ip_addr) != esp_modem::command_result::OK) {
|
||||
if (i++ > retries) {
|
||||
ESP_LOGE(TAG, "Failed obtain an IP address");
|
||||
return false;
|
||||
}
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class Factory: public ::esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
|
||||
{
|
||||
return esp_modem::dce_factory::Factory::build_module_T<DCE, std::unique_ptr<DCE>>(config, std::move(dte));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte)
|
||||
{
|
||||
return Factory::create(config, std::move(dte));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::net_open()
|
||||
{
|
||||
return sock_commands::net_open(dte.get());
|
||||
}
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::net_close()
|
||||
{
|
||||
return sock_commands::net_close(dte.get());
|
||||
}
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::tcp_open(const std::string &host, int port, int timeout)
|
||||
{
|
||||
return sock_commands::tcp_open(dte.get(), host, port, timeout);
|
||||
}
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::tcp_close()
|
||||
{
|
||||
return sock_commands::tcp_close(dte.get());
|
||||
}
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::get_ip(std::string &addr)
|
||||
{
|
||||
return sock_commands::get_ip(dte.get(), addr);
|
||||
}
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result DCE::set_rx_mode(int mode)
|
||||
{
|
||||
return sock_commands::set_rx_mode(dte.get(), mode);
|
||||
}
|
||||
} // namespace sock_dce
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "sock_commands.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace sock_dce {
|
||||
|
||||
|
||||
enum class status {
|
||||
IDLE,
|
||||
CONNECTING,
|
||||
SENDING,
|
||||
RECEIVING,
|
||||
FAILED,
|
||||
PENDING
|
||||
};
|
||||
|
||||
class Responder {
|
||||
public:
|
||||
enum class ret {
|
||||
OK, FAIL, IN_PROGRESS, NEED_MORE_DATA, NEED_MORE_TIME
|
||||
};
|
||||
Responder(int &s, int &ready_fd, std::shared_ptr<esp_modem::DTE> &dte_arg):
|
||||
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
|
||||
ret process_data(status state, uint8_t *data, size_t len);
|
||||
ret check_async_replies(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
bool start_connecting(std::string host, int port);
|
||||
status pending();
|
||||
uint8_t *get_buf()
|
||||
{
|
||||
return &buffer[0];
|
||||
}
|
||||
size_t get_buf_len()
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void clear_offsets()
|
||||
{
|
||||
actual_read = 0;
|
||||
}
|
||||
|
||||
size_t data_available()
|
||||
{
|
||||
return actual_read;
|
||||
}
|
||||
|
||||
size_t has_data()
|
||||
{
|
||||
return total_len;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
ret recv(uint8_t *data, size_t len);
|
||||
ret send(uint8_t *data, size_t len);
|
||||
ret send(std::string_view response);
|
||||
ret connect(std::string_view response);
|
||||
void send_cmd(std::string_view command)
|
||||
{
|
||||
dte->write((uint8_t *) command.begin(), command.size());
|
||||
}
|
||||
std::array<uint8_t, buffer_size> buffer;
|
||||
size_t data_to_recv = 0;
|
||||
size_t actual_read = 0;
|
||||
size_t total_len = 0;
|
||||
|
||||
bool read_again = false;
|
||||
int &sock;
|
||||
int &data_ready_fd;
|
||||
int send_stat = 0;
|
||||
size_t data_to_send = 0;
|
||||
std::shared_ptr<esp_modem::DTE> &dte;
|
||||
};
|
||||
|
||||
class DCE : public ::esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result net_open();
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result net_close();
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result tcp_open(const std::string &host, int port, int timeout);
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result tcp_close();
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result get_ip(std::string &addr);
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
esp_modem::command_result set_rx_mode(int mode);
|
||||
bool init();
|
||||
bool connect(std::string host, int port);
|
||||
void start_listening(int port);
|
||||
bool perform_sock();
|
||||
void set_idle()
|
||||
{
|
||||
signal.set(IDLE);
|
||||
}
|
||||
bool wait_to_idle(uint32_t ms)
|
||||
{
|
||||
if (!signal.wait(IDLE, ms)) {
|
||||
ESP_LOGE("dce", "Failed to get idle");
|
||||
return false;
|
||||
}
|
||||
if (state != status::IDLE) {
|
||||
ESP_LOGE("dce", "Unexpected state %d", static_cast<int>(state));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int sync_recv(char *buffer, int len, int timeout_ms)
|
||||
{
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
int max_len = std::min(len, (int)at.get_buf_len());
|
||||
at.start_receiving(max_len);
|
||||
if (!signal.wait(IDLE, 500 + timeout_ms)) {
|
||||
return 0;
|
||||
}
|
||||
int ret = at.data_available();
|
||||
if (ret > 0) {
|
||||
memcpy(buffer, at.get_buf(), ret);
|
||||
}
|
||||
set_idle();
|
||||
return ret;
|
||||
}
|
||||
int sync_send(const char *buffer, size_t len, int timeout_ms)
|
||||
{
|
||||
int len_to_send = std::min(len, at.get_buf_len());
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
at.start_sending(len_to_send);
|
||||
if (!signal.wait(IDLE, timeout_ms + 1000)) {
|
||||
if (state == status::PENDING) {
|
||||
state = status::IDLE;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
set_idle();
|
||||
return len_to_send;
|
||||
}
|
||||
int wait_to_read(uint32_t ms)
|
||||
{
|
||||
if (at.has_data() > 0) {
|
||||
ESP_LOGD("dce", "Data buffered in modem (len=%d)", at.has_data());
|
||||
return 1;
|
||||
}
|
||||
struct timeval tv = {
|
||||
.tv_sec = static_cast<time_t>(ms / 1000),
|
||||
.tv_usec = 0,
|
||||
};
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(data_ready_fd, &fdset);
|
||||
int s = select(data_ready_fd + 1, &fdset, nullptr, nullptr, &tv);
|
||||
if (s == 0) {
|
||||
return 0;
|
||||
} else if (s < 0) {
|
||||
ESP_LOGE("dce", "select error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
if (FD_ISSET(data_ready_fd, &fdset)) {
|
||||
ESP_LOGD("dce", "select read: modem data available");
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
void close_sock();
|
||||
bool accept_sock();
|
||||
bool sock_to_at();
|
||||
bool at_to_sock();
|
||||
void perform_at(uint8_t *data, size_t len);
|
||||
status state{status::IDLE};
|
||||
static constexpr uint8_t IDLE = 1;
|
||||
Responder at{sock, data_ready_fd, dte};
|
||||
int sock {-1};
|
||||
int listen_sock {-1};
|
||||
int data_ready_fd {-1};
|
||||
};
|
||||
std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "cxx_include/esp_modem_types.hpp"
|
||||
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
// --- ESP-MODEM command module starts here ---
|
||||
#include "esp_modem_command_declare_helper.inc"
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
|
||||
esp_modem::return_type name(esp_modem::CommandableIf *t ESP_MODEM_COMMAND_PARAMS_AFTER(__VA_ARGS__));
|
||||
|
||||
#include "socket_commands.inc"
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
}
|
||||
+6
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -304,23 +304,13 @@ std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr
|
||||
return Factory::create(config, std::move(dte));
|
||||
}
|
||||
|
||||
// Helper macros to handle multiple arguments of declared API
|
||||
#define ARGS0
|
||||
#define ARGS1 , p1
|
||||
#define ARGS2 , p1 , p2
|
||||
#define ARGS3 , p1 , p2 , p3
|
||||
|
||||
#define EXPAND_ARGS(x) ARGS ## x
|
||||
#define ARGS(x) EXPAND_ARGS(x)
|
||||
|
||||
//
|
||||
// Repeat all declarations and forward to the AT commands defined in ::sock_commands namespace
|
||||
//
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, arg_nr, ...) \
|
||||
esp_modem::return_type DCE::name(__VA_ARGS__) { return sock_commands::name(dte.get() ARGS(arg_nr) ); }
|
||||
|
||||
DECLARE_SOCK_COMMANDS(return_type name(...) )
|
||||
// --- ESP-MODEM command module starts here ---
|
||||
#include "esp_modem_command_declare_helper.inc"
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
|
||||
esp_modem::return_type DCE::name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__)) { return sock_commands::name(dte.get() ESP_MODEM_COMMAND_FORWARD_AFTER(__VA_ARGS__) ); }
|
||||
|
||||
#include "socket_commands.inc"
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
} // namespace sock_dce
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include <cxx_include/esp_modem_dce_factory.hpp>
|
||||
#include "socket_commands.inc"
|
||||
#include "sock_commands.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
@@ -102,11 +101,12 @@ class DCE : public ::esp_modem::GenericModule {
|
||||
using esp_modem::GenericModule::GenericModule;
|
||||
public:
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(__VA_ARGS__);
|
||||
|
||||
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
|
||||
// --- ESP-MODEM command module starts here ---
|
||||
#include "esp_modem_command_declare_helper.inc"
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, ...) \
|
||||
esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
|
||||
#include "socket_commands.inc"
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
bool init();
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result)
|
||||
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result)
|
||||
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, STR_IN(host), INT_IN(port), INT_IN(timeout))
|
||||
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result)
|
||||
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, STR_OUT(addr))
|
||||
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, INT_IN(mode))
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
#include "cxx_include/esp_modem_types.hpp"
|
||||
#include "socket_commands.inc"
|
||||
|
||||
namespace sock_commands {
|
||||
|
||||
#define ESP_MODEM_DECLARE_DCE_COMMAND(name, return_type, num, ...) \
|
||||
esp_modem::return_type name(esp_modem::CommandableIf *t, ## __VA_ARGS__);
|
||||
|
||||
DECLARE_SOCK_COMMANDS(declare name(Commandable *p, ...);)
|
||||
|
||||
#undef ESP_MODEM_DECLARE_DCE_COMMAND
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2021-2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "generate/esp_modem_command_declare_helper.inc"
|
||||
|
||||
#define DECLARE_SOCK_COMMANDS(...) \
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_open, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Closes network in AT command mode
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(net_close, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Opens a TCP connection
|
||||
* @param[in] host Host name or IP address to connect to
|
||||
* @param[in] port Port number
|
||||
* @param[in] timeout Connection timeout
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_open, command_result, 3, STRING_IN(p1, host), INT_IN(p2, port), INT_IN(p3, timeout)) \
|
||||
\
|
||||
/**
|
||||
* @brief Closes opened TCP socket
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(tcp_close, command_result, 0) \
|
||||
\
|
||||
/**
|
||||
* @brief Gets modem IP address
|
||||
* @param[out] addr String representation of modem's IP
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(get_ip, command_result, 1, STRING_OUT(p1, addr)) \
|
||||
\
|
||||
/**
|
||||
* @brief Sets Rx mode
|
||||
* @param[in] mode 0=auto, 1=manual
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/ \
|
||||
ESP_MODEM_DECLARE_DCE_COMMAND(set_rx_mode, command_result, 1, INT_IN(p1, mode))
|
||||
Reference in New Issue
Block a user