Update CMUX example to use VFS term and OTA

This commit is contained in:
David Cermak
2021-05-17 18:24:35 +02:00
parent 59c3837345
commit df3971105e
22 changed files with 532 additions and 372 deletions

View File

@ -2,9 +2,9 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
set(EXTRA_COMPONENT_DIRS "../.." $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(simple_cxx_client)
project(simple_cmux_client)

View File

@ -0,0 +1,17 @@
# Simple example of esp_modem component
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates the use of the [esp-modem component](https://components.espressif.com/component/espressif/esp_modem) to connect to a network and send some AT commands.
It uses modem CMUX mode so that commands and network could be used at the same time.
The example uses the following configuration options to demonstrate basic esp-modem capabilities:
* `EXAMPLE_NEED_SIM_PIN`: To unlock the SIM card with a PIN code if needed
* `EXAMPLE_PERFORM_OTA`: To start simple OTA at the end of the example to exercise basic CMUX/modem networking. Please note that the option `CONFIG_UART_ISR_IN_IRAM` is not enabled automatically, so that buffer overflows are expected and CMUX/PPP and networking should recover.
* `EXAMPLE_USE_VFS_TERM`: To demonstrate using an abstract file descriptor to talk to the device (instead of the UART driver directly). This option could be used when implementing a custom VFS driver.
## About the esp_modem
Please check the component [README](../../README.md)

View File

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

View File

@ -0,0 +1,64 @@
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 a 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_NEED_SIM_PIN
bool "SIM PIN needed"
default n
help
Enable to set SIM PIN before starting the example
config EXAMPLE_SIM_PIN
string "Set SIM PIN"
default "1234"
depends on EXAMPLE_NEED_SIM_PIN
help
Pin to unlock the SIM
config EXAMPLE_USE_VFS_TERM
bool "Use VFS terminal"
default n
help
Demonstrate use of VFS as a communication terminal of the DTE.
VFS driver implements non-block reads, writes and selects to communicate with esp-modem,
but this implementation uses UART resource only.
config EXAMPLE_PERFORM_OTA
bool "Perform OTA in the example"
default n
help
Perform the OTA update after connecting to the network and mqtt broker.
This option is used only to exercise network stability in CMUX mode.
config EXAMPLE_PERFORM_OTA_URI
string "URI of the binary"
default "https://my.code/esp32.bin"
depends on EXAMPLE_PERFORM_OTA
help
HTTPS address of the update binary.
endmenu

View File

@ -0,0 +1,167 @@
/* 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 <cstring>
#include <iostream>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include "esp_event_cxx.hpp"
#include "simple_mqtt_client.hpp"
#include "esp_vfs_dev.h" // For optional VFS support
#include "esp_https_ota.h" // For potential OTA configuration
#define BROKER_URL "mqtt://mqtt.eclipseprojects.io"
using namespace esp_modem;
using namespace idf::event;
static const char *TAG = "cmux_example";
extern "C" void app_main(void)
{
/* Init and register system/core components */
auto loop = std::make_shared<ESPEventLoop>();
ESP_ERROR_CHECK(esp_netif_init());
/* Configure and create the DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
#if CONFIG_EXAMPLE_USE_VFS_TERM == 1
/* The VFS terminal is just a demonstration of using an abstract file descriptor
* which implements non-block reads, writes and selects to communicate with esp-modem.
* This configuration uses the same UART driver as the terminal created by `create_uart_dte()`,
* so doesn't give any practical benefit besides the FD use demonstration and a placeholder
* to use FD terminal for other devices
*/
dte_config.vfs_config.dev_name = "/dev/uart/1";
dte_config.vfs_config.resource = ESP_MODEM_VFS_IS_UART;
dte_config.uart_config.event_queue_size = 0;
auto dte = create_vfs_dte(&dte_config);
esp_vfs_dev_uart_use_driver(dte_config.uart_config.port_num);
#else
auto dte = create_uart_dte(&dte_config);
#endif // CONFIG_EXAMPLE_USE_VFS_TERM
assert(dte);
/* 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();
/* Create the PPP and DCE objects */
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
assert(esp_netif);
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
std::unique_ptr<DCE> dce = create_BG96_dce(&dce_config, dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
std::unique_ptr<DCE> dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
std::unique_ptr<DCE> dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif);
#else
#error "Unsupported device"
#endif
assert(dce);
/* Setup basic operation mode for the DCE (pin if used, CMUX mode) */
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
bool pin_ok = true;
if (dce->read_pin(pin_ok) == command_result::OK && !pin_ok) {
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
vTaskDelay(pdMS_TO_TICKS(1000)); // Need to wait for some time after unlocking the SIM
}
#endif
if (dce->set_mode(esp_modem::modem_mode::CMUX_MODE) && dce->set_mode(esp_modem::modem_mode::DATA_MODE)) {
std::cout << "Modem has correctly entered multiplexed command/data mode" << std::endl;
} else {
ESP_LOGE(TAG, "Failed to configure desired mode... exiting");
return;
}
/* Read some data from the modem */
std::string str;
while (dce->get_operator_name(str) != esp_modem::command_result::OK) {
// Getting operator name could fail... retry after 500 ms
vTaskDelay(pdMS_TO_TICKS(500));
}
std::cout << "Operator name:" << str << std::endl;
/* Try to connect to the network and publish an mqtt topic */
ESPEventHandlerSync event_handler(loop);
event_handler.listen_to(ESPEvent(IP_EVENT, ESPEventID(ESP_EVENT_ANY_ID)));
auto result = event_handler.wait_event_for(std::chrono::milliseconds(60000));
if (result.timeout) {
ESP_LOGE(TAG, "Cannot get IP within specified timeout... exiting");
return;
} else if (result.event.id == ESPEventID(IP_EVENT_PPP_GOT_IP)) {
auto *event = (ip_event_got_ip_t *)result.ev_data;
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));
std::cout << "Got IP address" << std::endl;
/* When connected to network, subscribe and publish some MQTT data */
MqttClient mqtt(BROKER_URL);
event_handler.listen_to(MqttClient::get_event(MqttClient::Event::CONNECT));
event_handler.listen_to(MqttClient::get_event(MqttClient::Event::DATA));
auto reg = loop->register_event(MqttClient::get_event(MqttClient::Event::DATA),
[&mqtt](const ESPEvent &event, void *data) {
std::cout << " TOPIC:" << mqtt.get_topic(data) << std::endl;
std::cout << " DATA:" << mqtt.get_data(data) << std::endl;
});
mqtt.connect();
while (true) {
result = event_handler.wait_event_for(std::chrono::milliseconds(60000));
if (result.event == MqttClient::get_event(MqttClient::Event::CONNECT)) {
mqtt.subscribe("/topic/esp-modem");
mqtt.publish("/topic/esp-modem", "Hello modem");
continue;
} else if (result.event == MqttClient::get_event(MqttClient::Event::DATA)) {
std::cout << "Data received" << std::endl;
break; /* Continue with CMUX example after getting data from MQTT */
} else {
break;
}
}
} else if (result.event.id == ESPEventID(IP_EVENT_PPP_LOST_IP)) {
ESP_LOGE(TAG, "PPP client has lost connection... exiting");
return;
}
/* Again reading some data from the modem */
if (dce->get_imsi(str) == esp_modem::command_result::OK) {
std::cout << "Modem IMSI number:" << str << std::endl;
}
#if CONFIG_EXAMPLE_PERFORM_OTA == 1
esp_http_client_config_t config = { };
config.skip_cert_common_name_check = true;
config.url = CONFIG_EXAMPLE_PERFORM_OTA_URI;
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK) {
esp_restart();
} else {
ESP_LOGE(TAG, "Firmware upgrade failed");
return;
}
#endif // CONFIG_EXAMPLE_PERFORM_OTA
}

