Corrections on fragmenting/buffering for vfs/cmux

This commit is contained in:
David Cermak
2021-05-13 07:28:05 +02:00
parent 927ad418b6
commit 0d9b5dd8b7
21 changed files with 578 additions and 335 deletions

View File

@ -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()

View File

@ -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);

View File

@ -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();

View File

@ -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;
}; };

View File

@ -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 */
}; };

View File

@ -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;
}; };
/** /**

View File

@ -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

View File

@ -85,22 +85,38 @@ 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;
if (payload_start)
data = payload_start + total_payload_size;
else
data = buffer.get(); data = buffer.get();
actual_len = term->read(data, data_to_read); actual_len = term->read(data, data_to_read);
} }
@ -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) {

View File

@ -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 {
command_result generic_command(CommandableIf* t, const std::string &command,
const std::list<std::string_view>& pass_phrase,
const std::list<std::string_view>& fail_phrase,
uint32_t timeout_ms)
{
printf("Command %s\n", command.c_str());
return t->command(command, [&](uint8_t *data, size_t len) {
std::string_view response((char*)data, len);
printf("Response: %.*s\n", (int)response.length(), response.data());
for (auto it : pass_phrase)
if (response.find(it) != std::string::npos)
return command_result::OK;
for (auto it : fail_phrase)
if (response.find(it) != std::string::npos)
return command_result::FAIL;
return command_result::TIMEOUT;
}, timeout_ms);
}
static inline command_result generic_command(CommandableIf* t, const std::string& command, static inline command_result generic_command(CommandableIf* t, const std::string& command,
const std::string& pass_phrase, const std::string& pass_phrase,
const std::string& fail_phrase, uint32_t timeout_ms) const std::string& fail_phrase, uint32_t timeout_ms)
{ {
std::cout << command << std::endl; const auto pass = std::list<std::string_view>({pass_phrase});
return t->command(command, [&](uint8_t *data, size_t len) { const auto fail = std::list<std::string_view>({fail_phrase});
std::string response((char*)data, len); return generic_command(t, command, pass, fail, timeout_ms);
std::cout << response << std::endl;
if (response.find(pass_phrase) != std::string::npos) {
return command_result::OK;
} else if (response.find(fail_phrase) != std::string::npos) {
return command_result::FAIL;
}
return command_result::TIMEOUT;
}, 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_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) {
return command_result::FAIL;
} }
return command_result::TIMEOUT; if (out.find("READY") != std::string::npos) {
}, 5000); pin_ok = true;
return command_result::OK;
}
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;
if (out.find(pattern) == std::string::npos ||
(ber_pos = out.find(',')) == std::string::npos)
return command_result::FAIL; return command_result::FAIL;
} else if (token.find("+CSQ") != std::string::npos) {
sscanf(token.c_str(), "%*s%d,%d", &rssi, &ber); if (std::from_chars(out.data() + rssi_pos, out.data() + ber_pos, rssi).ec == std::errc::invalid_argument)
} return command_result::FAIL;
response = response.substr(pos+1); if (std::from_chars(out.data() + ber_pos + 1, out.data() + out.size(), ber).ec == std::errc::invalid_argument)
} return command_result::FAIL;
return command_result::TIMEOUT; return command_result::OK;
}, 500);
} }
} // esp_modem::dce_commands } // esp_modem::dce_commands

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View 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

View File

@ -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;
}; };

View File

@ -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)

View 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;

View 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_

View File

@ -2,81 +2,43 @@
#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;
int milli_volt, bcl, bcs;
CHECK(dce->get_battery_status(milli_volt, bcl, bcs) == command_result::OK);
CHECK(milli_volt == 123456);
CHECK(bcl == 1);
CHECK(bcs == 2);
int rssi, ber;
CHECK(dce->get_signal_quality(rssi, ber) == command_result::OK);
CHECK(rssi == 123);
CHECK(ber == 456);
bool pin_ok;
CHECK(dce->read_pin(pin_ok) == command_result::OK);
CHECK(pin_ok == false);
CHECK(dce->set_pin("1234") == command_result::OK);
CHECK(dce->read_pin(pin_ok) == command_result::OK);
CHECK(pin_ok == true);
} }
void stop() override {
status = status_t::STOPPED;
}
int write(uint8_t *data, size_t len) override {
if (len > 2 && (data[len-1] == '\r' || data[len-1] == '+') ) { // Simple AT responder
std::string command((char*)data, len);
std::string response;
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 {
size_t read_len = std::min(data_len, len);
if (read_len) {
memcpy(data, &loopback_data[0], len);
loopback_data.erase(loopback_data.begin(), loopback_data.begin() + read_len);
data_len -= len;
}
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]")
{ {
@ -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,6 +128,7 @@ 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);

View File

@ -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"...

View File

@ -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;
} }