esp-modem: C++ rework

This commit is contained in:
David Cermak
2021-02-26 18:32:15 +01:00
parent 3d00b41d2d
commit 86b8cfd6f5
15 changed files with 1103 additions and 12 deletions

View File

@ -7,7 +7,8 @@ set(srcs "src/esp_modem.c"
"src/esp_modem_recov_helper.c"
"src/esp_sim800.c"
"src/esp_sim7600.c"
"src/esp_bg96.c")
"src/esp_bg96.c"
"src/esp_modem_dte.cpp")
set(include_dirs "include")
@ -20,3 +21,5 @@ idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS private_include
REQUIRES driver)
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)

View File

@ -277,7 +277,9 @@ void app_main(void)
// init the DTE
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.pattern_queue_size = 100;
dte_config.event_task_stack_size = 4096;
dte_config.event_task_priority = 15;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
dce_config.populate_command_list = true;
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();

View File

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(simple_cxx_client)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := pppos_client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,10 @@
# PPPoS simple client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows how to act as a MQTT client after the PPPoS channel created by using [ESP-MQTT](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/mqtt.html) APIs.
## How to use this example
See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "simple_client.cpp"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,143 @@
menu "Example Configuration"
choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
help
Select modem device connected to the ESP DTE.
config EXAMPLE_MODEM_DEVICE_SIM800
bool "SIM800"
help
SIMCom SIM800L is a GSM/GPRS module.
It supports Quad-band 850/900/1800/1900MHz.
config EXAMPLE_MODEM_DEVICE_BG96
bool "BG96"
help
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
config EXAMPLE_MODEM_DEVICE_SIM7600
bool "SIM7600"
help
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
endchoice
config EXAMPLE_MODEM_PPP_APN
string "Set MODEM APN"
default "internet"
help
Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_MODEM_LEGACY_API
bool "Use Modem legacy API"
default y
select MODEM_LEGACY_API
help
Set this to true to use backward compatible API to the original modem
component in example/protocol folder in IDFv4.2 and below
config EXAMPLE_MODEM_PPP_AUTH_USERNAME
string "Set username for authentication"
default "espressif"
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
help
Set username for PPP Authentication.
config EXAMPLE_MODEM_PPP_AUTH_PASSWORD
string "Set password for authentication"
default "esp32"
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
help
Set password for PPP Authentication.
config EXAMPLE_MODEM_PPP_AUTH_NONE
bool "Skip PPP authentication"
default n
help
Set to true for the PPP client to skip authentication
config EXAMPLE_SEND_MSG
bool "Short message (SMS)"
default n
help
Select this, the modem will send a short message before power off.
if EXAMPLE_SEND_MSG
config EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER
string "Peer Phone Number (with area code)"
default "+8610086"
help
Enter the peer phone number that you want to send message to.
endif
menu "UART Configuration"
config EXAMPLE_MODEM_UART_TX_PIN
int "TXD Pin Number"
default 25
range 0 31
help
Pin number of UART TX.
config EXAMPLE_MODEM_UART_RX_PIN
int "RXD Pin Number"
default 26
range 0 31
help
Pin number of UART RX.
config EXAMPLE_MODEM_UART_RTS_PIN
int "RTS Pin Number"
default 27
range 0 31
help
Pin number of UART RTS.
config EXAMPLE_MODEM_UART_CTS_PIN
int "CTS Pin Number"
default 23
range 0 31
help
Pin number of UART CTS.
config EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE
int "UART Event Task Stack Size"
range 2000 6000
default 2048
help
Stack size of UART event task.
config EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY
int "UART Event Task Priority"
range 3 22
default 5
help
Priority of UART event task.
config EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE
int "UART Event Queue Size"
range 10 40
default 30
help
Length of UART event queue.
config EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE
int "UART Pattern Queue Size"
range 10 40
default 20
help
Length of UART pattern queue.
config EXAMPLE_MODEM_UART_TX_BUFFER_SIZE
int "UART TX Buffer Size"
range 256 2048
default 512
help
Buffer size of UART TX buffer.
config EXAMPLE_MODEM_UART_RX_BUFFER_SIZE
int "UART RX Buffer Size"
range 256 2048
default 1024
help
Buffer size of UART RX buffer.
endmenu
endmenu

