diff --git a/esp_modem/examples/simple_cxx_client/CMakeLists.txt b/esp_modem/examples/simple_cmux_client/CMakeLists.txt similarity index 62% rename from esp_modem/examples/simple_cxx_client/CMakeLists.txt rename to esp_modem/examples/simple_cmux_client/CMakeLists.txt index ccd2e1433..dfe712c00 100644 --- a/esp_modem/examples/simple_cxx_client/CMakeLists.txt +++ b/esp_modem/examples/simple_cmux_client/CMakeLists.txt @@ -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) diff --git a/esp_modem/examples/simple_cmux_client/README.md b/esp_modem/examples/simple_cmux_client/README.md new file mode 100644 index 000000000..3f9b2528a --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/README.md @@ -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) diff --git a/esp_modem/examples/simple_cmux_client/adc.log b/esp_modem/examples/simple_cmux_client/adc.log new file mode 100644 index 000000000..e69de29bb diff --git a/esp_modem/examples/simple_cmux_client/main/CMakeLists.txt b/esp_modem/examples/simple_cmux_client/main/CMakeLists.txt new file mode 100644 index 000000000..010b56bde --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "simple_client.cpp" "simple_mqtt_client.cpp" + INCLUDE_DIRS ".") diff --git a/esp_modem/examples/simple_cmux_client/main/Kconfig.projbuild b/esp_modem/examples/simple_cmux_client/main/Kconfig.projbuild new file mode 100644 index 000000000..2933fe830 --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/main/Kconfig.projbuild @@ -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 diff --git a/esp_modem/examples/simple_cmux_client/main/simple_client.cpp b/esp_modem/examples/simple_cmux_client/main/simple_client.cpp new file mode 100644 index 000000000..1fd8c70e8 --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/main/simple_client.cpp @@ -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 +#include +#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(); + 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 = create_BG96_dce(&dce_config, dte, esp_netif); +#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM800 == 1 + std::unique_ptr dce = create_SIM800_dce(&dce_config, uart_dte, esp_netif); +#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600 == 1 + std::unique_ptr 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 +} diff --git a/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.cpp b/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.cpp new file mode 100644 index 000000000..526024037 --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.cpp @@ -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 +#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(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; diff --git a/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.hpp b/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.hpp new file mode 100644 index 000000000..ca3d564af --- /dev/null +++ b/esp_modem/examples/simple_cmux_client/main/simple_mqtt_client.hpp @@ -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 +#include +#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 h; +}; + + +#endif //_SIMPLE_MQTT_CLIENT_H_ diff --git a/esp_modem/examples/simple_cxx_client/sdkconfig.defaults b/esp_modem/examples/simple_cmux_client/sdkconfig.defaults similarity index 59% rename from esp_modem/examples/simple_cxx_client/sdkconfig.defaults rename to esp_modem/examples/simple_cmux_client/sdkconfig.defaults index 6c519dc85..50b2fb0dd 100644 --- a/esp_modem/examples/simple_cxx_client/sdkconfig.defaults +++ b/esp_modem/examples/simple_cmux_client/sdkconfig.defaults @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/esp_modem/examples/simple_cxx_client/Makefile b/esp_modem/examples/simple_cxx_client/Makefile deleted file mode 100644 index 25571d087..000000000 --- a/esp_modem/examples/simple_cxx_client/Makefile +++ /dev/null @@ -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 - diff --git a/esp_modem/examples/simple_cxx_client/README.md b/esp_modem/examples/simple_cxx_client/README.md deleted file mode 100644 index 376f37eb8..000000000 --- a/esp_modem/examples/simple_cxx_client/README.md +++ /dev/null @@ -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. diff --git a/esp_modem/examples/simple_cxx_client/main/CMakeLists.txt b/esp_modem/examples/simple_cxx_client/main/CMakeLists.txt deleted file mode 100644 index 2d88e9d93..000000000 --- a/esp_modem/examples/simple_cxx_client/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "simple_client.cpp" - INCLUDE_DIRS ".") diff --git a/esp_modem/examples/simple_cxx_client/main/Kconfig.projbuild b/esp_modem/examples/simple_cxx_client/main/Kconfig.projbuild deleted file mode 100644 index 717e162ef..000000000 --- a/esp_modem/examples/simple_cxx_client/main/Kconfig.projbuild +++ /dev/null @@ -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 diff --git a/esp_modem/examples/simple_cxx_client/main/simple_client.cpp b/esp_modem/examples/simple_cxx_client/main/simple_client.cpp deleted file mode 100644 index da3d71f56..000000000 --- a/esp_modem/examples/simple_cxx_client/main/simple_client.cpp +++ /dev/null @@ -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 -#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 -#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"); -} diff --git a/esp_modem/include/esp_modem_config.h b/esp_modem/include/esp_modem_config.h index a3fa3923c..be0df5fc1 100644 --- a/esp_modem/include/esp_modem_config.h +++ b/esp_modem/include/esp_modem_config.h @@ -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 \ }\ } diff --git a/esp_modem/private_include/uart_resource.hpp b/esp_modem/private_include/uart_resource.hpp new file mode 100644 index 000000000..3be5efb4b --- /dev/null +++ b/esp_modem/private_include/uart_resource.hpp @@ -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 create_vfs_terminal(const esp_modem_dte_config *config); + +} // namespace esp_modem + +#endif // _UART_RESOURCE_HPP_ diff --git a/esp_modem/src/esp_modem_cmux.cpp b/esp_modem/src/esp_modem_cmux.cpp index efb77381f..8c9d986fe 100644 --- a/esp_modem/src/esp_modem_cmux.cpp +++ b/esp_modem/src/esp_modem_cmux.cpp @@ -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; } diff --git a/esp_modem/src/esp_modem_command_library.cpp b/esp_modem/src/esp_modem_command_library.cpp index 9e9785c98..ec4d6893c 100644 --- a/esp_modem/src/esp_modem_command_library.cpp +++ b/esp_modem/src/esp_modem_command_library.cpp @@ -28,12 +28,12 @@ command_result generic_command(CommandableIf* t, const std::string &command, const std::list& 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; diff --git a/esp_modem/src/esp_modem_term_fs.cpp b/esp_modem/src/esp_modem_term_fs.cpp index dea8777e0..b9ff1043e 100644 --- a/esp_modem/src/esp_modem_term_fs.cpp +++ b/esp_modem/src/esp_modem_term_fs.cpp @@ -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(config, nullptr, fd)) + {} std::optional 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 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(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; diff --git a/esp_modem/src/esp_modem_term_uart.cpp b/esp_modem/src/esp_modem_term_uart.cpp index 5eb885720..2828f440e 100644 --- a/esp_modem/src/esp_modem_term_uart.cpp +++ b/esp_modem/src/esp_modem_term_uart.cpp @@ -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; diff --git a/esp_modem/src/esp_modem_uart.cpp b/esp_modem/src/esp_modem_uart.cpp index 7c7872b10..1dc2eea1b 100644 --- a/esp_modem/src/esp_modem_uart.cpp +++ b/esp_modem/src/esp_modem_uart.cpp @@ -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; diff --git a/esp_modem/src/esp_modem_uart_linux.cpp b/esp_modem/src/esp_modem_uart_linux.cpp index 4d118bae2..da68e7757 100644 --- a/esp_modem/src/esp_modem_uart_linux.cpp +++ b/esp_modem/src/esp_modem_uart_linux.cpp @@ -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 #include #include -#include -#include -#include -#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; -} \ No newline at end of file +} // namespace esp_modem \ No newline at end of file