View File

@ -0,0 +1,101 @@
/* 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 <memory>
#include "mqtt_client.h"
#include "esp_event_cxx.hpp"
#include "simple_mqtt_client.hpp"
using namespace idf::event;
/**
* Reference to the MQTT event base
*/
ESP_EVENT_DECLARE_BASE(MQTT_EVENTS);
/**
* Thin wrapper around C mqtt_client
*/
struct MqttClientHandle
{
explicit MqttClientHandle(const std::string & uri)
{
esp_mqtt_client_config_t config = { };
config.uri = uri.c_str();
client = esp_mqtt_client_init(&config);
esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, this);
}
~MqttClientHandle()
{
esp_mqtt_client_destroy(client);
}
static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t id, void *data)
{
// forwards the internal event to the global ESPEvent
esp_event_post(base, id, data, sizeof(esp_mqtt_event_t), portMAX_DELAY);
}
esp_mqtt_client_handle_t client;
};
/**
* @brief Definitions of MqttClient methods
*/
MqttClient::MqttClient(const std::string & uri):
h(std::unique_ptr<MqttClientHandle>(new MqttClientHandle(uri)))
{}
void MqttClient::connect()
{
esp_mqtt_client_start(h->client);
}
idf::event::ESPEvent MqttClient::get_event(MqttClient::Event ev)
{
switch (ev) {
case Event::CONNECT: {
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_CONNECTED) };
}
case Event::DATA:
return { MQTT_EVENTS, ESPEventID(MQTT_EVENT_DATA) };
}
return { };
}
int MqttClient::publish(const std::string &topic, const std::string &data, int qos)
{
return esp_mqtt_client_publish(h->client, topic.c_str(), data.c_str(), 0, qos, 0);
}
int MqttClient::subscribe(const std::string &topic, int qos)
{
return esp_mqtt_client_subscribe(h->client, topic.c_str(), qos);
}
std::string MqttClient::get_topic(void * event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)
return {};
return std::string(event->topic, event->topic_len);
}
std::string MqttClient::get_data(void * event_data)
{
auto event = (esp_mqtt_event_handle_t)event_data;
if (event == nullptr || event->client != h->client)
return {};
return std::string(event->data, event->data_len);
}
MqttClient::~MqttClient() = default;