View File

@ -0,0 +1,347 @@
/* PPPoS Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "cxx_include/uart_terminal.hpp"
#define BROKER_URL "mqtt://mqtt.eclipse.org"
static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int STOP_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
#if CONFIG_EXAMPLE_SEND_MSG
/**
* @brief This example will also show how to send short message using the infrastructure provided by esp modem library.
* @note Not all modem support SMG.
*
*/
static esp_err_t example_default_handle(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
static esp_err_t example_handle_cmgs(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CMGS", strlen("+CMGS"))) {
err = ESP_OK;
}
return err;
}
#define MODEM_SMS_MAX_LENGTH (128)
#define MODEM_COMMAND_TIMEOUT_SMS_MS (120000)
#define MODEM_PROMPT_TIMEOUT_MS (10)
static esp_err_t example_send_message_text(modem_dce_t *user_dce, const char *phone_num, const char *text)
{
esp_modem_dce_t *dce = &user_dce->parent;
modem_dte_t *dte = dce->dte;
dce->handle_line = example_default_handle;
/* Set text mode */
if (dte->send_cmd(dte, "AT+CMGF=1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "set message format failed");
goto err;
}
ESP_LOGD(TAG, "set message format ok");
/* Specify character set */
dce->handle_line = example_default_handle;
if (dte->send_cmd(dte, "AT+CSCS=\"GSM\"\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "set character set failed");
goto err;
}
ESP_LOGD(TAG, "set character set ok");
/* send message */
char command[MODEM_SMS_MAX_LENGTH] = {0};
int length = snprintf(command, MODEM_SMS_MAX_LENGTH, "AT+CMGS=\"%s\"\r", phone_num);
/* set phone number and wait for "> " */
dte->send_wait(dte, command, length, "\r\n> ", MODEM_PROMPT_TIMEOUT_MS);
/* end with CTRL+Z */
snprintf(command, MODEM_SMS_MAX_LENGTH, "%s\x1A", text);
dce->handle_line = example_handle_cmgs;
if (dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_SMS_MS) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "send message failed");
goto err;
}
ESP_LOGD(TAG, "send message ok");
return ESP_OK;
err:
return ESP_FAIL;
}
#endif
static void modem_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
switch (event_id) {
case ESP_MODEM_EVENT_PPP_START:
ESP_LOGI(TAG, "Modem PPP Started");
break;
case ESP_MODEM_EVENT_PPP_STOP:
ESP_LOGI(TAG, "Modem PPP Stopped");
xEventGroupSetBits(event_group, STOP_BIT);
break;
case ESP_MODEM_EVENT_UNKNOWN:
ESP_LOGW(TAG, "Unknown line received: %s", (char *)event_data);
break;
default:
break;
}
}
//static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, "/topic/esp-pppos", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "esp32-pppos", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
xEventGroupSetBits(event_group, GOT_DATA_BIT);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
break;
}
}
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = (esp_netif_t *)event_data;
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
}
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config);
extern "C" void app_main(void)
{
/* Init and register system/core components */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
event_group = xEventGroupCreate();
/* Configure the DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
/* setup UART specific configuration based on kconfig options */
dte_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
dte_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
dte_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
dte_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
dte_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
dte_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
dte_config.pattern_queue_size = CONFIG_EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE;
dte_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
dte_config.event_task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE;
dte_config.event_task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
dte_config.line_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
/* Configure the DCE */
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
/* Configure the PPP netif */
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
/* Run the modem demo app */
return modem_test_app(&dte_config, &dce_config,&netif_ppp_config);
}
#include <iostream>
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config)
{
/* create dte object */
esp_modem_dte_t *dte; // = esp_modem_dte_new(dte_config);
// assert(dte != NULL);
// dte_config->line_buffer_size = 1000000;
uint8_t data[32] = {};
int actual_len = 0;
auto ddd = create_dte(dte_config);
ddd->set_mode(dte_mode::UNDEF);
ddd->send_command("AT\r", [&](uint8_t *data, size_t len) {
std::string response((char*)data, len);
ESP_LOGI("in the lambda", "len=%d data %s", len, (char*)data);
std::cout << response << std::endl;
return true;
}, 1000);
// auto uart = create_uart_terminal(dte_config);
// uart->set_data_cb([&](size_t len){
// actual_len = uart->read(data, 32);
// ESP_LOGI("in the lambda", "len=%d data %s", len, (char*)data);
// });
// uart->write((uint8_t*)"AT\r",3);
vTaskDelay(pdMS_TO_TICKS(1000));
// int len = uart->read(data, 32);
// ESP_LOGI(TAG, "len=%d data %s", len, (char*)data);
// vTaskDelay(pdMS_TO_TICKS(1000));
// len = uart->read(data, 32);
ESP_LOGI(TAG, "len=%d data %s", actual_len, (char*)data);
return;
/* create dce object */
#if CONFIG_EXAMPLE_MODEM_DEVICE_SIM800
dce_config->device = ESP_MODEM_DEVICE_SIM800;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96
dce_config->device = ESP_MODEM_DEVICE_BG96;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600
dce_config->device = ESP_MODEM_DEVICE_SIM7600;
#else
#error "Unsupported DCE"
#endif
esp_modem_dce_t *dce = esp_modem_dce_new(dce_config);
assert(dce != NULL);
/* create netif object */
esp_netif_t *esp_netif = esp_netif_new(ppp_config);
assert(esp_netif);
#if !defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_NONE) && (defined(CONFIG_LWIP_PPP_PAP_SUPPORT) || defined(CONFIG_LWIP_PPP_CHAP_SUPPORT))
#if CONFIG_LWIP_PPP_PAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#else
#error "Unsupported AUTH Negotiation"
#endif
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
/* attach the DCE, DTE, netif to initialize the modem */
ESP_ERROR_CHECK(esp_modem_default_attach(dte, dce, esp_netif));
while (1) {
ESP_ERROR_CHECK(esp_modem_default_start(dte));
/* Start PPP mode */
ESP_ERROR_CHECK(esp_modem_start_ppp(dte));
/* Wait for IP address */
xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
/* Config MQTT */
esp_mqtt_client_config_t mqtt_config = { };
mqtt_config.uri = BROKER_URL;
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_register_event(mqtt_client, MQTT_EVENT_ANY, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
esp_mqtt_client_destroy(mqtt_client);
/* Exit PPP mode */
ESP_ERROR_CHECK(esp_modem_stop_ppp(dte));
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "Restart after 60 seconds");
vTaskDelay(pdMS_TO_TICKS(60000));
}
/* Default destroy all modem sub-units attached to it (DTE, DCE, netif) */
ESP_ERROR_CHECK(esp_modem_default_destroy(dte));
}

