Quellcode durchsuchen

Merge branch 'feature/esp_netif_ppp_params' into 'master'

Examples: Add esp_modem example to experimental cxx components

Closes IDF-1923, IDFGH-217, IDFGH-2608, IDFGH-4838, IDFGH-1229, IDFGH-3848, and IDFGH-3809

See merge request espressif/esp-idf!13161
David Čermák vor 4 Jahren
Ursprung
Commit
25e14bd8d9

+ 19 - 2
components/esp_netif/include/esp_netif_ppp.h

@@ -38,6 +38,11 @@ typedef struct esp_netif_ppp_config {
  */
 #define NETIF_PP_PHASE_OFFSET (0x100)
 
+/** @brief event id offset for internal errors
+ *
+ */
+#define NETIF_PPP_INTERNAL_ERR_OFFSET (0x200)
+
 /** @brief event ids for different PPP related events
  *
  */
@@ -68,6 +73,7 @@ typedef enum {
     NETIF_PPP_PHASE_RUNNING      = NETIF_PP_PHASE_OFFSET +  10,
     NETIF_PPP_PHASE_TERMINATE    = NETIF_PP_PHASE_OFFSET +  11,
     NETIF_PPP_PHASE_DISCONNECT   = NETIF_PP_PHASE_OFFSET +  12,
+    NETIF_PPP_CONNECT_FAILED     = NETIF_PPP_INTERNAL_ERR_OFFSET + 0,
 } esp_netif_ppp_status_event_t;
 
 /** @brief definitions of different authorisation types
@@ -89,7 +95,8 @@ typedef enum {
  * @param[in]  user User name
  * @param[in]  passwd Password
  *
- * @return     ESP_OK on success, ESP_ERR_ESP_NETIF_INVALID_PARAMS if netif null or not PPP
+ * @return     ESP_OK on success,
+ *             ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null
  */
 esp_err_t esp_netif_ppp_set_auth(esp_netif_t *netif, esp_netif_auth_type_t authtype, const char *user, const char *passwd);
 
@@ -98,10 +105,20 @@ esp_err_t esp_netif_ppp_set_auth(esp_netif_t *netif, esp_netif_auth_type_t autht
  * @param[in]  esp_netif Handle to esp-netif instance
  * @param[in]  config Pointer to PPP netif configuration structure
  *
- * @return     ESP_OK on success, ESP_ERR_ESP_NETIF_INVALID_PARAMS if netif null or not PPP
+ * @return     ESP_OK on success,
+ *             ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null
  */
 esp_err_t esp_netif_ppp_set_params(esp_netif_t *netif, const esp_netif_ppp_config_t *config);
 
+/** @brief Gets parameters configured in the supplied esp-netif.
+ *
+ * @param[in]  esp_netif Handle to esp-netif instance
+ * @param[out]  config Pointer to PPP netif configuration structure
+ *
+ * @return     ESP_OK on success,
+ *             ESP_ERR_ESP_NETIF_INVALID_PARAMS if the supplied netif is not of PPP type, or netif is null
+ */
+esp_err_t esp_netif_ppp_get_params(esp_netif_t *netif, esp_netif_ppp_config_t *config);
 
 #ifdef __cplusplus
 }

+ 1 - 1
components/esp_netif/lwip/esp_netif_lwip.c

@@ -786,7 +786,7 @@ esp_err_t esp_netif_start(esp_netif_t *esp_netif)
     if (_IS_NETIF_POINT2POINT_TYPE(esp_netif, PPP_LWIP_NETIF)) {
 #if CONFIG_PPP_SUPPORT
         // No need to start PPP interface in lwip thread
-        esp_err_t ret = esp_netif_start_ppp(esp_netif->related_data);
+        esp_err_t ret = esp_netif_start_ppp(esp_netif);
         if (ret == ESP_OK) {
             esp_netif_update_default_netif(esp_netif, ESP_NETIF_STARTED);
         }

+ 21 - 1
components/esp_netif/lwip/esp_netif_lwip_ppp.c

@@ -296,8 +296,9 @@ netif_related_data_t * esp_netif_new_ppp(esp_netif_t *esp_netif, const esp_netif
     return (netif_related_data_t *)ppp_obj;
 }
 
-esp_err_t esp_netif_start_ppp(netif_related_data_t *netif_related)
+esp_err_t esp_netif_start_ppp(esp_netif_t *esp_netif)
 {
+    netif_related_data_t *netif_related = esp_netif->related_data;
     lwip_peer2peer_ctx_t *ppp_ctx = (lwip_peer2peer_ctx_t *)netif_related;
     assert(ppp_ctx->base.netif_type == PPP_LWIP_NETIF);
 
@@ -305,6 +306,9 @@ esp_err_t esp_netif_start_ppp(netif_related_data_t *netif_related)
     esp_err_t err = pppapi_connect(ppp_ctx->ppp, 0);
     if (err != ESP_OK) {
         ESP_LOGE(TAG, "%s: PPP connection cannot be started", __func__);
+        if (ppp_ctx->ppp_error_event_enabled) {
+            esp_event_post(NETIF_PPP_STATUS, NETIF_PPP_CONNECT_FAILED, esp_netif, sizeof(esp_netif), 0);
+        }
         return ESP_FAIL;
     }
     return ESP_OK;
@@ -343,10 +347,26 @@ void esp_netif_destroy_ppp(netif_related_data_t *netif_related)
 
 esp_err_t esp_netif_ppp_set_params(esp_netif_t *netif, const esp_netif_ppp_config_t *config)
 {
+    if (netif == NULL || netif->related_data == NULL || config == NULL ||
+        ((struct lwip_peer2peer_ctx *)netif->related_data)->base.netif_type != PPP_LWIP_NETIF) {
+        return ESP_ERR_INVALID_ARG;
+    }
     struct lwip_peer2peer_ctx *obj =  (struct lwip_peer2peer_ctx *)netif->related_data;
     obj->ppp_phase_event_enabled = config->ppp_phase_event_enabled;
     obj->ppp_error_event_enabled = config->ppp_error_event_enabled;
     return ESP_OK;
 }
 
+esp_err_t esp_netif_ppp_get_params(esp_netif_t *netif, esp_netif_ppp_config_t *config)
+{
+    if (netif == NULL || netif->related_data == NULL || config == NULL ||
+        ((struct lwip_peer2peer_ctx *)netif->related_data)->base.netif_type != PPP_LWIP_NETIF) {
+        return ESP_ERR_INVALID_ARG;
+    }
+    struct lwip_peer2peer_ctx *obj =  (struct lwip_peer2peer_ctx *)netif->related_data;
+    config->ppp_phase_event_enabled = obj->ppp_phase_event_enabled;
+    config->ppp_error_event_enabled = obj->ppp_error_event_enabled;
+    return ESP_OK;
+}
+
 #endif /* CONFIG_ESP_NETIF_TCPIP_LWIP */

+ 2 - 2
components/esp_netif/lwip/esp_netif_lwip_ppp.h

@@ -32,12 +32,12 @@ netif_related_data_t * esp_netif_new_ppp(esp_netif_t *esp_netif, const esp_netif
 /**
  * @brief  Creates new PPP related structure
  *
- * @param[in]    netif_related pointer to internal ppp context instance
+ * @param[in]     esp_netif pointer esp-netif instance
  *
  * @return
  *         - ESP_OK on success
  */
-esp_err_t esp_netif_start_ppp(netif_related_data_t *netif_related);
+esp_err_t esp_netif_start_ppp(esp_netif_t *esp_netif);
 
 /**
  * @brief  Data path API to input incoming packets to PPP

+ 8 - 0
examples/cxx/experimental/esp_modem_cxx/CMakeLists.txt

@@ -0,0 +1,8 @@
+# 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 $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(simple_cxx_ppp_client)

+ 18 - 0
examples/cxx/experimental/esp_modem_cxx/README.md

@@ -0,0 +1,18 @@
+| Supported Targets | ESP32 | ESP32-S2 |
+| ----------------- | ----- | -------- |
+
+# 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. 
+
+## About the esp_modem
+
+Please check the component [README](managed_components/espressif__esp_modem/README.md)
+
+Or refer to the component's [documentation](managed_components/espressif__esp_modem/docs/html/index.html)

+ 2 - 0
examples/cxx/experimental/esp_modem_cxx/main/CMakeLists.txt

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

+ 42 - 0
examples/cxx/experimental/esp_modem_cxx/main/Kconfig.projbuild

@@ -0,0 +1,42 @@
+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
+
+endmenu

+ 7 - 0
examples/cxx/experimental/esp_modem_cxx/main/idf_component.yml

@@ -0,0 +1,7 @@
+targets:
+  - esp32
+  - esp32s2
+description: cmux example of esp_modem
+dependencies:
+  espressif/esp_modem:
+    version: "0.1.9"

+ 137 - 0
examples/cxx/experimental/esp_modem_cxx/main/simple_client.cpp

@@ -0,0 +1,137 @@
+/* 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 "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"
+
+
+#define BROKER_URL "mqtt://mqtt.eclipseprojects.io"
+
+using namespace esp_modem;
+using namespace idf::event;
+
+constexpr auto 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 the DTE */
+    esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
+
+    /* 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 DTE, PPP and DCE objects */
+    auto uart_dte = create_uart_dte(&dte_config);
+    assert(uart_dte);
+
+    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, uart_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) {
+        if (dce->set_pin(CONFIG_EXAMPLE_SIM_PIN) == command_result::OK) {
+            vTaskDelay(pdMS_TO_TICKS(1000)); // Need to wait for some time after unlocking the SIM
+        } else {
+            ESP_LOGE(TAG, "Failed to set PIN... exiting");
+            return;
+        }
+    }
+#endif
+
+    if (dce->set_mode(esp_modem::modem_mode::CMUX_MODE) && dce->set_mode(esp_modem::modem_mode::DATA_MODE)) {
+        ESP_LOGI(TAG, "Modem has correctly entered multiplexed command/data mode");
+    } 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));
+    }
+    ESP_LOGI(TAG, "Operator name: %s", str.c_str());
+
+    /* 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, "Got IP address");
+        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));
+
+        /* 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) {
+                                            ESP_LOGI(TAG, " TOPIC: %s", mqtt.get_topic(data).c_str());
+                                            ESP_LOGI(TAG, " DATA: %s", mqtt.get_data(data).c_str());
+                                        });
+        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)) {
+                ESP_LOGI(TAG, "Data received");
+                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) {
+        ESP_LOGI(TAG, "Modem IMSI number: %s", str.c_str());
+    }
+}

+ 101 - 0
examples/cxx/experimental/esp_modem_cxx/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 <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;

+ 77 - 0
examples/cxx/experimental/esp_modem_cxx/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 <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_

+ 8 - 0
examples/cxx/experimental/esp_modem_cxx/sdkconfig.defaults

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

+ 1 - 0
tools/ci/check_examples_cmake_make-cmake_ignore.txt

@@ -1,6 +1,7 @@
 components/
 common_components/
 cxx/experimental/experimental_cpp_component/
+cxx/experimental/esp_modem_cxx
 main/
 build_system/cmake/
 mb_example_common/

+ 1 - 0
tools/ci/check_examples_cmake_make-make_ignore.txt

@@ -2,3 +2,4 @@ build_system/cmake
 temp_
 examples/bluetooth/bluedroid/ble_50/
 examples/cxx/experimental/blink_cxx
+examples/cxx/experimental/esp_modem_cxx/