View File

@ -0,0 +1,77 @@
/* 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.
*/
#ifndef _SIMPLE_MQTT_CLIENT_H_
#define _SIMPLE_MQTT_CLIENT_H_
#include <string>
#include <memory>
#include "esp_event_cxx.hpp"
struct MqttClientHandle;
/**
* @brief Simple MQTT client wrapper
*/
class MqttClient {
public:
enum class Event {
CONNECT,
DATA,
};
explicit MqttClient(const std::string & uri);
~MqttClient();
/**
* @brief Start the mqtt-client
*/
void connect();
/**
* @brief Publish to topic
* @param topic Topic to publish
* @param data Data to publish
* @param qos QoS (0 by default)
* @return message id
*/
int publish(const std::string & topic, const std::string & data, int qos = 0);
/**
* @brief Subscribe to a topic
* @param topic Topic to subscribe
* @param qos QoS (0 by default)
* @return message id
*/
int subscribe(const std::string & topic, int qos = 0);
/**
* @brief Get topic from event data
* @return String topic
*/
std::string get_topic(void *);
/**
* @brief Get published data from event
* @return String representation of the data
*/
std::string get_data(void *);
/**
* @brief Convert internal MQTT event to standard ESPEvent
* @param ev internal mqtt event
* @return corresponding ESPEvent
*/
static idf::event::ESPEvent get_event(Event ev);
private:
std::unique_ptr<MqttClientHandle> h;
};
#endif //_SIMPLE_MQTT_CLIENT_H_

View File

@ -3,6 +3,8 @@ 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
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_PARTITION_TABLE_TWO_OTA=y
CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF=y
CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=y

View File

@ -1,9 +0,0 @@
#
# 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

@ -1,10 +0,0 @@
# 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

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

View File

@ -1,35 +0,0 @@
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_SIM_PIN
string "Set SIM PIN"
default "1234"
help
Pin to unlock the SIM
endmenu

View File