View File

@ -0,0 +1,8 @@
# Override some defaults to enable PPP
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y
CONFIG_LWIP_PPP_PAP_SUPPORT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
# Do not enable IPV6 in dte<->dce link local
CONFIG_LWIP_PPP_ENABLE_IPV6=n
CONFIG_COMPILER_CXX_EXCEPTIONS=y

View File

@ -0,0 +1,86 @@
#ifndef SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_HPP
#define SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_HPP
#include <memory>
#include <functional>
#include <exception>
#include <cstddef>
#include <cstdint>
#include <utility>
#include "esp_err.h"
#include "terminal_objects.hpp"
enum class terminal_error {
BUFFER_OVERFLOW,
CHECKSUM_ERROR,
UNEXPECTED_CONTROL_FLOW,
};
class terminal {
public:
virtual ~terminal() = default;
void set_data_cb(std::function<void(size_t len)> f) { on_data = std::move(f); }
void set_error_cb(std::function<void(terminal_error)> f) { on_error = std::move(f); }
virtual int write(uint8_t *data, size_t len) = 0;
virtual int read(uint8_t *data, size_t len) = 0;
virtual void start() = 0;
virtual void stop() = 0;
protected:
std::function<void(size_t len)> on_data;
std::function<void(terminal_error)> on_error;
};
class dte_adapter: public terminal {
public:
dte_adapter(std::unique_ptr<terminal> terminal):
original_dte(std::move(terminal)) {}
~dte_adapter() override = default;
int write(uint8_t *data, size_t len) override { return original_dte->write(data, len); }
int read(uint8_t *data, size_t len) override { return original_dte->read(data, len); }
private:
std::unique_ptr<terminal> original_dte;
};
enum class dte_mode {
UNDEF,
COMMAND_MODE,
DATA_MODE
};
const int DTE_BUFFER_SIZE = 1024;
struct dte_data {
uint8_t *data;
size_t len;
};
typedef std::function<bool(uint8_t *data, size_t len)> got_line_cb;
class dte {
public:
explicit dte(std::unique_ptr<terminal> terminal);
~dte() = default;
// void set_line_cb(got_line f) { on_line_cb = std::move(f); }
void set_mode(dte_mode m) { term->start(); mode = m; }
bool send_command(const std::string& command, got_line_cb got_line, uint32_t time_ms);
private:
const size_t GOT_LINE = BIT0;
size_t buffer_size;
size_t consumed;
std::unique_ptr<uint8_t[]> buffer;
std::unique_ptr<terminal> term;
got_line_cb on_line;
dte_mode mode;
signal_group signal;
};
#endif //SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_HPP

