mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-18 13:02:21 +02:00
Corrections on fragmenting/buffering for vfs/cmux
This commit is contained in:
@ -8,6 +8,7 @@ if(${target} STREQUAL "linux")
|
|||||||
else()
|
else()
|
||||||
set(platform_srcs src/esp_modem_primitives_freertos.cpp
|
set(platform_srcs src/esp_modem_primitives_freertos.cpp
|
||||||
src/esp_modem_uart.cpp
|
src/esp_modem_uart.cpp
|
||||||
|
src/esp_modem_term_uart.cpp
|
||||||
src/esp_modem_netif.cpp)
|
src/esp_modem_netif.cpp)
|
||||||
set(dependencies driver)
|
set(dependencies driver)
|
||||||
endif()
|
endif()
|
||||||
|
@ -47,12 +47,13 @@ extern "C" void app_main(void)
|
|||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
// init the DTE
|
// init the DTE
|
||||||
esp_modem_dte_config_t dte_config = {
|
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||||
|
esp_modem_dte_config_t dte_config2 = {
|
||||||
.dte_buffer_size = 512,
|
.dte_buffer_size = 512,
|
||||||
.vfs_config = {.port_num = UART_NUM_1,
|
.vfs_config = {.port_num = UART_NUM_1,
|
||||||
.dev_name = "/dev/uart/1",
|
.dev_name = "/dev/uart/1",
|
||||||
.rx_buffer_size = 512,
|
.rx_buffer_size = 1024,
|
||||||
.tx_buffer_size = 512,
|
.tx_buffer_size = 1024,
|
||||||
.baud_rate = 115200,
|
.baud_rate = 115200,
|
||||||
.tx_io_num = 25,
|
.tx_io_num = 25,
|
||||||
.rx_io_num = 26,
|
.rx_io_num = 26,
|
||||||
@ -64,7 +65,8 @@ extern "C" void app_main(void)
|
|||||||
|
|
||||||
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
|
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
|
||||||
assert(esp_netif);
|
assert(esp_netif);
|
||||||
auto uart_dte = create_vfs_dte(&dte_config);
|
auto uart_dte = create_vfs_dte(&dte_config2);
|
||||||
|
// auto uart_dte = create_uart_dte(&dte_config);
|
||||||
|
|
||||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
|
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
|
||||||
|
|
||||||
@ -192,7 +194,7 @@ extern "C" void app_main(void)
|
|||||||
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
||||||
});
|
});
|
||||||
|
|
||||||
signal_group exit_signal;
|
SignalGroup exit_signal;
|
||||||
const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand *c){
|
const ConsoleCommand ExitConsole("exit", "exit the console application", no_args, [&](ConsoleCommand *c){
|
||||||
ESP_LOGI(TAG, "Exiting...");
|
ESP_LOGI(TAG, "Exiting...");
|
||||||
exit_signal.set(1);
|
exit_signal.set(1);
|
||||||
|
@ -128,6 +128,18 @@ extern "C" void app_main(void)
|
|||||||
|
|
||||||
/* Configure the DTE */
|
/* Configure the DTE */
|
||||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||||
|
esp_modem_dte_config_t dte_config2 = {
|
||||||
|
.dte_buffer_size = 512,
|
||||||
|
.vfs_config = {.port_num = UART_NUM_1,
|
||||||
|
.dev_name = "/dev/uart/1",
|
||||||
|
.rx_buffer_size = 512,
|
||||||
|
.tx_buffer_size = 512,
|
||||||
|
.baud_rate = 115200,
|
||||||
|
.tx_io_num = 25,
|
||||||
|
.rx_io_num = 26,
|
||||||
|
.task_stack_size = 4096,
|
||||||
|
.task_prio = 5}
|
||||||
|
};
|
||||||
|
|
||||||
/* Configure the DCE */
|
/* Configure the DCE */
|
||||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
|
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
|
||||||
@ -135,7 +147,8 @@ extern "C" void app_main(void)
|
|||||||
/* Configure the PPP netif */
|
/* Configure the PPP netif */
|
||||||
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
|
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
|
||||||
|
|
||||||
auto uart_dte = create_uart_dte(&dte_config);
|
// auto uart_dte = create_uart_dte(&dte_config);
|
||||||
|
auto uart_dte = create_vfs_dte(&dte_config2);
|
||||||
|
|
||||||
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
|
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
|
||||||
assert(esp_netif);
|
assert(esp_netif);
|
||||||
@ -160,10 +173,18 @@ extern "C" void app_main(void)
|
|||||||
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
// dce->get_imsi(str);
|
||||||
|
// std::cout << "Modem IMSI number:" << str << "|" << std::endl;
|
||||||
|
// std::cout << "|" << str << "|" << std::endl;
|
||||||
|
|
||||||
dce->set_mode(esp_modem::modem_mode::CMUX_MODE);
|
dce->set_mode(esp_modem::modem_mode::CMUX_MODE);
|
||||||
dce->get_imsi(str);
|
dce->get_imsi(str);
|
||||||
std::cout << "Modem IMSI number:" << str << "|" << std::endl;
|
std::cout << "Modem IMSI number:" << str << "|" << std::endl;
|
||||||
|
dce->get_imei(str);
|
||||||
|
std::cout << "Modem IMEI number:" << str << "|" << std::endl;
|
||||||
|
dce->get_operator_name(str);
|
||||||
|
std::cout << "Operator name:" << str << "|" << std::endl;
|
||||||
|
// return;
|
||||||
|
|
||||||
dce->set_data();
|
dce->set_data();
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class CMuxInstance;
|
|||||||
class CMux {
|
class CMux {
|
||||||
public:
|
public:
|
||||||
explicit CMux(std::unique_ptr<Terminal> t, std::unique_ptr<uint8_t[]> b, size_t buff_size):
|
explicit CMux(std::unique_ptr<Terminal> t, std::unique_ptr<uint8_t[]> b, size_t buff_size):
|
||||||
term(std::move(t)), buffer_size(buff_size), buffer(std::move(b)) {}
|
term(std::move(t)), buffer_size(buff_size), buffer(std::move(b)), payload_start(nullptr), total_payload_size(0) {}
|
||||||
~CMux() = default;
|
~CMux() = default;
|
||||||
[[nodiscard]] bool init();
|
[[nodiscard]] bool init();
|
||||||
void set_read_cb(int inst, std::function<bool(uint8_t *data, size_t len)> f);
|
void set_read_cb(int inst, std::function<bool(uint8_t *data, size_t len)> f);
|
||||||
@ -64,7 +64,7 @@ public:
|
|||||||
int write(int i, uint8_t *data, size_t len);
|
int write(int i, uint8_t *data, size_t len);
|
||||||
private:
|
private:
|
||||||
std::function<bool(uint8_t *data, size_t len)> read_cb[max_terms];
|
std::function<bool(uint8_t *data, size_t len)> read_cb[max_terms];
|
||||||
void data_available(uint8_t *data, size_t len);
|
void data_available(uint8_t *data, size_t len, size_t payload_offset);
|
||||||
void send_sabm(size_t i);
|
void send_sabm(size_t i);
|
||||||
std::unique_ptr<Terminal> term;
|
std::unique_ptr<Terminal> term;
|
||||||
cmux_state state;
|
cmux_state state;
|
||||||
@ -80,6 +80,8 @@ private:
|
|||||||
Lock lock;
|
Lock lock;
|
||||||
int instance;
|
int instance;
|
||||||
int sabm_ack;
|
int sabm_ack;
|
||||||
|
uint8_t *payload_start;
|
||||||
|
size_t total_payload_size;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public:
|
|||||||
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
|
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const size_t GOT_LINE = signal_group::bit0; /*!< Bit indicating response available */
|
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||||
|
|
||||||
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
|
[[nodiscard]] bool setup_cmux(); /*!< Internal setup of CMUX mode */
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ private:
|
|||||||
Terminal *command_term; /*!< Reference to the terminal used for sending commands */
|
Terminal *command_term; /*!< Reference to the terminal used for sending commands */
|
||||||
std::unique_ptr<Terminal> other_term; /*!< Secondary terminal for this DTE */
|
std::unique_ptr<Terminal> other_term; /*!< Secondary terminal for this DTE */
|
||||||
modem_mode mode; /*!< DTE operation mode */
|
modem_mode mode; /*!< DTE operation mode */
|
||||||
signal_group signal; /*!< Event group used to signal request-response operations */
|
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||||
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
|
std::function<bool(uint8_t *data, size_t len)> on_data; /*!< on data callback for current terminal */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,9 +75,9 @@ private:
|
|||||||
std::shared_ptr<DTE> ppp_dte;
|
std::shared_ptr<DTE> ppp_dte;
|
||||||
esp_netif_t *netif;
|
esp_netif_t *netif;
|
||||||
struct ppp_netif_driver driver{};
|
struct ppp_netif_driver driver{};
|
||||||
signal_group signal;
|
SignalGroup signal;
|
||||||
static const size_t PPP_STARTED = signal_group::bit0;
|
static const size_t PPP_STARTED = SignalGroup::bit0;
|
||||||
static const size_t PPP_EXIT = signal_group::bit1;
|
static const size_t PPP_EXIT = SignalGroup::bit1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_LINUX)
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// forward declarations of FreeRTOS primitives
|
// forward declarations of FreeRTOS primitives
|
||||||
struct QueueDefinition;
|
struct QueueDefinition;
|
||||||
@ -27,10 +29,12 @@ typedef void * EventGroupHandle_t;
|
|||||||
|
|
||||||
namespace esp_modem {
|
namespace esp_modem {
|
||||||
|
|
||||||
|
// Forward declaration for both linux/FreeRTOS targets
|
||||||
|
//
|
||||||
|
using TaskFunction_t = void (*)(void*);
|
||||||
#if !defined(CONFIG_IDF_TARGET_LINUX)
|
#if !defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
struct Lock {
|
struct Lock {
|
||||||
using MutexT = QueueDefinition*;
|
using MutexT = QueueDefinition*;
|
||||||
|
|
||||||
explicit Lock();
|
explicit Lock();
|
||||||
~Lock();
|
~Lock();
|
||||||
void lock();
|
void lock();
|
||||||
@ -38,13 +42,14 @@ struct Lock {
|
|||||||
private:
|
private:
|
||||||
MutexT m{};
|
MutexT m{};
|
||||||
};
|
};
|
||||||
|
using TaskT = void*;
|
||||||
using Signal = void*;
|
using SignalT = void*;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
using Lock = std::mutex;
|
using Lock = std::mutex;
|
||||||
struct SignalGroup;
|
struct SignalGroupInternal;
|
||||||
using Signal = std::unique_ptr<SignalGroup>;
|
using SignalT = std::unique_ptr<SignalGroupInternal>;
|
||||||
|
using TaskT = std::thread;
|
||||||
|
static constexpr uint32_t portMAX_DELAY = UINT32_MAX;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@ -57,13 +62,26 @@ private:
|
|||||||
T& lock;
|
T& lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct signal_group {
|
class Task {
|
||||||
|
public:
|
||||||
|
explicit Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function);
|
||||||
|
~Task();
|
||||||
|
|
||||||
|
static void Delete();
|
||||||
|
static void Relinquish();
|
||||||
|
private:
|
||||||
|
TaskT task_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SignalGroup {
|
||||||
|
public:
|
||||||
static constexpr size_t bit0 = 1 << 0;
|
static constexpr size_t bit0 = 1 << 0;
|
||||||
static constexpr size_t bit1 = 1 << 1;
|
static constexpr size_t bit1 = 1 << 1;
|
||||||
static constexpr size_t bit2 = 1 << 2;
|
static constexpr size_t bit2 = 1 << 2;
|
||||||
static constexpr size_t bit3 = 1 << 3;
|
static constexpr size_t bit3 = 1 << 3;
|
||||||
|
|
||||||
explicit signal_group();
|
explicit SignalGroup();
|
||||||
|
|
||||||
void set(uint32_t bits);
|
void set(uint32_t bits);
|
||||||
|
|
||||||
@ -77,10 +95,10 @@ struct signal_group {
|
|||||||
// waiting for any bit, not clearing them
|
// waiting for any bit, not clearing them
|
||||||
bool wait_any(uint32_t flags, uint32_t time_ms);
|
bool wait_any(uint32_t flags, uint32_t time_ms);
|
||||||
|
|
||||||
~signal_group();
|
~SignalGroup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Signal event_group;
|
SignalT event_group;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esp_modem
|
} // namespace esp_modem
|
||||||
|
@ -85,23 +85,39 @@ void CMux::send_sabm(size_t dlci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CMux::data_available(uint8_t *data, size_t len)
|
void CMux::data_available(uint8_t *data, size_t len, size_t payload_offset)
|
||||||
{
|
{
|
||||||
if (type == 0xFF && len > 0 && dlci > 0) {
|
if (type == 0xFF && len > 0 && dlci > 0) {
|
||||||
int virtual_term = dlci - 1;
|
int virtual_term = dlci - 1;
|
||||||
if (virtual_term < max_terms && read_cb[virtual_term])
|
if (virtual_term < max_terms && read_cb[virtual_term]) {
|
||||||
read_cb[virtual_term](data, len);
|
if (payload_start == nullptr) {
|
||||||
|
payload_start = data;
|
||||||
|
}
|
||||||
|
total_payload_size += len;
|
||||||
|
// Post partial data (if configured)
|
||||||
|
// read_cb[virtual_term](payload_start, total_payload_size);
|
||||||
|
|
||||||
|
}
|
||||||
} else if (type == 0x73 && len == 0) { // notify the initial SABM command
|
} else if (type == 0x73 && len == 0) { // notify the initial SABM command
|
||||||
Scoped<Lock> l(lock);
|
Scoped<Lock> l(lock);
|
||||||
sabm_ack = dlci;
|
sabm_ack = dlci;
|
||||||
|
} else if (payload_offset == -1) {
|
||||||
|
int virtual_term = dlci - 1;
|
||||||
|
if (virtual_term < max_terms && read_cb[virtual_term]) {
|
||||||
|
read_cb[virtual_term](payload_start, total_payload_size);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
||||||
{
|
{
|
||||||
if (!data) {
|
if (!data) {
|
||||||
auto data_to_read = std::min(actual_len, buffer_size);
|
auto data_to_read = buffer_size;
|
||||||
data = buffer.get();
|
if (payload_start)
|
||||||
|
data = payload_start + total_payload_size;
|
||||||
|
else
|
||||||
|
data = buffer.get();
|
||||||
actual_len = term->read(data, data_to_read);
|
actual_len = term->read(data, data_to_read);
|
||||||
}
|
}
|
||||||
ESP_LOG_BUFFER_HEXDUMP("Received", data, actual_len, ESP_LOG_DEBUG);
|
ESP_LOG_BUFFER_HEXDUMP("Received", data, actual_len, ESP_LOG_DEBUG);
|
||||||
@ -135,7 +151,7 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
|||||||
return false;
|
return false;
|
||||||
case cmux_state::INIT:
|
case cmux_state::INIT:
|
||||||
if (frame[0] != SOF_MARKER) {
|
if (frame[0] != SOF_MARKER) {
|
||||||
ESP_LOGI("CMUX", "Protocol mismatch!");
|
ESP_LOGE("CMUX", "Protocol mismatch!");
|
||||||
state = cmux_state::RECOVER;
|
state = cmux_state::RECOVER;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -151,6 +167,13 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
|||||||
frame++;
|
frame++;
|
||||||
break;
|
break;
|
||||||
case cmux_state::HEADER:
|
case cmux_state::HEADER:
|
||||||
|
if (available_len > 0 && frame_header_offset == 1) {
|
||||||
|
if (frame[0] == SOF_MARKER) {
|
||||||
|
available_len--;
|
||||||
|
frame++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (available_len + frame_header_offset < 4) {
|
if (available_len + frame_header_offset < 4) {
|
||||||
memcpy(frame_header + frame_header_offset, frame, available_len);
|
memcpy(frame_header + frame_header_offset, frame, available_len);
|
||||||
frame_header_offset += available_len;
|
frame_header_offset += available_len;
|
||||||
@ -168,14 +191,15 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
|||||||
state = cmux_state::PAYLOAD;
|
state = cmux_state::PAYLOAD;
|
||||||
break;
|
break;
|
||||||
case cmux_state::PAYLOAD:
|
case cmux_state::PAYLOAD:
|
||||||
|
// ESP_LOGD("PAYLOAD", "CMUX FR: A:%02x T:%02x available:%d payloadLen:%d", dlci, type, available_len, payload_len);
|
||||||
if (available_len < payload_len) { // payload
|
if (available_len < payload_len) { // payload
|
||||||
state = cmux_state::PAYLOAD;
|
state = cmux_state::PAYLOAD;
|
||||||
data_available(frame, available_len); // partial read
|
data_available(frame, available_len, 0); // partial read
|
||||||
payload_len -= available_len;
|
payload_len -= available_len;
|
||||||
return false;
|
return false;
|
||||||
} else { // complete
|
} else { // complete
|
||||||
if (payload_len > 0) {
|
if (payload_len > 0) {
|
||||||
data_available(&frame[0], payload_len); // rest read
|
data_available(&frame[0], payload_len, 0); // rest read
|
||||||
}
|
}
|
||||||
available_len -= payload_len;
|
available_len -= payload_len;
|
||||||
frame += payload_len;
|
frame += payload_len;
|
||||||
@ -183,25 +207,42 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case cmux_state::FOOTER:
|
case cmux_state::FOOTER:
|
||||||
if (available_len + frame_header_offset < 6) {
|
if (available_len == 0) {
|
||||||
memcpy(frame_header + frame_header_offset, frame, available_len);
|
return false; // need read more
|
||||||
frame_header_offset += available_len;
|
} else if (available_len == 1) {
|
||||||
|
frame_header[4] = frame[0];
|
||||||
|
frame ++;
|
||||||
|
available_len --;
|
||||||
return false; // need read more
|
return false; // need read more
|
||||||
} else {
|
} else {
|
||||||
footer_offset = std::min(available_len, 6 - frame_header_offset);
|
//
|
||||||
memcpy(frame_header + frame_header_offset, frame, footer_offset);
|
// }
|
||||||
|
// if (available_len + frame_header_offset < 6) {
|
||||||
|
// memcpy(frame_header + frame_header_offset, frame, available_len);
|
||||||
|
// frame_header_offset += available_len;
|
||||||
|
// return false; // need read more
|
||||||
|
// } else {
|
||||||
|
frame_header[4] = frame[0];
|
||||||
|
frame_header[5] = frame[1];
|
||||||
|
footer_offset = 2;
|
||||||
|
// footer_offset = std::min(available_len, 6 - frame_header_offset);
|
||||||
|
// memcpy(frame_header + frame_header_offset, frame, footer_offset);
|
||||||
if (frame_header[5] != SOF_MARKER) {
|
if (frame_header[5] != SOF_MARKER) {
|
||||||
ESP_LOGI("CMUX", "Protocol mismatch!");
|
ESP_LOGE("CMUX", "Footer Protocol mismatch!");
|
||||||
state = cmux_state::RECOVER;
|
state = cmux_state::RECOVER;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (payload_len == 0) {
|
if (payload_len == 0) {
|
||||||
data_available(frame_header, 0); // Null payload
|
data_available(frame_header, 0, 0); // Null payload
|
||||||
}
|
}
|
||||||
frame += footer_offset;
|
frame += footer_offset;
|
||||||
available_len -= footer_offset;
|
available_len -= footer_offset;
|
||||||
state = cmux_state::INIT;
|
state = cmux_state::INIT;
|
||||||
frame_header_offset = 0;
|
frame_header_offset = 0;
|
||||||
|
if (payload_len)
|
||||||
|
data_available(nullptr, 0, -1); // Null payload
|
||||||
|
payload_start = nullptr;
|
||||||
|
total_payload_size = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -223,7 +264,7 @@ bool CMux::init()
|
|||||||
{
|
{
|
||||||
int timeout = 0;
|
int timeout = 0;
|
||||||
send_sabm(i);
|
send_sabm(i);
|
||||||
while (1) {
|
while (true) {
|
||||||
usleep(10'000);
|
usleep(10'000);
|
||||||
Scoped<Lock> l(lock);
|
Scoped<Lock> l(lock);
|
||||||
if (sabm_ack == i) {
|
if (sabm_ack == i) {
|
||||||
|
@ -12,50 +12,62 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
#include <list>
|
||||||
|
#include "esp_log.h"
|
||||||
#include "cxx_include/esp_modem_dte.hpp"
|
#include "cxx_include/esp_modem_dte.hpp"
|
||||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||||
#include "cxx_include/esp_modem_command_library.hpp"
|
#include "cxx_include/esp_modem_command_library.hpp"
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
// TODO: Remove iostream
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace esp_modem::dce_commands {
|
namespace esp_modem::dce_commands {
|
||||||
|
|
||||||
static inline 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::list<std::string_view>& pass_phrase,
|
||||||
const std::string& fail_phrase, uint32_t timeout_ms)
|
const std::list<std::string_view>& fail_phrase,
|
||||||
|
uint32_t timeout_ms)
|
||||||
{
|
{
|
||||||
std::cout << command << std::endl;
|
printf("Command %s\n", command.c_str());
|
||||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||||
std::string response((char*)data, len);
|
std::string_view response((char*)data, len);
|
||||||
std::cout << response << std::endl;
|
printf("Response: %.*s\n", (int)response.length(), response.data());
|
||||||
|
for (auto it : pass_phrase)
|
||||||
if (response.find(pass_phrase) != std::string::npos) {
|
if (response.find(it) != std::string::npos)
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
} else if (response.find(fail_phrase) != std::string::npos) {
|
for (auto it : fail_phrase)
|
||||||
return command_result::FAIL;
|
if (response.find(it) != std::string::npos)
|
||||||
}
|
return command_result::FAIL;
|
||||||
return command_result::TIMEOUT;
|
return command_result::TIMEOUT;
|
||||||
}, timeout_ms);
|
}, timeout_ms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500)
|
static inline command_result generic_command(CommandableIf* t, const std::string& command,
|
||||||
|
const std::string& pass_phrase,
|
||||||
|
const std::string& fail_phrase, uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
const auto pass = std::list<std::string_view>({pass_phrase});
|
||||||
|
const auto fail = std::list<std::string_view>({fail_phrase});
|
||||||
|
return generic_command(t, command, pass, fail, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string_view& output, uint32_t timeout_ms = 500)
|
||||||
{
|
{
|
||||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
std::string response((char*)data, len);
|
std::string_view response((char*)data, len);
|
||||||
while ((pos = response.find('\n')) != std::string::npos) {
|
while ((pos = response.find('\n')) != std::string::npos) {
|
||||||
std::string token = response.substr(0, pos);
|
std::string_view token = response.substr(0, pos);
|
||||||
for (auto i = 0; i<2; ++i) // trip trailing \n\r of last two chars
|
for (auto it = token.end() - 1; it > token.begin(); it--) // strip trailing CR or LF
|
||||||
if (pos >= 1 && (token[pos-1] == '\r' || token[pos-1] == '\n'))
|
if (*it == '\r' || *it == '\n')
|
||||||
token.pop_back();
|
token.remove_suffix(1);
|
||||||
|
printf("{%.*s}\n", static_cast<int>(token.size()), token.data());
|
||||||
|
|
||||||
if (token.find("OK") != std::string::npos) {
|
if (token.find("OK") != std::string::npos) {
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
} else if (token.find("ERROR") != std::string::npos) {
|
} else if (token.find("ERROR") != std::string::npos) {
|
||||||
return command_result::FAIL;
|
return command_result::FAIL;
|
||||||
} else if (token.length() > 2) {
|
} else if (token.size() > 2) {
|
||||||
output = token;
|
output = token;
|
||||||
}
|
}
|
||||||
response = response.substr(pos+1);
|
response = response.substr(pos+1);
|
||||||
@ -64,7 +76,17 @@ static inline command_result generic_get_string(CommandableIf* t, const std::str
|
|||||||
}, timeout_ms);
|
}, timeout_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline command_result generic_command_common(CommandableIf* t, std::string command, uint32_t timeout = 500)
|
static inline command_result generic_get_string(CommandableIf* t, const std::string& command, std::string& output, uint32_t timeout_ms = 500)
|
||||||
|
{
|
||||||
|
std::string_view out;
|
||||||
|
auto ret = generic_get_string(t, command, out, timeout_ms);
|
||||||
|
if (ret == command_result::OK)
|
||||||
|
output = out;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline command_result generic_command_common(CommandableIf* t, const std::string &command, uint32_t timeout = 500)
|
||||||
{
|
{
|
||||||
return generic_command(t, command, "OK", "ERROR", timeout);
|
return generic_command(t, command, "OK", "ERROR", timeout);
|
||||||
}
|
}
|
||||||
@ -111,28 +133,54 @@ command_result hang_up(CommandableIf* t)
|
|||||||
|
|
||||||
command_result get_battery_status(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
command_result get_battery_status(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string_view out;
|
||||||
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
||||||
if (ret != command_result::OK)
|
if (ret != command_result::OK)
|
||||||
return ret;
|
return ret;
|
||||||
if (out.find("+CBC") == std::string::npos)
|
|
||||||
|
constexpr std::string_view pattern = "+CBC: ";
|
||||||
|
if (out.find(pattern) == std::string_view::npos)
|
||||||
return command_result::FAIL;
|
return command_result::FAIL;
|
||||||
// Parsing +CBC: <bcs>,<bcl>,<voltage>
|
// Parsing +CBC: <bcs>,<bcl>,<voltage>
|
||||||
sscanf(out.c_str(), "%*s%d,%d,%d", &bcs, &bcl, &voltage);
|
out = out.substr(pattern.size());
|
||||||
|
int pos, value, property = 0;
|
||||||
|
while ((pos = out.find(',') != std::string::npos)) {
|
||||||
|
if (std::from_chars(out.data(), out.data() + pos, value).ec == std::errc::invalid_argument)
|
||||||
|
return command_result::FAIL;
|
||||||
|
switch (property++) {
|
||||||
|
case 0: bcs = value;
|
||||||
|
break;
|
||||||
|
case 1: bcl = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return command_result::FAIL;
|
||||||
|
}
|
||||||
|
out = out.substr(pos + 1);
|
||||||
|
}
|
||||||
|
if (std::from_chars(out.data(), out.data() + out.size(), voltage).ec == std::errc::invalid_argument)
|
||||||
|
return command_result::FAIL;
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
command_result get_battery_status_sim7xxx(CommandableIf* t, int& voltage, int &bcs, int &bcl)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string_view out;
|
||||||
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
auto ret = generic_get_string(t, "AT+CBC\r", out);
|
||||||
if (ret != command_result::OK)
|
if (ret != command_result::OK)
|
||||||
return ret;
|
return ret;
|
||||||
if (out.find("+CBC") == std::string::npos)
|
|
||||||
return command_result::FAIL;
|
|
||||||
// Parsing +CBC: <voltage in Volts> V
|
// Parsing +CBC: <voltage in Volts> V
|
||||||
|
constexpr std::string_view pattern = "+CBC: ";
|
||||||
|
constexpr int num_pos = pattern.size();
|
||||||
|
int dot_pos;
|
||||||
|
if (out.find(pattern) == std::string::npos ||
|
||||||
|
(dot_pos = out.find('.')) == std::string::npos)
|
||||||
|
return command_result::FAIL;
|
||||||
|
|
||||||
int volt, fraction;
|
int volt, fraction;
|
||||||
sscanf(out.c_str(), "+CBC: %d.%dV", &volt, &fraction);
|
if (std::from_chars(out.data() + num_pos, out.data() + dot_pos, volt).ec == std::errc::invalid_argument)
|
||||||
|
return command_result::FAIL;
|
||||||
|
if (std::from_chars(out.data() + dot_pos + 1, out.data() + out.size() - 1, fraction).ec == std::errc::invalid_argument)
|
||||||
|
return command_result::FAIL;
|
||||||
bcl = bcs = -1; // not available for these models
|
bcl = bcs = -1; // not available for these models
|
||||||
voltage = 1000*volt + fraction;
|
voltage = 1000*volt + fraction;
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
@ -145,7 +193,7 @@ command_result set_flow_control(CommandableIf* t, int dce_flow, int dte_flow)
|
|||||||
|
|
||||||
command_result get_operator_name(CommandableIf* t, std::string& operator_name)
|
command_result get_operator_name(CommandableIf* t, std::string& operator_name)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string_view out;
|
||||||
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
|
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
|
||||||
if (ret != command_result::OK)
|
if (ret != command_result::OK)
|
||||||
return ret;
|
return ret;
|
||||||
@ -193,19 +241,9 @@ command_result resume_data_mode(CommandableIf* t)
|
|||||||
|
|
||||||
command_result set_command_mode(CommandableIf* t)
|
command_result set_command_mode(CommandableIf* t)
|
||||||
{
|
{
|
||||||
std::cout << "Sending +++" << std::endl;
|
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
|
||||||
return t->command("+++", [&](uint8_t *data, size_t len) {
|
const auto fail = std::list<std::string_view>({"ERROR"});
|
||||||
std::string response((char*)data, len);
|
return generic_command(t, "+++", pass, fail, 5000);
|
||||||
std::cout << response << std::endl;
|
|
||||||
if (response.find("OK") != std::string::npos) {
|
|
||||||
return command_result::OK;
|
|
||||||
} else if (response.find("NO CARRIER") != std::string::npos) {
|
|
||||||
return command_result::OK;
|
|
||||||
} else if (response.find("ERROR") != std::string::npos) {
|
|
||||||
return command_result::FAIL;
|
|
||||||
}
|
|
||||||
return command_result::TIMEOUT;
|
|
||||||
}, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result get_imsi(CommandableIf* t, std::string& imsi_number)
|
command_result get_imsi(CommandableIf* t, std::string& imsi_number)
|
||||||
@ -239,8 +277,8 @@ command_result sms_character_set(CommandableIf* t)
|
|||||||
command_result send_sms(CommandableIf* t, const std::string& number, const std::string& message)
|
command_result send_sms(CommandableIf* t, const std::string& number, const std::string& message)
|
||||||
{
|
{
|
||||||
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
|
auto ret = t->command("AT+CMGS=\"" + number + "\"\r", [&](uint8_t *data, size_t len) {
|
||||||
std::string response((char*)data, len);
|
std::string_view response((char*)data, len);
|
||||||
std::cout << response << std::endl;
|
printf("%.*s", static_cast<int>(response.size()), response.data());
|
||||||
if (response.find('>') != std::string::npos) {
|
if (response.find('>') != std::string::npos) {
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
}
|
}
|
||||||
@ -259,53 +297,49 @@ command_result set_cmux(CommandableIf* t)
|
|||||||
|
|
||||||
command_result read_pin(CommandableIf* t, bool& pin_ok)
|
command_result read_pin(CommandableIf* t, bool& pin_ok)
|
||||||
{
|
{
|
||||||
std::cout << "Sending read_pin" << std::endl;
|
std::string_view out;
|
||||||
return t->command("AT+CPIN?\r", [&](uint8_t *data, size_t len) {
|
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
|
||||||
std::string response((char*)data, len);
|
if (ret != command_result::OK)
|
||||||
std::cout << response << std::endl;
|
return ret;
|
||||||
if (response.find("READY") != std::string::npos) {
|
if (out.find("+CPIN:") == std::string::npos)
|
||||||
pin_ok = true;
|
return command_result::FAIL;
|
||||||
return command_result::OK;
|
if (out.find("SIM PIN") != std::string::npos || out.find("SIM PUK") != std::string::npos) {
|
||||||
} else if (response.find("PIN") != std::string::npos || response.find("PUK") != std::string::npos ) {
|
pin_ok = false;
|
||||||
pin_ok = false;
|
return command_result::OK;
|
||||||
return command_result::OK;
|
}
|
||||||
} else if (response.find("ERROR") != std::string::npos) {
|
if (out.find("READY") != std::string::npos) {
|
||||||
return command_result::FAIL;
|
pin_ok = true;
|
||||||
}
|
return command_result::OK;
|
||||||
return command_result::TIMEOUT;
|
}
|
||||||
}, 5000);
|
return command_result::FAIL; // Neither pin-ok, nor waiting for pin/puk -> mark as error
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result set_pin(CommandableIf* t, const std::string& pin)
|
command_result set_pin(CommandableIf* t, const std::string& pin)
|
||||||
{
|
{
|
||||||
std::cout << "Sending set_pin" << std::endl;
|
|
||||||
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
|
std::string set_pin_command = "AT+CPIN=" + pin + "\r";
|
||||||
return generic_command_common(t, set_pin_command);
|
return generic_command_common(t, set_pin_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
command_result get_signal_quality(CommandableIf* t, int &rssi, int &ber)
|
command_result get_signal_quality(CommandableIf* t, int &rssi, int &ber)
|
||||||
{
|
{
|
||||||
std::cout << "get_signal_quality" << std::endl;
|
printf("%s", __func__ );
|
||||||
return t->command("AT+CSQ\r", [&](uint8_t *data, size_t len) {
|
std::string_view out;
|
||||||
size_t pos = 0;
|
auto ret = generic_get_string(t, "AT+CSQ\r", out);
|
||||||
std::string response((char*)data, len);
|
if (ret != command_result::OK)
|
||||||
while ((pos = response.find('\n')) != std::string::npos) {
|
return ret;
|
||||||
std::string token = response.substr(0, pos);
|
|
||||||
for (auto i = 0; i<2; ++i) // trip trailing \n\r of last two chars
|
|
||||||
if (pos >= 1 && (token[pos-1] == '\r' || token[pos-1] == '\n'))
|
|
||||||
token.pop_back();
|
|
||||||
|
|
||||||
if (token.find("OK") != std::string::npos) {
|
constexpr std::string_view pattern = "+CSQ: ";
|
||||||
return command_result::OK;
|
constexpr int rssi_pos = pattern.size();
|
||||||
} else if (token.find("ERROR") != std::string::npos) {
|
int ber_pos;
|
||||||
return command_result::FAIL;
|
if (out.find(pattern) == std::string::npos ||
|
||||||
} else if (token.find("+CSQ") != std::string::npos) {
|
(ber_pos = out.find(',')) == std::string::npos)
|
||||||
sscanf(token.c_str(), "%*s%d,%d", &rssi, &ber);
|
return command_result::FAIL;
|
||||||
}
|
|
||||||
response = response.substr(pos+1);
|
if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument)
|
||||||
}
|
return command_result::FAIL;
|
||||||
return command_result::TIMEOUT;
|
if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument)
|
||||||
}, 500);
|
return command_result::FAIL;
|
||||||
|
return command_result::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // esp_modem::dce_commands
|
} // esp_modem::dce_commands
|
@ -39,18 +39,19 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui
|
|||||||
command_result res = command_result::TIMEOUT;
|
command_result res = command_result::TIMEOUT;
|
||||||
command_term->set_read_cb([&](uint8_t *data, size_t len) {
|
command_term->set_read_cb([&](uint8_t *data, size_t len) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
auto data_to_read = std::min(len, buffer_size - consumed);
|
data = buffer.get(); // + consumed;
|
||||||
data = buffer.get() + consumed;
|
len = command_term->read(data + consumed, buffer_size - consumed);
|
||||||
len = command_term->read(data, data_to_read);
|
} else {
|
||||||
|
consumed = 0;
|
||||||
}
|
}
|
||||||
consumed += len;
|
if (memchr(data + consumed, separator, len)) {
|
||||||
if (memchr(data, separator, len)) {
|
res = got_line(data, consumed + len);
|
||||||
res = got_line(data, consumed);
|
|
||||||
if (res == command_result::OK || res == command_result::FAIL) {
|
if (res == command_result::OK || res == command_result::FAIL) {
|
||||||
signal.set(GOT_LINE);
|
signal.set(GOT_LINE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
consumed += len;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
command_term->write((uint8_t *)command.c_str(), command.length());
|
command_term->write((uint8_t *)command.c_str(), command.length());
|
||||||
@ -107,9 +108,8 @@ void DTE::set_read_cb(std::function<bool(uint8_t *, size_t)> f)
|
|||||||
on_data = std::move(f);
|
on_data = std::move(f);
|
||||||
term->set_read_cb([this](uint8_t *data, size_t len) {
|
term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||||
if (!data) { // if no data available from terminal callback -> need to explicitly read some
|
if (!data) { // if no data available from terminal callback -> need to explicitly read some
|
||||||
auto data_to_read = std::min(len, buffer_size - consumed);
|
|
||||||
data = buffer.get();
|
data = buffer.get();
|
||||||
len = term->read(data, data_to_read);
|
len = term->read(buffer.get(), buffer_size);
|
||||||
}
|
}
|
||||||
if (on_data)
|
if (on_data)
|
||||||
return on_data(data, len);
|
return on_data(data, len);
|
||||||
|
@ -18,12 +18,8 @@
|
|||||||
#include "freertos/event_groups.h"
|
#include "freertos/event_groups.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace esp_modem {
|
namespace esp_modem {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Lock::unlock() {
|
void Lock::unlock() {
|
||||||
xSemaphoreGiveRecursive(m);
|
xSemaphoreGiveRecursive(m);
|
||||||
}
|
}
|
||||||
@ -43,42 +39,66 @@ void Lock::lock() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
signal_group::signal_group(): event_group(nullptr)
|
SignalGroup::SignalGroup(): event_group(nullptr)
|
||||||
{
|
{
|
||||||
event_group = xEventGroupCreate();
|
event_group = xEventGroupCreate();
|
||||||
throw_if_false(event_group != nullptr, "create signal event group failed");
|
throw_if_false(event_group != nullptr, "create signal event group failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_group::set(uint32_t bits)
|
void SignalGroup::set(uint32_t bits)
|
||||||
{
|
{
|
||||||
xEventGroupSetBits(event_group, bits);
|
xEventGroupSetBits(event_group, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_group::clear(uint32_t bits)
|
void SignalGroup::clear(uint32_t bits)
|
||||||
{
|
{
|
||||||
xEventGroupClearBits(event_group, bits);
|
xEventGroupClearBits(event_group, bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::wait(uint32_t flags, uint32_t time_ms)
|
bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
|
||||||
{
|
{
|
||||||
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdTRUE, pdTRUE, pdMS_TO_TICKS(time_ms));
|
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdTRUE, pdTRUE, pdMS_TO_TICKS(time_ms));
|
||||||
return bits & flags;
|
return bits & flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::is_any(uint32_t flags)
|
bool SignalGroup::is_any(uint32_t flags)
|
||||||
{
|
{
|
||||||
return xEventGroupGetBits(event_group) & flags;
|
return xEventGroupGetBits(event_group) & flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::wait_any(uint32_t flags, uint32_t time_ms)
|
bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
|
||||||
{
|
{
|
||||||
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdFALSE, pdFALSE, pdMS_TO_TICKS(time_ms));
|
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdFALSE, pdFALSE, pdMS_TO_TICKS(time_ms));
|
||||||
return bits & flags;
|
return bits & flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
signal_group::~signal_group()
|
SignalGroup::~SignalGroup()
|
||||||
{
|
{
|
||||||
if (event_group) vEventGroupDelete(event_group);
|
if (event_group)
|
||||||
|
vEventGroupDelete(event_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function)
|
||||||
|
:task_handle(nullptr)
|
||||||
|
{
|
||||||
|
BaseType_t ret = xTaskCreate(task_function, "vfs_task", stack_size, task_param, priority, &task_handle);
|
||||||
|
throw_if_false(ret == pdTRUE, "create vfs task failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Task::~Task()
|
||||||
|
{
|
||||||
|
if (task_handle)
|
||||||
|
vTaskDelete(task_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Task::Delete()
|
||||||
|
{
|
||||||
|
vTaskDelete(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Task::Relinquish()
|
||||||
|
{
|
||||||
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esp_modem
|
} // namespace esp_modem
|
@ -13,35 +13,35 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <unistd.h>
|
||||||
#include "cxx_include/esp_modem_primitives.hpp"
|
#include "cxx_include/esp_modem_primitives.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace esp_modem {
|
namespace esp_modem {
|
||||||
|
|
||||||
struct SignalGroup {
|
struct SignalGroupInternal {
|
||||||
std::condition_variable notify;
|
std::condition_variable notify;
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
uint32_t flags{ 0 };
|
uint32_t flags{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
signal_group::signal_group(): event_group(std::make_unique<SignalGroup>())
|
SignalGroup::SignalGroup(): event_group(std::make_unique<SignalGroupInternal>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_group::set(uint32_t bits)
|
void SignalGroup::set(uint32_t bits)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(event_group->m);
|
std::unique_lock<std::mutex> lock(event_group->m);
|
||||||
event_group->flags |= bits;
|
event_group->flags |= bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_group::clear(uint32_t bits)
|
void SignalGroup::clear(uint32_t bits)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(event_group->m);
|
std::unique_lock<std::mutex> lock(event_group->m);
|
||||||
event_group->flags &= ~bits;
|
event_group->flags &= ~bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::wait(uint32_t flags, uint32_t time_ms)
|
bool SignalGroup::wait(uint32_t flags, uint32_t time_ms)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(event_group->m);
|
std::unique_lock<std::mutex> lock(event_group->m);
|
||||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{
|
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{
|
||||||
@ -55,18 +55,35 @@ bool signal_group::wait(uint32_t flags, uint32_t time_ms)
|
|||||||
// , [&]{return flags&event_group->flags; });
|
// , [&]{return flags&event_group->flags; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::is_any(uint32_t flags)
|
bool SignalGroup::is_any(uint32_t flags)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(event_group->m);
|
std::unique_lock<std::mutex> lock(event_group->m);
|
||||||
return flags&event_group->flags;
|
return flags&event_group->flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signal_group::wait_any(uint32_t flags, uint32_t time_ms)
|
bool SignalGroup::wait_any(uint32_t flags, uint32_t time_ms)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(event_group->m);
|
std::unique_lock<std::mutex> lock(event_group->m);
|
||||||
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ return flags&event_group->flags; });
|
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&]{ return flags&event_group->flags; });
|
||||||
}
|
}
|
||||||
|
|
||||||
signal_group::~signal_group() = default;
|
SignalGroup::~SignalGroup() = default;
|
||||||
|
|
||||||
|
Task::Task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function)
|
||||||
|
{
|
||||||
|
#warning "Define this for linux"
|
||||||
|
}
|
||||||
|
|
||||||
|
Task::~Task()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Task::Delete() {}
|
||||||
|
|
||||||
|
void Task::Relinquish()
|
||||||
|
{
|
||||||
|
usleep(0);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace esp_modem
|
} // namespace esp_modem
|
@ -12,15 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#include <sys/fcntl.h>
|
#include <sys/fcntl.h>
|
||||||
#include "cxx_include/esp_modem_dte.hpp"
|
#include "cxx_include/esp_modem_dte.hpp"
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "esp_modem_config.h"
|
#include "esp_modem_config.h"
|
||||||
#include "exception_stub.hpp"
|
#include "exception_stub.hpp"
|
||||||
#include "esp_vfs_dev.h"
|
|
||||||
|
|
||||||
static const char *TAG = "fs_terminal";
|
static const char *TAG = "fs_terminal";
|
||||||
|
|
||||||
@ -33,68 +31,16 @@ struct uart_resource {
|
|||||||
int fd;
|
int fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vfs_task {
|
|
||||||
explicit vfs_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) :
|
|
||||||
task_handle(nullptr) {
|
|
||||||
BaseType_t ret = xTaskCreate(task_function, "vfs_task"
|
|
||||||
"", stack_size, task_param, priority, &task_handle);
|
|
||||||
throw_if_false(ret == pdTRUE, "create uart event task failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
~vfs_task() {
|
|
||||||
if (task_handle) vTaskDelete(task_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskHandle_t task_handle; /*!< UART event task handle */
|
|
||||||
};
|
|
||||||
|
|
||||||
uart_resource::~uart_resource()
|
|
||||||
{
|
|
||||||
if (port >= UART_NUM_0 && port < UART_NUM_MAX) {
|
|
||||||
uart_driver_delete(port);
|
|
||||||
}
|
|
||||||
if (fd >= 0) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uart_resource::uart_resource(const esp_modem_dte_config *config) :
|
|
||||||
port(-1), fd(-1)
|
|
||||||
{
|
|
||||||
/* Config UART */
|
|
||||||
uart_config_t uart_config = {};
|
|
||||||
uart_config.baud_rate = config->vfs_config.baud_rate;
|
|
||||||
uart_config.data_bits = UART_DATA_8_BITS;
|
|
||||||
uart_config.parity = UART_PARITY_DISABLE;
|
|
||||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
|
||||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
|
||||||
uart_config.source_clk = UART_SCLK_REF_TICK;
|
|
||||||
|
|
||||||
throw_if_esp_fail(uart_param_config(config->vfs_config.port_num, &uart_config), "config uart parameter failed");
|
|
||||||
|
|
||||||
throw_if_esp_fail(uart_set_pin(config->vfs_config.port_num, config->vfs_config.tx_io_num, config->vfs_config.rx_io_num,
|
|
||||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), "config uart gpio failed");
|
|
||||||
|
|
||||||
throw_if_esp_fail(uart_driver_install(config->vfs_config.port_num, config->vfs_config.rx_buffer_size, config->vfs_config.tx_buffer_size,
|
|
||||||
0, nullptr, 0), "install uart driver failed");
|
|
||||||
|
|
||||||
throw_if_esp_fail(uart_set_rx_timeout(config->vfs_config.port_num, 1), "set rx timeout failed");
|
|
||||||
|
|
||||||
throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed");
|
|
||||||
|
|
||||||
/* mark UART as initialized */
|
|
||||||
port = config->vfs_config.port_num;
|
|
||||||
|
|
||||||
fd = open(config->vfs_config.dev_name, O_RDWR);
|
|
||||||
|
|
||||||
throw_if_false(fd >= 0, "Cannot open the fd");
|
|
||||||
}
|
|
||||||
|
|
||||||
class vfs_terminal : public Terminal {
|
class vfs_terminal : public Terminal {
|
||||||
public:
|
public:
|
||||||
explicit vfs_terminal(const esp_modem_dte_config *config) :
|
explicit vfs_terminal(const esp_modem_dte_config *config) :
|
||||||
uart(config), signal(),
|
uart(config), signal(),
|
||||||
task_handle(config->vfs_config.task_stack_size, config->vfs_config.task_prio, this, s_task) {}
|
task_handle(config->vfs_config.task_stack_size, config->vfs_config.task_prio, this, [](void* p){
|
||||||
|
auto t = static_cast<vfs_terminal *>(p);
|
||||||
|
t->task();
|
||||||
|
Task::Delete();
|
||||||
|
}) {}
|
||||||
|
|
||||||
~vfs_terminal() override = default;
|
~vfs_terminal() override = default;
|
||||||
|
|
||||||
@ -116,22 +62,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void s_task(void *task_param) {
|
|
||||||
auto t = static_cast<vfs_terminal *>(task_param);
|
|
||||||
t->task();
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void task();
|
void task();
|
||||||
|
|
||||||
static const size_t TASK_INIT = BIT0;
|
static const size_t TASK_INIT = SignalGroup::bit0;
|
||||||
static const size_t TASK_START = BIT1;
|
static const size_t TASK_START = SignalGroup::bit1;
|
||||||
static const size_t TASK_STOP = BIT2;
|
static const size_t TASK_STOP = SignalGroup::bit2;
|
||||||
static const size_t TASK_PARAMS = BIT3;
|
static const size_t TASK_PARAMS = SignalGroup::bit3;
|
||||||
|
|
||||||
uart_resource uart;
|
uart_resource uart;
|
||||||
signal_group signal;
|
SignalGroup signal;
|
||||||
vfs_task task_handle;
|
Task task_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config) {
|
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config) {
|
||||||
@ -144,13 +84,15 @@ std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config
|
|||||||
|
|
||||||
void vfs_terminal::task() {
|
void vfs_terminal::task() {
|
||||||
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
|
std::function<bool(uint8_t *data, size_t len)> on_data_priv = nullptr;
|
||||||
size_t len;
|
// size_t len;
|
||||||
signal.set(TASK_INIT);
|
signal.set(TASK_INIT);
|
||||||
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
|
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
|
||||||
if (signal.is_any(TASK_STOP)) {
|
if (signal.is_any(TASK_STOP)) {
|
||||||
return; // exits to the static method where the task gets deleted
|
return; // exits to the static method where the task gets deleted
|
||||||
}
|
}
|
||||||
esp_vfs_dev_uart_use_driver(uart.port);
|
// esp_vfs_dev_uart_use_driver(uart.port);
|
||||||
|
int flags = fcntl(uart.fd, F_GETFL, NULL) | O_NONBLOCK;
|
||||||
|
fcntl(uart.fd, F_SETFL, flags);
|
||||||
|
|
||||||
while (signal.is_any(TASK_START)) {
|
while (signal.is_any(TASK_START)) {
|
||||||
int s;
|
int s;
|
||||||
@ -163,35 +105,36 @@ void vfs_terminal::task() {
|
|||||||
FD_SET(uart.fd, &rfds);
|
FD_SET(uart.fd, &rfds);
|
||||||
|
|
||||||
s = select(uart.fd + 1, &rfds, NULL, NULL, &tv);
|
s = select(uart.fd + 1, &rfds, NULL, NULL, &tv);
|
||||||
// if (signal.is_any(TASK_PARAMS)) {
|
if (signal.is_any(TASK_PARAMS)) {
|
||||||
on_data_priv = on_data;
|
on_data_priv = on_data;
|
||||||
// signal.clear(TASK_PARAMS);
|
signal.clear(TASK_PARAMS);
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
ESP_LOGE(TAG, "Select failed: errno %d", errno);
|
|
||||||
break;
|
break;
|
||||||
} else if (s == 0) {
|
} else if (s == 0) {
|
||||||
ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
|
|
||||||
} else {
|
} else {
|
||||||
if (FD_ISSET(uart.fd, &rfds)) {
|
if (FD_ISSET(uart.fd, &rfds)) {
|
||||||
uart_get_buffered_data_len(uart.port, &len);
|
// uart_get_buffered_data_len(uart.port, &len);
|
||||||
if (len && on_data_priv) {
|
if (on_data_priv) {
|
||||||
if (on_data_priv(nullptr, len)) {
|
if (on_data_priv(nullptr, 0)) {
|
||||||
on_data_priv = nullptr;
|
on_data_priv = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vTaskDelay(1);
|
Task::Relinquish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_terminal::read(uint8_t *data, size_t len) {
|
int vfs_terminal::read(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
return ::read(uart.fd, data, len);
|
return ::read(uart.fd, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_terminal::write(uint8_t *data, size_t len) {
|
int vfs_terminal::write(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
return ::write(uart.fd, data, len);
|
return ::write(uart.fd, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
77
esp_modem/src/esp_modem_term_uart.cpp
Normal file
77
esp_modem/src/esp_modem_term_uart.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2021 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.
|
||||||
|
|
||||||
|
|
||||||
|
#include "cxx_include/esp_modem_dte.hpp"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "esp_modem_config.h"
|
||||||
|
#include "exception_stub.hpp"
|
||||||
|
#include "esp_vfs_dev.h"
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static const char *TAG = "uart_terminal";
|
||||||
|
|
||||||
|
namespace esp_modem::terminal {
|
||||||
|
|
||||||
|
struct uart_resource {
|
||||||
|
explicit uart_resource(const esp_modem_dte_config *config);
|
||||||
|
~uart_resource();
|
||||||
|
uart_port_t port;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
uart_resource::uart_resource(const esp_modem_dte_config *config) :
|
||||||
|
port(-1), fd(-1)
|
||||||
|
{
|
||||||
|
/* Config UART */
|
||||||
|
uart_config_t uart_config = {};
|
||||||
|
uart_config.baud_rate = config->vfs_config.baud_rate;
|
||||||
|
uart_config.data_bits = UART_DATA_8_BITS;
|
||||||
|
uart_config.parity = UART_PARITY_DISABLE;
|
||||||
|
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||||
|
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||||
|
uart_config.source_clk = UART_SCLK_REF_TICK;
|
||||||
|
|
||||||
|
throw_if_esp_fail(uart_param_config(config->vfs_config.port_num, &uart_config), "config uart parameter failed");
|
||||||
|
|
||||||
|
throw_if_esp_fail(uart_set_pin(config->vfs_config.port_num, config->vfs_config.tx_io_num, config->vfs_config.rx_io_num,
|
||||||
|
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), "config uart gpio failed");
|
||||||
|
|
||||||
|
throw_if_esp_fail(uart_driver_install(config->vfs_config.port_num, config->vfs_config.rx_buffer_size, config->vfs_config.tx_buffer_size,
|
||||||
|
0, nullptr, 0), "install uart driver failed");
|
||||||
|
|
||||||
|
// throw_if_esp_fail(uart_set_rx_timeout(config->vfs_config.port_num, 1), "set rx timeout failed");
|
||||||
|
//
|
||||||
|
// throw_if_esp_fail(uart_set_rx_full_threshold(config->uart_config.port_num, 64), "config rx full threshold failed");
|
||||||
|
|
||||||
|
/* mark UART as initialized */
|
||||||
|
port = config->vfs_config.port_num;
|
||||||
|
esp_vfs_dev_uart_use_driver(port);
|
||||||
|
|
||||||
|
fd = open(config->vfs_config.dev_name, O_RDWR);
|
||||||
|
|
||||||
|
throw_if_false(fd >= 0, "Cannot open the fd");
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_resource::~uart_resource() {
|
||||||
|
if (port >= UART_NUM_0 && port < UART_NUM_MAX) {
|
||||||
|
uart_driver_delete(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esp_modem::terminal
|
@ -150,7 +150,7 @@ private:
|
|||||||
static const size_t TASK_PARAMS = BIT3;
|
static const size_t TASK_PARAMS = BIT3;
|
||||||
|
|
||||||
uart_resource uart;
|
uart_resource uart;
|
||||||
signal_group signal;
|
SignalGroup signal;
|
||||||
uart_task task_handle;
|
uart_task task_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
idf_component_register(SRCS "test_modem.cpp"
|
idf_component_register(SRCS "test_modem.cpp" "LoopbackTerm.cpp"
|
||||||
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
|
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
|
||||||
REQUIRES esp_modem)
|
REQUIRES esp_modem)
|
||||||
|
|
||||||
|
82
esp_modem/test/host_test/main/LoopbackTerm.cpp
Normal file
82
esp_modem/test/host_test/main/LoopbackTerm.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <future>
|
||||||
|
#include <cstring>
|
||||||
|
#include "LoopbackTerm.h"
|
||||||
|
|
||||||
|
void LoopbackTerm::start()
|
||||||
|
{
|
||||||
|
status = status_t::STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoopbackTerm::stop()
|
||||||
|
{
|
||||||
|
status = status_t::STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LoopbackTerm::write(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder
|
||||||
|
std::string command((char*)data, len);
|
||||||
|
std::string response;
|
||||||
|
if (command == "+++") {
|
||||||
|
response = "NO CARRIER\r\n";
|
||||||
|
} else if (command == "ATE1\r" || command == "ATE0\r") {
|
||||||
|
response = "OK\r\n";
|
||||||
|
} else if (command == "ATO\r") {
|
||||||
|
response = "ERROR\r\n";
|
||||||
|
} else if (command.find("ATD") != std::string::npos) {
|
||||||
|
response = "CONNECT\r\n";
|
||||||
|
} else if (command.find("AT+CSQ\r") != std::string::npos) {
|
||||||
|
response = "+CSQ: 123,456\n\r\nOK\r\n";
|
||||||
|
} else if (command.find("AT+CBC\r") != std::string::npos) {
|
||||||
|
response = is_bg96 ? "+CBC: 1,2,123456V\r\r\n\r\nOK\r\n\n\r\n":
|
||||||
|
"+CBC: 123.456V\r\r\n\r\nOK\r\n\n\r\n";
|
||||||
|
} else if (command.find("AT+CPIN=1234\r") != std::string::npos) {
|
||||||
|
response = "OK\r\n";
|
||||||
|
pin_ok = true;
|
||||||
|
} else if (command.find("AT+CPIN?\r") != std::string::npos) {
|
||||||
|
response = pin_ok?"+CPIN: READY\r\nOK\r\n":"+CPIN: SIM PIN\r\nOK\r\n";
|
||||||
|
} else if (command.find("AT") != std::string::npos) {
|
||||||
|
response = "OK\r\n";
|
||||||
|
}
|
||||||
|
if (!response.empty()) {
|
||||||
|
data_len = response.length();
|
||||||
|
loopback_data.resize(data_len);
|
||||||
|
memcpy(&loopback_data[0], &response[0], data_len);
|
||||||
|
auto ret = std::async(on_data, nullptr, data_len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
|
||||||
|
// turn the request into a reply -> implements CMUX loopback
|
||||||
|
if (data[2] == 0x3f) // SABM command
|
||||||
|
data[2] = 0x73;
|
||||||
|
else if (data[2] == 0xef) { // Generic request
|
||||||
|
data[2] = 0xff; // generic reply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loopback_data.resize(data_len + len);
|
||||||
|
memcpy(&loopback_data[data_len], data, len);
|
||||||
|
data_len += len;
|
||||||
|
auto ret = std::async(on_data, nullptr, data_len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LoopbackTerm::read(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
size_t read_len = std::min(data_len, len);
|
||||||
|
if (read_len) {
|
||||||
|
if (loopback_data.capacity() < len)
|
||||||
|
loopback_data.reserve(len);
|
||||||
|
memcpy(data, &loopback_data[0], read_len);
|
||||||
|
loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len);
|
||||||
|
data_len -= read_len;
|
||||||
|
}
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoopbackTerm::LoopbackTerm(bool is_bg96): loopback_data(), data_len(0), pin_ok(false), is_bg96(is_bg96) {}
|
||||||
|
|
||||||
|
LoopbackTerm::LoopbackTerm(): loopback_data(), data_len(0), pin_ok(false), is_bg96(false) {}
|
||||||
|
|
||||||
|
LoopbackTerm::~LoopbackTerm() = default;
|
36
esp_modem/test/host_test/main/LoopbackTerm.h
Normal file
36
esp_modem/test/host_test/main/LoopbackTerm.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef _LOOPBACKTERM_H_
|
||||||
|
#define _LOOPBACKTERM_H_
|
||||||
|
|
||||||
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
|
#include "cxx_include/esp_modem_terminal.hpp"
|
||||||
|
|
||||||
|
using namespace esp_modem;
|
||||||
|
|
||||||
|
class LoopbackTerm : public Terminal {
|
||||||
|
public:
|
||||||
|
explicit LoopbackTerm(bool is_bg96);
|
||||||
|
explicit LoopbackTerm();
|
||||||
|
|
||||||
|
~LoopbackTerm() override;
|
||||||
|
|
||||||
|
void start() override;
|
||||||
|
void stop() override;
|
||||||
|
|
||||||
|
int write(uint8_t *data, size_t len) override;
|
||||||
|
|
||||||
|
int read(uint8_t *data, size_t len) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class status_t {
|
||||||
|
STARTED,
|
||||||
|
STOPPED
|
||||||
|
};
|
||||||
|
status_t status;
|
||||||
|
SignalGroup signal;
|
||||||
|
std::vector<uint8_t> loopback_data;
|
||||||
|
size_t data_len;
|
||||||
|
bool pin_ok;
|
||||||
|
bool is_bg96;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_LOOPBACKTERM_H_
|
@ -2,82 +2,44 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include "cxx_include/esp_modem_terminal.hpp"
|
|
||||||
#include "cxx_include/esp_modem_api.hpp"
|
#include "cxx_include/esp_modem_api.hpp"
|
||||||
|
#include "LoopbackTerm.h"
|
||||||
|
|
||||||
using namespace esp_modem;
|
using namespace esp_modem;
|
||||||
|
|
||||||
class LoopbackTerm : public Terminal {
|
TEST_CASE("DCE AT parser", "[esp_modem]")
|
||||||
public:
|
{
|
||||||
explicit LoopbackTerm(): loopback_data(), data_len(0) {}
|
auto term = std::make_unique<LoopbackTerm>(true);
|
||||||
|
auto dte = std::make_shared<DTE>(std::move(term));
|
||||||
|
CHECK(term == nullptr);
|
||||||
|
|
||||||
~LoopbackTerm() override = default;
|
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN");
|
||||||
|
esp_netif_t netif{};
|
||||||
|
auto dce = create_BG96_dce(&dce_config, dte, &netif);
|
||||||
|
CHECK(dce != nullptr);
|
||||||
|
|
||||||
void start() override {
|
CHECK(dce->set_command_mode() == command_result::OK);
|
||||||
status = status_t::STARTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop() override {
|
int milli_volt, bcl, bcs;
|
||||||
status = status_t::STOPPED;
|
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
|
||||||
}
|
CHECK(milli_volt == 123456);
|
||||||
|
CHECK(bcl == 1);
|
||||||
|
CHECK(bcs == 2);
|
||||||
|
|
||||||
int write(uint8_t *data, size_t len) override {
|
int rssi, ber;
|
||||||
if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder
|
CHECK(dce->get_signal_quality(rssi, ber) == command_result::OK);
|
||||||
std::string command((char*)data, len);
|
CHECK(rssi == 123);
|
||||||
std::string response;
|
CHECK(ber == 456);
|
||||||
if (command == "ATE1\r" || command == "ATE0\r" || command == "+++") {
|
|
||||||
response = "OK\r\n";
|
|
||||||
} else if (command == "ATO\r") {
|
|
||||||
response = "ERROR\r\n";
|
|
||||||
} else if (command.find("ATD") != std::string::npos) {
|
|
||||||
response = "CONNECT\r\n";
|
|
||||||
} else if (command.find("AT") != std::string::npos) {
|
|
||||||
response = "OK\r\n";
|
|
||||||
}
|
|
||||||
if (!response.empty()) {
|
|
||||||
data_len = response.length();
|
|
||||||
loopback_data.resize(data_len);
|
|
||||||
memcpy(&loopback_data[0], &response[0], data_len);
|
|
||||||
auto ret = std::async(on_data, nullptr, data_len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len > 2 && data[0] == 0xf9) { // Simple CMUX responder
|
|
||||||
// turn the request into a reply -> implements CMUX loopback
|
|
||||||
if (data[2] == 0x3f) // SABM command
|
|
||||||
data[2] = 0x73;
|
|
||||||
else if (data[2] == 0xef) // Generic request
|
|
||||||
data[2] = 0xff; // generic reply
|
|
||||||
}
|
|
||||||
loopback_data.resize(data_len + len);
|
|
||||||
memcpy(&loopback_data[data_len], data, len);
|
|
||||||
data_len += len;
|
|
||||||
auto ret = std::async(on_data, nullptr, data_len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read(uint8_t *data, size_t len) override {
|
bool pin_ok;
|
||||||
size_t read_len = std::min(data_len, len);
|
CHECK(dce->read_pin(pin_ok) == command_result::OK);
|
||||||
if (read_len) {
|
CHECK(pin_ok == false);
|
||||||
memcpy(data, &loopback_data[0], len);
|
CHECK(dce->set_pin("1234") == command_result::OK);
|
||||||
loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len);
|
CHECK(dce->read_pin(pin_ok) == command_result::OK);
|
||||||
data_len -= len;
|
CHECK(pin_ok == true);
|
||||||
}
|
}
|
||||||
return read_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum class status_t {
|
|
||||||
STARTED,
|
|
||||||
STOPPED
|
|
||||||
};
|
|
||||||
status_t status;
|
|
||||||
signal_group signal;
|
|
||||||
std::vector<uint8_t> loopback_data;
|
|
||||||
size_t data_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("DTE send/receive command", "[esp_modem]")
|
TEST_CASE("DTE send/receive command", "[esp_modem]")
|
||||||
{
|
{
|
||||||
auto term = std::make_unique<LoopbackTerm>();
|
auto term = std::make_unique<LoopbackTerm>();
|
||||||
@ -127,11 +89,15 @@ TEST_CASE("DCE AT commands", "[esp_modem]")
|
|||||||
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
|
auto dce = create_SIM7600_dce(&dce_config, dte, &netif);
|
||||||
CHECK(dce != nullptr);
|
CHECK(dce != nullptr);
|
||||||
|
|
||||||
|
int milli_volt, bcl, bcs;
|
||||||
CHECK(dce->set_echo(false) == command_result::OK);
|
CHECK(dce->set_echo(false) == command_result::OK);
|
||||||
CHECK(dce->set_echo(true) == command_result::OK);
|
CHECK(dce->set_echo(true) == command_result::OK);
|
||||||
|
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
|
||||||
|
CHECK(milli_volt == 123456);
|
||||||
CHECK(dce->resume_data_mode() == command_result::FAIL);
|
CHECK(dce->resume_data_mode() == command_result::FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("DCE modes", "[esp_modem]")
|
TEST_CASE("DCE modes", "[esp_modem]")
|
||||||
{
|
{
|
||||||
auto term = std::make_unique<LoopbackTerm>();
|
auto term = std::make_unique<LoopbackTerm>();
|
||||||
@ -162,8 +128,9 @@ TEST_CASE("DCE CMUX test", "[esp_modem]") {
|
|||||||
const auto test_command = "Test\n";
|
const auto test_command = "Test\n";
|
||||||
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
|
auto ret = dce->command(test_command, [&](uint8_t *data, size_t len) {
|
||||||
std::string response((char *) data, len);
|
std::string response((char *) data, len);
|
||||||
|
std::cout << "Response:" << response << std::endl;
|
||||||
CHECK(response == test_command);
|
CHECK(response == test_command);
|
||||||
return command_result::OK;
|
return command_result::OK;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
CHECK(ret == command_result::OK);
|
CHECK(ret == command_result::OK);
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
-- Found Git: /usr/bin/git (found version "2.30.2")
|
|
||||||
-- Component directory /home/david/esp/idf/components/aws_iot does not contain a CMakeLists.txt file. No component will be added
|
|
||||||
-- Component directory /home/david/esp/idf/components/micro-ecc does not contain a CMakeLists.txt file. No component will be added
|
|
||||||
-- Component directory /home/david/esp/idf/components/nimble does not contain a CMakeLists.txt file. No component will be added
|
|
||||||
-- ccache will be used for faster recompilation
|
|
||||||
-- The C compiler identification is GNU 10.2.1
|
|
||||||
-- The CXX compiler identification is GNU 10.2.1
|
|
||||||
-- The ASM compiler identification is GNU
|
|
||||||
-- Found assembler: /usr/lib64/ccache/cc
|
|
||||||
-- Detecting C compiler ABI info
|
|
||||||
-- Detecting C compiler ABI info - done
|
|
||||||
-- Check for working C compiler: /usr/lib64/ccache/cc - skipped
|
|
||||||
-- Detecting C compile features
|
|
||||||
-- Detecting C compile features - done
|
|
||||||
-- Detecting CXX compiler ABI info
|
|
||||||
-- Detecting CXX compiler ABI info - done
|
|
||||||
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped
|
|
||||||
-- Detecting CXX compile features
|
|
||||||
-- Detecting CXX compile features - done
|
|
||||||
-- Building ESP-IDF components for target linux
|
|
||||||
-- adding linux stuff...
|
|
||||||
-- Found Git: /usr/bin/git (found version "2.30.2")
|
|
||||||
-- Configuring incomplete, errors occurred!
|
|
||||||
See also "/home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build/CMakeFiles/CMakeOutput.log".
|
|
||||||
Adding "set-target"'s dependency "fullclean" to list of commands with default set of options.
|
|
||||||
Executing action: fullclean
|
|
||||||
Build directory '/home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build' not found. Nothing to clean.
|
|
||||||
Executing action: set-target
|
|
||||||
Set Target to: linux, new sdkconfig created. Existing sdkconfig renamed to sdkconfig.old.
|
|
||||||
Running cmake in directory /home/david/repos/esp-idf-contrib/esp_modem/test/host_test/build
|
|
||||||
Executing "cmake -G Ninja -DPYTHON_DEPS_CHECKED=1 -DESP_PLATFORM=1 -DIDF_TARGET=linux -DIDF_TARGET=linux -DCCACHE_ENABLE=1 /home/david/repos/esp-idf-contrib/esp_modem/test/host_test"...
|
|
@ -54,10 +54,23 @@ public:
|
|||||||
{
|
{
|
||||||
// configure
|
// configure
|
||||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||||
|
esp_modem_dte_config_t dte_config2 = {
|
||||||
|
.dte_buffer_size = 512,
|
||||||
|
.vfs_config = {.port_num = UART_NUM_1,
|
||||||
|
.dev_name = "/dev/uart/1",
|
||||||
|
.rx_buffer_size = 1024,
|
||||||
|
.tx_buffer_size = 1024,
|
||||||
|
.baud_rate = 115200,
|
||||||
|
.tx_io_num = 25,
|
||||||
|
.rx_io_num = 26,
|
||||||
|
.task_stack_size = 4096,
|
||||||
|
.task_prio = 5}
|
||||||
|
};
|
||||||
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("");
|
esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("");
|
||||||
|
|
||||||
// create DTE and minimal network DCE
|
// create DTE and minimal network DCE
|
||||||
auto uart_dte = create_uart_dte(&dte_config);
|
auto uart_dte = create_vfs_dte(&dte_config2);
|
||||||
|
// auto uart_dte = create_uart_dte(&dte_config);
|
||||||
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif);
|
dce = NetDCE_Factory::create<NetModule>(&dce_config, uart_dte, netif);
|
||||||
return dce == nullptr ? ESP_FAIL : ESP_OK;
|
return dce == nullptr ? ESP_FAIL : ESP_OK;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user