@ -1,237 +0,0 @@
/* 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_log.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "cxx_include/esp_modem_api.hpp"
#include <iostream>
#include "esp_https_ota.h"
#include "esp_vfs_dev.h"
#define BROKER_URL "mqtt://mqtt.eclipseprojects.io"
using namespace esp_modem;
static const char *TAG = "cmux_example";
static EventGroupHandle_t event_group = nullptr;
static const int CONNECT_BIT = BIT0;
static const int STOP_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
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));
}
}
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();
// dte_config.task_stack_size = 8192;
dte_config.vfs_config.dev_name = "/dev/uart/1";
dte_config.uart_config.event_queue_size = 0;
// 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 = 512,
// .baud_rate = 115200,
// .tx_io_num = 25,
// .rx_io_num = 26,
// .task_stack_size = 4096,
// .task_prio = 5}
// };
/* 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();
// dte_config.dte_buffer_size = 512;
// dte_config.uart_config.port_num = UART_NUM_2;
// auto uart_dte = create_uart_dte(&dte_config);
auto uart_dte = create_vfs_dte(&dte_config);
esp_vfs_dev_uart_use_driver(dte_config.uart_config.port_num);
esp_netif_t *esp_netif = esp_netif_new(&netif_ppp_config);
assert(esp_netif);
#if CONFIG_EXAMPLE_MODEM_DEVICE_BG96 == 1
auto dce = create_BG96_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1
auto dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1
auto dce = create_SIM7600_dce(&dce_config, uart_dte, esp_netif);
#else
#error "Unsupported device"
#endif
dce->set_command_mode();
std::string str;
dce->get_module_name(str);
std::cout << "Module name:" << str << std::endl;
bool pin_ok = true;
if (dce->read_pin(pin_ok) == command_result::OK && !pin_ok) {
throw_if_false(dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK, "Cannot set PIN!");
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->get_imsi(str);
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_mode(esp_modem::modem_mode::CMUX_MODE);
dce->set_data();
// MQTT connection
xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
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, nullptr);
esp_mqtt_client_start(mqtt_client);
EventBits_t got_data = 0;
while (got_data == 0) { // reading IMSI number until we get published data from MQTT
// dce->get_imsi(str);
// std::cout << "Modem IMSI number:" << str << "|" << std::endl;
got_data = xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(500));
}
esp_mqtt_client_destroy(mqtt_client);
// dce->get_imsi(str);
// std::cout << "Modem IMSI number:" << str << "|" << std::endl;
esp_http_client_config_t config = { };
config.skip_cert_common_name_check = true;
config.url = "https://github.com/david-cermak/esp32-ipv6-examples/raw/test/ota/hello-world.bin";
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK) {
esp_restart();
} else {
ESP_LOGE(TAG, "Firmware upgrade failed");
}
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "Example finished");
}

View File

@ -37,7 +37,7 @@ typedef enum {
} esp_modem_flow_ctrl_t;
/**
* @brief DTE configuration structure
* @brief UART configuration structure
*
*/
struct esp_modem_uart_term_config {
@ -66,11 +66,22 @@ typedef enum {
} esp_modem_vfs_resource_t;
/**
* @brief VFS configuration structure
*
*/
struct esp_modem_vfs_term_config {
const char* dev_name; /*!< VFS device name, e.g. /dev/uart/n */
esp_modem_vfs_resource_t resource; /*!< Underlying device which gets initialized during VFS init */
};
/**
* @brief Complete DTE configuration structure
*
* Note that the generic part is common for DTE and its SW resources
* The following portions for config is dedicated to the chosen HW resource
* used as a communication terminal for this DTE
*/
struct esp_modem_dte_config {
size_t dte_buffer_size; /*!< DTE buffer size */
uint32_t task_stack_size; /*!< Terminal task stack size */
@ -106,6 +117,7 @@ struct esp_modem_dte_config {
}, \
.vfs_config = { \
.dev_name = "/null", \
.resource = ESP_MODEM_VFS_IS_EXTERN \
}\
}

View File

@ -0,0 +1,38 @@
// 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.
#ifndef _UART_RESOURCE_HPP_
#define _UART_RESOURCE_HPP_
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
struct esp_modem_dte_config;
namespace esp_modem {
/**
* @brief Uart Resource is a platform specific struct which is implemented separately for ESP_PLATFORM and linux target
*/
struct uart_resource {
explicit uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue, int fd);
~uart_resource();
uart_port_t port{};
};
std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config);
} // namespace esp_modem
#endif // _UART_RESOURCE_HPP_