View File

@ -0,0 +1,84 @@
//
// Created by david on 2/26/21.
//
#ifndef SIMPLE_CXX_CLIENT_TERMINAL_OBJECTS_HPP
#define SIMPLE_CXX_CLIENT_TERMINAL_OBJECTS_HPP
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
class esp_err_exception: virtual public std::exception {
public:
explicit esp_err_exception(esp_err_t err): esp_err(err) {}
explicit esp_err_exception(std::string msg): esp_err(ESP_FAIL), message(std::move(msg)) {}
explicit esp_err_exception(std::string msg, esp_err_t err): esp_err(err), message(std::move(msg)) {}
virtual esp_err_t get_err_t() { return esp_err; }
~esp_err_exception() noexcept override = default;
virtual const char* what() const noexcept {
return message.c_str();
}
private:
esp_err_t esp_err;
std::string message;
};
static inline void throw_if_false(bool condition, std::string message)
{
if (!condition) {
throw(esp_err_exception(std::move(message)));
}
}
static inline void throw_if_esp_fail(esp_err_t err, std::string message)
{
if (err != ESP_OK) {
throw(esp_err_exception(std::move(message), err));
}
}
static inline void throw_if_esp_fail(esp_err_t err)
{
if (err != ESP_OK) {
throw(esp_err_exception(err));
}
}
struct signal_group {
explicit signal_group(): event_group(nullptr)
{
event_group = xEventGroupCreate();
throw_if_false(event_group != nullptr, "create signal event group failed");
}
void set(uint32_t bits)
{
xEventGroupSetBits(event_group, bits);
}
bool wait(uint32_t flags, uint32_t time_ms) // waiting for all and clearing if set
{
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdTRUE, pdTRUE, pdMS_TO_TICKS(time_ms));
return bits & flags;
}
bool is_any(uint32_t flags)
{
return xEventGroupGetBits(event_group) & flags;
}
bool wait_any(uint32_t flags, uint32_t time_ms) // waiting for any bit, not clearing them
{
EventBits_t bits = xEventGroupWaitBits(event_group, flags, pdFALSE, pdFALSE, pdMS_TO_TICKS(time_ms));
return bits & flags;
}
~signal_group()
{
if (event_group) vEventGroupDelete(event_group);
}
EventGroupHandle_t event_group;
};
#endif //SIMPLE_CXX_CLIENT_TERMINAL_OBJECTS_HPP

View File

@ -0,0 +1,17 @@
//
// Created by david on 2/25/21.
//
#ifndef SIMPLE_CXX_CLIENT_UART_TERMINAL_HPP
#define SIMPLE_CXX_CLIENT_UART_TERMINAL_HPP
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_dte_config.h"
std::unique_ptr<terminal> create_uart_terminal(const struct dte_config *config);
class dte;
std::unique_ptr<dte> create_dte(const struct dte_config *config);
#endif //SIMPLE_CXX_CLIENT_UART_TERMINAL_HPP

View File