View File

@ -235,12 +235,6 @@ bool CMux::on_cmux(uint8_t *data, size_t actual_len)
ESP_LOGW("CMUX", "Protocol mismatch: Missed trailing SOF, recovering...");
payload_start = nullptr;
total_payload_size = 0;
// ESP_LOGE("CMUX-Footer", "Protocol mismatch! total pyaload: %d", total_payload_size);
// ESP_LOG_BUFFER_HEXDUMP("Data-valid", payload_start, total_payload_size, ESP_LOG_INFO);
// ESP_LOG_BUFFER_HEXDUMP("Footer", frame-8, 16, ESP_LOG_ERROR);
// while(1) { usleep(10000); };
// abort();
state = cmux_state::RECOVER;
break;
}

View File

@ -28,12 +28,12 @@ command_result generic_command(CommandableIf* t, const std::string &command,
const std::list<std::string_view>& fail_phrase,
uint32_t timeout_ms)
{
ESP_LOGI(TAG, "%s command %s\n", __func__, command.c_str());
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
return t->command(command, [&](uint8_t *data, size_t len) {
std::string_view response((char*)data, len);
if (data == nullptr || len == 0 || response.empty())
return command_result::TIMEOUT;
ESP_LOGI(TAG, "Response: %.*s\n", (int)response.length(), response.data());
ESP_LOGD(TAG, "Response: %.*s\n", (int)response.length(), response.data());
for (auto &it : pass_phrase)
if (response.find(it) != std::string::npos)
return command_result::OK;

View File

@ -27,11 +27,32 @@ namespace esp_modem {
class Resource {
public:
Resource(const esp_modem_dte_config *config);
explicit Resource(const esp_modem_dte_config *config, int fd):
uart(config->vfs_config.resource == ESP_MODEM_VFS_IS_EXTERN? std::nullopt : std::make_optional<uart_resource>(config, nullptr, fd))
{}
std::optional<uart_resource> uart;
};
struct File {
explicit File(const char *name): fd(-1)
{
fd = open(name, O_RDWR);
throw_if_false(fd >= 0, "Cannot open the fd");
// Set the FD to non-blocking mode
int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}
~File() {
if (fd >= 0) {
close(fd);
}
}
int fd;
};
class FdTerminal : public Terminal {
public:
explicit FdTerminal(const esp_modem_dte_config *config);
@ -63,9 +84,9 @@ private:
static const size_t TASK_STOP = SignalGroup::bit2;
static const size_t TASK_PARAMS = SignalGroup::bit3;
uart_resource uart;
File f;
Resource resource;
SignalGroup signal;
int fd;
Task task_handle;
};
@ -78,19 +99,13 @@ std::unique_ptr<Terminal> create_vfs_terminal(const esp_modem_dte_config *config
}
FdTerminal::FdTerminal(const esp_modem_dte_config *config) :
uart(config, nullptr), signal(), fd(-1),
f(config->vfs_config.dev_name), resource(config, f.fd), signal(),
task_handle(config->task_stack_size, config->task_priority, this, [](void* p){
auto t = static_cast<FdTerminal *>(p);
t->task();
Task::Delete();
if (t->fd >= 0) {
close(t->fd);
}
})
{
fd = open(config->vfs_config.dev_name, O_RDWR);
throw_if_false(fd >= 0, "Cannot open the fd");
}
{}
void FdTerminal::task()
{
@ -101,10 +116,6 @@ void FdTerminal::task()
return; // exits to the static method where the task gets deleted
}
// Set the FD to non-blocking mode
int flags = fcntl(fd, F_GETFL, nullptr) | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
while (signal.is_any(TASK_START)) {
int s;
fd_set rfds;
@ -113,9 +124,9 @@ void FdTerminal::task()
.tv_usec = 0,
};
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
FD_SET(f.fd, &rfds);
s = select(fd + 1, &rfds, nullptr, nullptr, &tv);
s = select(f.fd + 1, &rfds, nullptr, nullptr, &tv);
if (signal.is_any(TASK_PARAMS)) {
on_data_priv = on_data;
signal.clear(TASK_PARAMS);
@ -126,7 +137,7 @@ void FdTerminal::task()
} else if (s == 0) {
// ESP_LOGV(TAG, "Select exited with timeout");
} else {
if (FD_ISSET(fd, &rfds)) {
if (FD_ISSET(f.fd, &rfds)) {
if (on_data_priv) {
if (on_data_priv(nullptr, 0)) {
on_data_priv = nullptr;
@ -140,7 +151,7 @@ void FdTerminal::task()
int FdTerminal::read(uint8_t *data, size_t len)
{
int size = ::read(fd, data, len);
int size = ::read(f.fd, data, len);
if (size < 0) {
if (errno != EAGAIN) {
ESP_LOGE(TAG, "Error occurred during read: %d", errno);
@ -153,7 +164,7 @@ int FdTerminal::read(uint8_t *data, size_t len)
int FdTerminal::write(uint8_t *data, size_t len)
{
int size = ::write(fd, data, len);
int size = ::write(f.fd, data, len);
if (size < 0) {
ESP_LOGE(TAG, "Error occurred during read: %d", errno);
return 0;

View File

@ -3,7 +3,6 @@
// 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
@ -11,18 +10,14 @@
// 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 "driver/uart.h"
#include "esp_modem_config.h"
#include "uart_resource.hpp"
namespace esp_modem {
struct uart_resource {
explicit uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue);
~uart_resource();
uart_port_t port;
};
uart_resource::~uart_resource()
{
if (port >= UART_NUM_0 && port < UART_NUM_MAX) {
@ -30,8 +25,8 @@ uart_resource::~uart_resource()
}
}
uart_resource::uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue) :
port(-1)
uart_resource::uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue, int fd)
:port(-1)
{
esp_err_t res;

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cxx_include/esp_modem_dte.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
@ -20,19 +19,13 @@
#include "driver/uart.h"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
#include "cxx_include/esp_modem_dte.hpp"
#include "uart_resource.hpp"
static const char *TAG = "uart_terminal";
namespace esp_modem {
/**
* @brief Uart Resource is a platform specific struct which is implemented separately
*/
struct uart_resource {
explicit uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue);
~uart_resource();
uart_port_t port;
};
struct uart_task {
explicit uart_task(size_t stack_size, size_t priority, void *task_param, TaskFunction_t task_function) :
@ -53,7 +46,7 @@ struct uart_task {
class uart_terminal : public Terminal {
public:
explicit uart_terminal(const esp_modem_dte_config *config) :
event_queue(), uart(config, &event_queue), signal(),
event_queue(), uart(config, &event_queue, -1), signal(),
task_handle(config->task_stack_size, config->task_priority, this, s_task) {}
~uart_terminal() override = default;

View File

@ -12,38 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cxx_include/esp_modem_dte.hpp"
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_log.h"
#include "driver/uart.h"
#include "cxx_include/esp_modem_dte.hpp"
#include "esp_modem_config.h"
#include "exception_stub.hpp"
#include "uart_resource.hpp"
namespace esp_modem::terminal {
namespace esp_modem {
constexpr const char *TAG = "uart_term";
constexpr const char *TAG = "uart_resource";
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)
uart_resource::uart_resource(const esp_modem_dte_config *config, struct QueueDefinition** event_queue, int fd): port(-1)
{
ESP_LOGD(TAG, "Creating uart resource" );
struct termios tty = {};
fd = open( config->vfs_config.dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK );
throw_if_false(fd >= 0 , "Failed to open serial port");
throw_if_false(tcgetattr(fd, &tty) == 0, "Failed to tcgetattr()");
tty.c_cflag &= ~PARENB;
@ -67,9 +50,6 @@ uart_resource::uart_resource(const esp_modem_dte_config *config)
ioctl(fd, TCSETS, &tty);
}
uart_resource::~uart_resource()
{
close(fd);
}
uart_resource::~uart_resource() = default;
}
} // namespace esp_modem