@ -19,6 +19,7 @@ extern "C" {
#include "esp_event.h"
#include "driver/uart.h"
#include "esp_modem_dte_config.h"
/**
* @brief Forward declare DTE and DCE objects
@ -52,21 +53,15 @@ typedef enum {
* @{
*/
/**
* @brief Modem flow control type
*
*/
typedef enum {
ESP_MODEM_FLOW_CONTROL_NONE = 0,
ESP_MODEM_FLOW_CONTROL_SW,
ESP_MODEM_FLOW_CONTROL_HW
} esp_modem_flow_ctrl_t;
/**
* @brief ESP Modem DTE Configuration
*
*/
typedef struct {
typedef struct dte_config
#if 0
{
uart_port_t port_num; /*!< UART port number */
uart_word_length_t data_bits; /*!< Data bits of UART */
uart_stop_bits_t stop_bits; /*!< Stop bits of UART */
@ -84,7 +79,9 @@ typedef struct {
uint32_t event_task_stack_size; /*!< UART Event Task Stack size */
int event_task_priority; /*!< UART Event Task Priority */
int line_buffer_size; /*!< Line buffer size for command mode */
} esp_modem_dte_config_t;
}
#endif
esp_modem_dte_config_t;
/**
* @brief ESP Modem DTE Default Configuration

View File

@ -0,0 +1,39 @@
//
// Created by david on 2/24/21.
//
#ifndef SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_CONFIG_H
#define SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_CONFIG_H
/**
* @brief Modem flow control type
*
*/
typedef enum {
ESP_MODEM_FLOW_CONTROL_NONE = 0,
ESP_MODEM_FLOW_CONTROL_SW,
ESP_MODEM_FLOW_CONTROL_HW
} esp_modem_flow_ctrl_t;
struct dte_config {
uart_port_t port_num; /*!< UART port number */
uart_word_length_t data_bits; /*!< Data bits of UART */
uart_stop_bits_t stop_bits; /*!< Stop bits of UART */
uart_parity_t parity; /*!< Parity type */
esp_modem_flow_ctrl_t flow_control; /*!< Flow control type */
int baud_rate; /*!< Communication baud rate */
int tx_io_num; /*!< TXD Pin Number */
int rx_io_num; /*!< RXD Pin Number */
int rts_io_num; /*!< RTS Pin Number */
int cts_io_num; /*!< CTS Pin Number */
int rx_buffer_size; /*!< UART RX Buffer Size */
int tx_buffer_size; /*!< UART TX Buffer Size */
int pattern_queue_size; /*!< UART Pattern Queue Size */
int event_queue_size; /*!< UART Event Queue Size */
uint32_t event_task_stack_size; /*!< UART Event Task Stack size */
int event_task_priority; /*!< UART Event Task Priority */
int line_buffer_size; /*!< Line buffer size for command mode */
};
#endif //SIMPLE_CXX_CLIENT_ESP_MODEM_DTE_CONFIG_H

View File

@ -0,0 +1,334 @@
//
// Created by david on 2/24/21.
//
#include "cxx_include/esp_modem_dte.hpp"
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_modem.h"
#include "esp_modem_dce.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_modem_internal.h"
#include "esp_modem_dte_internal.h"
#include "esp_modem_dte_config.h"
#include <memory>
#include <utility>
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
#define MIN_PATTERN_INTERVAL (9)
#define MIN_POST_IDLE (0)
#define MIN_PRE_IDLE (0)
static const char *TAG = "dte_uart";
//class uart_terminal;
//class dte {
//public:
// esp_err_t init(std::unique_ptr<terminal> t);
//
//private:
// std::unique_ptr<terminal> m_terminal;
//
//};
struct uart_task {
explicit uart_task(size_t stack_size, size_t priority, void* task_param, TaskFunction_t task_function):
task_handle(nullptr)
{
BaseType_t ret = xTaskCreate(task_function, "uart_task", stack_size, task_param, priority, &task_handle);
throw_if_false(ret == pdTRUE, "create uart event task failed");
}
~uart_task()
{
if (task_handle) vTaskDelete(task_handle);
}
TaskHandle_t task_handle; /*!< UART event task handle */
};
struct uart_event_loop {
explicit uart_event_loop(): event_loop_hdl(nullptr)
{
esp_event_loop_args_t loop_args = {};
loop_args.queue_size = ESP_MODEM_EVENT_QUEUE_SIZE;
loop_args.task_name = nullptr;
throw_if_esp_fail(esp_event_loop_create(&loop_args, &event_loop_hdl), "create event loop failed");
}
void run() { esp_event_loop_run(event_loop_hdl, pdMS_TO_TICKS(0)); }
~uart_event_loop() { if (event_loop_hdl) esp_event_loop_delete(event_loop_hdl); }
esp_event_loop_handle_t event_loop_hdl;
};
struct uart_resource {
explicit uart_resource(const struct dte_config *config);
~uart_resource();
bool get_event(uart_event_t& event, uint32_t time_ms)
{
return xQueueReceive(event_queue, &event, pdMS_TO_TICKS(time_ms));
}
void reset_events()
{
uart_flush_input(port);
xQueueReset(event_queue);
}
uart_port_t port; /*!< UART port */
QueueHandle_t event_queue; /*!< UART event queue handle */
// esp_modem_on_receive receive_cb; /*!< ptr to data reception */
// void *receive_cb_ctx; /*!< ptr to rx fn context data */
int line_buffer_size; /*!< line buffer size in command mode */
int pattern_queue_size; /*!< UART pattern queue size */
};
class uart_terminal: public terminal {
public:
explicit uart_terminal(const struct dte_config *config):
uart(config), event_loop(), signal(),
task_handle(config->event_task_stack_size, config->event_task_priority, this, s_task) {}
~uart_terminal() override = default;
void start() override
{
signal.set(TASK_START);
}
void stop() override
{
signal.set(TASK_STOP);
}
// { ESP_LOGE(TAG, "uart_terminal destruct"); }
int write(uint8_t *data, size_t len) override;
int read(uint8_t *data, size_t len) override;
private:
static void s_task(void * task_param)
{
auto t = static_cast<uart_terminal*>(task_param);
t->task();
vTaskDelete(NULL);
}
void task();
const size_t TASK_INIT = BIT0;
const size_t TASK_START = BIT1;
const size_t TASK_STOP = BIT2;
uart_resource uart;
uart_event_loop event_loop;
signal_group signal;
uart_task task_handle;
};
uart_resource::~uart_resource()
{
if (port >= UART_NUM_0 && port < UART_NUM_MAX) {
uart_driver_delete(port);
}
}
uart_resource::uart_resource(const struct dte_config *config):
port(-1)
// buffer(std::make_unique<uint8_t[]>(config->line_buffer_size))
{
esp_err_t res;
line_buffer_size = config->line_buffer_size;
/* TODO: Bind methods */
/* Config UART */
uart_config_t uart_config = {};
uart_config.baud_rate = config->baud_rate;
uart_config.data_bits = config->data_bits;
uart_config.parity = config->parity;
uart_config.stop_bits = config->stop_bits;
uart_config.flow_ctrl = (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS
: UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_REF_TICK;
throw_if_esp_fail(uart_param_config(config->port_num, &uart_config), "config uart parameter failed");
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
res = uart_set_pin(config->port_num, config->tx_io_num, config->rx_io_num,
config->rts_io_num, config->cts_io_num);
} else {
res = uart_set_pin(config->port_num, config->tx_io_num, config->rx_io_num,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
throw_if_esp_fail(res, "config uart gpio failed");
/* Set flow control threshold */
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
res = uart_set_hw_flow_ctrl(config->port_num, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
} else if (config->flow_control == ESP_MODEM_FLOW_CONTROL_SW) {
res = uart_set_sw_flow_ctrl(config->port_num, true, 8, UART_FIFO_LEN - 8);
}
throw_if_esp_fail(res, "config uart flow control failed");
/* Install UART driver and get event queue used inside driver */
res = uart_driver_install(config->port_num, config->rx_buffer_size, config->tx_buffer_size,
config->event_queue_size, &(event_queue), 0);
throw_if_esp_fail(res, "install uart driver failed");
throw_if_esp_fail(uart_set_rx_timeout(config->port_num, 1), "set rx timeout failed");
/* Set pattern interrupt, used to detect the end of a line. */
// res = uart_enable_pattern_det_baud_intr(config->port_num, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
/* Set pattern queue size */
// pattern_queue_size = config->pattern_queue_size;
// res |= uart_pattern_queue_reset(config->port_num, config->pattern_queue_size);
/* Starting in command mode -> explicitly disable RX interrupt */
// uart_disable_rx_intr(config->port_num);
uart_set_rx_full_threshold(config->port_num, 64);
throw_if_esp_fail(res, "config uart pattern failed");
/* mark UART as initialized */
port = config->port_num;
}
std::unique_ptr<terminal> create_uart_terminal(const struct dte_config *config)
{
try {
auto term = std::make_unique<uart_terminal>(config);
term->start();
return term;
} catch (std::bad_alloc& e) {
ESP_LOGE(TAG, "Out of memory");
return nullptr;
} catch (esp_err_exception& e) {
esp_err_t err = e.get_err_t();
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err);
ESP_LOGE(TAG, "%s", e.what());
return nullptr;
}
}
std::unique_ptr<dte> create_dte(const struct dte_config *config)
{
try {
auto term = std::make_unique<dte>(std::make_unique<uart_terminal>(config));
// term->start();
return term;
} catch (std::bad_alloc& e) {
ESP_LOGE(TAG, "Out of memory");
return nullptr;
} catch (esp_err_exception& e) {
esp_err_t err = e.get_err_t();
ESP_LOGE(TAG, "Error occurred during UART term init: %d", err);
ESP_LOGE(TAG, "%s", e.what());
return nullptr;
}
}
void uart_terminal::task()
{
uart_event_t event;
size_t len;
signal.set(TASK_INIT);
signal.wait_any(TASK_START | TASK_STOP, portMAX_DELAY);
if (signal.is_any(TASK_STOP)) {
return; // deletes the static task
}
while(signal.is_any(TASK_START)) {
event_loop.run();
if (uart.get_event(event, 100)) {
switch (event.type) {
case UART_DATA:
ESP_LOGI(TAG, "UART_DATA");
uart_get_buffered_data_len(uart.port, &len);
ESP_LOGI(TAG, "UART_DATA len=%d, on_data=%d", len, (bool)on_data);
if (len && on_data) {
on_data(len);
}
break;
case UART_FIFO_OVF:
ESP_LOGW(TAG, "HW FIFO Overflow");
uart.reset_events();
break;
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "Ring Buffer Full");
uart.reset_events();
break;
case UART_BREAK:
ESP_LOGW(TAG, "Rx Break");
break;
case UART_PARITY_ERR:
ESP_LOGE(TAG, "Parity Error");
break;
case UART_FRAME_ERR:
ESP_LOGE(TAG, "Frame Error");
break;
case UART_PATTERN_DET:
ESP_LOGI(TAG, "UART_PATTERN_DET");
break;
default:
ESP_LOGW(TAG, "unknown uart event type: %d", event.type);
break;
}
}
// ESP_LOGI(TAG, "uart_event_task_entry");
// vTaskDelay(pdMS_TO_TICKS(200));
}
}
int uart_terminal::read(uint8_t *data, size_t len)
{
size_t length = 0;
uart_get_buffered_data_len(uart.port, &length);
if (length > 0) {
return uart_read_bytes(uart.port, data, length, portMAX_DELAY);
}
return 0;
}
int uart_terminal::write(uint8_t *data, size_t len)
{
return uart_write_bytes(uart.port, data, len);
}
dte::dte(std::unique_ptr<terminal> terminal):
buffer_size(DTE_BUFFER_SIZE), consumed(0),
buffer(std::make_unique<uint8_t[]>(buffer_size)),
term(std::move(terminal)), mode(dte_mode::UNDEF) {}
bool dte::send_command(const std::string& command, got_line_cb got_line, uint32_t time_ms)
{
term->write((uint8_t *)command.c_str(), command.length());
term->set_data_cb([&](size_t len){
auto data_to_read = std::min(len, buffer_size - consumed);
auto data = buffer.get() + consumed;
auto actual_len = term->read(data, data_to_read);
consumed += actual_len;
if (memchr(data, '\n', actual_len)) {
ESP_LOGI("in the lambda", "FOUND");
if (got_line(buffer.get(), consumed)) {
signal.set(GOT_LINE);
}
}
ESP_LOGI("in the lambda", "len=%d data %s", len, (char*)buffer.get());
});
auto res = signal.wait(GOT_LINE, time_ms);
consumed = 0;
return res;
}