]> granicus.if.org Git - esp-idf/commitdiff
modem: support modem facility and PPPoS
authormorris <maoshengrong@espressif.com>
Fri, 23 Nov 2018 03:53:20 +0000 (11:53 +0800)
committermorris <maoshengrong@espressif.com>
Wed, 23 Jan 2019 10:53:32 +0000 (18:53 +0800)
1. add support for ESP32 DTE
2. add support for SIM800/BG96 DCE
3. add PPPoS setup procedure
4. add support for SMS
5. add mqtt example after PPP connection established

18 files changed:
components/lwip/Kconfig
components/lwip/port/esp32/include/lwipopts.h
examples/protocols/pppos_client/README.md
examples/protocols/pppos_client/components/modem/CMakeLists.txt [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/component.mk [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/bg96.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/esp_modem.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/esp_modem_dce.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/esp_modem_dce_service.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/esp_modem_dte.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/include/sim800.h [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/src/bg96.c [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/src/esp_modem.c [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/src/esp_modem_dce_service.c [new file with mode: 0644]
examples/protocols/pppos_client/components/modem/src/sim800.c [new file with mode: 0644]
examples/protocols/pppos_client/main/Kconfig.projbuild
examples/protocols/pppos_client/main/pppos_client_main.c
examples/protocols/pppos_client/sdkconfig.defaults

index f124ac0b110b232e2d0dc67d59c5ad1eac132cab..9e2e77c34ac9942eae5902a6be1df4c044d64315 100644 (file)
@@ -7,13 +7,13 @@ config L2_TO_L3_COPY
         If this feature is enabled, all traffic from layer2(WIFI Driver) will be
         copied to a new buffer before sending it to layer3(LWIP stack), freeing
         the layer2 buffer.
-        Please be notified that the total layer2 receiving buffer is fixed and 
-        ESP32 currently supports 25 layer2 receiving buffer, when layer2 buffer 
-        runs out of memory, then the incoming packets will be dropped in hardware. 
+        Please be notified that the total layer2 receiving buffer is fixed and
+        ESP32 currently supports 25 layer2 receiving buffer, when layer2 buffer
+        runs out of memory, then the incoming packets will be dropped in hardware.
         The layer3 buffer is allocated from the heap, so the total layer3 receiving
-        buffer depends on the available heap size, when heap runs out of memory,  
-        no copy will be sent to layer3 and packet will be dropped in layer2. 
-        Please make sure you fully understand the impact of this feature before 
+        buffer depends on the available heap size, when heap runs out of memory,
+        no copy will be sent to layer3 and packet will be dropped in layer2.
+        Please make sure you fully understand the impact of this feature before
         enabling it.
 
 config LWIP_IRAM_OPTIMIZATION
@@ -121,17 +121,17 @@ config LWIP_ETHARP_TRUST_IP_MAC
          - The LAN peer may have bug to update the ARP table after the ARP entry is aged out.
            If the ARP entry on the LAN peer is aged out but failed to be updated, all IP packets
            sent from LWIP to the LAN peer will be dropped by LAN peer.
-         - The LAN peer may not be trustful, the LAN peer may send IP packets to LWIP with 
+         - The LAN peer may not be trustful, the LAN peer may send IP packets to LWIP with
            two different MACs, but the same IP address. If this happens, the LWIP has problem
            to receive IP packets from LAN peer.
 
-        So the recommendation is to disable this option. 
+        So the recommendation is to disable this option.
         Here the LAN peer means the other side to which the ESP station or soft-AP is connected.
 
 config ESP_GRATUITOUS_ARP
     bool "Send gratuitous ARP periodically"
     default y
-    help 
+    help
         Enable this option allows to send gratuitous ARP periodically.
 
         This option solve the compatibility issues.If the ARP table of the AP is old, and the AP
@@ -175,8 +175,8 @@ config LWIP_DHCPS_LEASE_UNIT
     range 1 3600
     default 60
     help
-        The DHCP server is calculating lease time multiplying the sent 
-        and received times by this number of seconds per unit. 
+        The DHCP server is calculating lease time multiplying the sent
+        and received times by this number of seconds per unit.
         The default is 60, that equals one minute.
 
 config LWIP_DHCPS_MAX_STATION_NUM
@@ -342,17 +342,17 @@ config TCP_RECVMBOX_SIZE
     range 6 64
     help
         Set TCP receive mail box size. Generally bigger value means higher throughput
-        but more memory. The recommended value is: TCP_WND_DEFAULT/TCP_MSS + 2, e.g. if 
-        TCP_WND_DEFAULT=14360, TCP_MSS=1436, then the recommended receive mail box size is 
+        but more memory. The recommended value is: TCP_WND_DEFAULT/TCP_MSS + 2, e.g. if
+        TCP_WND_DEFAULT=14360, TCP_MSS=1436, then the recommended receive mail box size is
         (14360/1436 + 2) = 12.
 
         TCP receive mail box is a per socket mail box, when the application receives packets
-        from TCP socket, LWIP core firstly posts the packets to TCP receive mail box and the 
-        application then fetches the packets from mail box. It means LWIP can caches maximum 
+        from TCP socket, LWIP core firstly posts the packets to TCP receive mail box and the
+        application then fetches the packets from mail box. It means LWIP can caches maximum
         TCP_RECCVMBOX_SIZE packets for each TCP socket, so the maximum possible cached TCP packets
         for all TCP sockets is TCP_RECCVMBOX_SIZE multiples the maximum TCP socket number. In other
         words, the bigger TCP_RECVMBOX_SIZE means more memory.
-        On the other hand, if the receiv mail box is too small, the mail box may be full. If the 
+        On the other hand, if the receiv mail box is too small, the mail box may be full. If the
         mail box is full, the LWIP drops the packets. So generally we need to make sure the TCP
         receive mail box is big enough to avoid packet drop between LWIP core and application.
 
@@ -371,7 +371,7 @@ config ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES
     help
         This option is enabled when the following scenario happen:
         network dropped and reconnected, IP changes is like: 192.168.0.2->0.0.0.0->192.168.0.2
-        
+
         Disable this option to keep consistent with the original LWIP code behavior.
 
 
@@ -420,10 +420,10 @@ config UDP_RECVMBOX_SIZE
     help
         Set UDP receive mail box size. The recommended value is 6.
 
-        UDP receive mail box is a per socket mail box, when the application receives packets 
+        UDP receive mail box is a per socket mail box, when the application receives packets
         from UDP socket, LWIP core firstly posts the packets to UDP receive mail box and the
         application then fetches the packets from mail box. It means LWIP can caches maximum
-        UDP_RECCVMBOX_SIZE packets for each UDP socket, so the maximum possible cached UDP packets 
+        UDP_RECCVMBOX_SIZE packets for each UDP socket, so the maximum possible cached UDP packets
         for all UDP sockets is UDP_RECCVMBOX_SIZE multiples the maximum UDP socket number. In other
         words, the bigger UDP_RECVMBOX_SIZE means more memory.
         On the other hand, if the receiv mail box is too small, the mail box may be full. If the
@@ -476,6 +476,13 @@ menuconfig PPP_SUPPORT
 
         PPP over serial support is experimental and unsupported.
 
+config PPP_NOTIFY_PHASE_SUPPORT
+    bool "Enable Notify Phase Callback"
+    depends on PPP_SUPPORT
+    default n
+    help
+        Enable to set a callback which is called on change of the internal PPP state machine.
+
 config PPP_PAP_SUPPORT
    bool "Enable PAP support"
    depends on PPP_SUPPORT
index 53b598609c6bde706f80bc47ec310bb341b99afe..27f436d9dcdf59318d4cc3a2b4952af6bc60e790 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
+ *    derived from this software without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  *
  * This file is part of the lwIP TCP/IP stack.
- * 
+ *
  * Author: Simon Goldschmidt
  *
  */
  * scenario happens: 192.168.0.2 -> 0.0.0.0 -> 192.168.0.2 or 192.168.0.2 -> 0.0.0.0
  */
 
-#define ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES  CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES 
+#define ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES  CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES
 /*
  *     LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
  *         events (accept, sent, etc) that happen in the system.
 
 #if PPP_SUPPORT
 
+/**
+ * PPP_NOTIFY_PHASE==1: Support PPP notify phase.
+ */
+#define PPP_NOTIFY_PHASE                CONFIG_PPP_NOTIFY_PHASE_SUPPORT
+
 /**
  * PAP_SUPPORT==1: Support PAP.
  */
 #if CONFIG_LWIP_IRAM_OPTIMIZATION
 #define ESP_IRAM_ATTR                   IRAM_ATTR
 #else
-#define ESP_IRAM_ATTR                   
+#define ESP_IRAM_ATTR
 #endif
 
 #if ESP_PERF
@@ -782,7 +787,7 @@ enum {
 };
 
 #else
-#define DBG_PERF_PATH_SET(dir, point)   
+#define DBG_PERF_PATH_SET(dir, point)
 #define DBG_PERF_FILTER_LEN             1000
 #endif
 
index f9e0ce690fa494bd29a75d55c13dff12e32c2aae..bcfb8e50347ac7e7852b038a303955d00beb9181 100644 (file)
@@ -1,9 +1,131 @@
-#PPP over Serial (PPPoS) client example
+# PPP over Serial (PPPoS) client example
 
-It shows example of ppp client using lwip PPPoS api and GSM.
-Before you run this example, make sure your GSM is in command mode
-and is registered to network.
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
 
-PPP over serial support is experimental and unsupported. This example was tested with GSM Telit GL865-DUAL V3.
+## Overview
 
-See the README.md file in the upper level 'examples' directory for more information about examples.
+A general PPP application consists of two parts: PPP server which is provided by cellular modem module and PPP client which is provided by ESP32 in this example.
+Standard operating systems like Windows and Unix integrate a full PPP stack and provide a way to setup PPP connection at the same time. But how can we get access to Internet by PPP protocol in a resource constrained system? Fortunately, the PPP protocol has already been implemented in lwIP, but it doesn't supply a common way to setup a PPP connection.
+This example introduces a library focusing on sending and parsing AT commands, and also provides useful functions to set up PPP connection.
+When PPP connection has been established, the IP packet flow from application side will be transmitted to Internet by cellular module. 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 example
+
+### Hardware Required
+
+To run this example, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC).
+For test purpose, you also need a cellular modem module. Here we take the [SIM800L](http://www.simcom.com/product/showproduct.php?lang=en&id=277) and [BG96](https://www.quectel.com/product/bg96.htm) as an example.
+You can also try other modules as long as they embedded PPP protocol.
+
+**Note:** Since SIM800L only support **2G** which will **not** work in some countries. And also keep in mind that in some other countries it will stop working soon (many remaining 2G networks will be switched off in the next 2-3 years). So you should **check with your local providers for further details** if you try this example with any 2G modules.
+
+#### Pin Assignment
+
+**Note:** The following pin assignments are used by default which can be changed in menuconfig.
+
+| ESP32  | Cellular Modem |
+| ------ | -------------- |
+| GPIO25 | RX             |
+| GPIO26 | TX             |
+| GND    | GND            |
+| 5V     | VCC            |
+
+### Configure the project
+
+Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you are using CMake based build system. Then go into `Example Configuration` menu.
+
+- Choose the modem module in `Choose supported modem device(DCE)` option, currently we only support BG96 and SIM800L.
+- Set the access point name in `Set Access Point Name(APN)` option, which should depend on the operator of your SIM card.
+- Set the username and password for PPP authentication in `Set username for authentication` and `Set password for authentication` options.
+- Select `Send MSG before power off` if you want to send a short message in the end of this example, and also you need to set the phone number correctly in `Peer Phone Number(with area code)` option.
+- In `UART Configuration` menu, you need to set the GPIO numbers of UART and task specific parameters such as stack size, priority.
+
+**Note:** During PPP setup, we should specify the way of authentication negotiation. By default it's configured to `PAP`. You can change to others (e.g. `CHAP`) in `Component config-->LWIP-->Enable PPP support` menu.
+
+### Build and Flash
+
+Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you are using CMake based build system.
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+The example will get module and operator's information after start up, and then go into PPP mode to start mqtt client operations. This example will also send a short message to someone's phone if you have enabled this feature in menuconfig.
+
+### BG96 Output
+
+```bash
+I (1276) pppos_example: Module: BG96
+I (1276) pppos_example: Operator: "CHINA MOBILE CMCC"
+I (1276) pppos_example: IMEI: 866425030121349
+I (1276) pppos_example: IMSI: 460007454185220
+I (1476) pppos_example: rssi: 27, ber: 99
+I (1676) pppos_example: Battery voltage: 3908 mV
+I (1876) pppos_example: Modem PPP Started
+I (2656) pppos_example: Modem Connect to PPP Server
+I (2656) pppos_example: ~~~~~~~~~~~~~~
+I (2656) pppos_example: IP          : 10.65.71.127
+I (2656) pppos_example: Netmask     : 255.255.255.255
+I (2666) pppos_example: Gateway     : 10.64.64.64
+I (2666) pppos_example: Name Server1: 211.136.112.50
+I (2676) pppos_example: Name Server2: 211.136.150.66
+I (2676) pppos_example: ~~~~~~~~~~~~~~
+I (2686) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
+I (2696) pppos_example: MQTT other event id: 7
+I (3426) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
+I (3856) pppos_example: MQTT_EVENT_CONNECTED
+I (3856) pppos_example: sent subscribe successful, msg_id=20132
+I (4226) pppos_example: MQTT_EVENT_SUBSCRIBED, msg_id=20132
+I (4226) pppos_example: sent publish successful, msg_id=0
+I (4646) pppos_example: MQTT_EVENT_DATA
+TOPIC=/topic/esp-pppos
+DATA=esp32-pppos
+I (4696) pppos_example: Modem PPP Stopped
+I (9466) pppos_example: Send send message [Welcome to ESP32!] ok
+I (9666) pppos_example: Power down
+```
+
+### SIM800L Output
+```bash
+I (1276) pppos_example: Module: SIMCOM_SIM800L
+I (1276) pppos_example: Operator: "CHINA MOBILE"
+I (1276) pppos_example: IMEI: 865992039850864
+I (1276) pppos_example: IMSI: 460007454185220
+I (1476) pppos_example: rssi: 25, ber: 0
+I (1676) pppos_example: Battery voltage: 4674 mV
+I (1876) pppos_example: Modem PPP Started
+I (2806) pppos_example: Modem Connect to PPP Server
+I (2806) pppos_example: ~~~~~~~~~~~~~~
+I (2806) pppos_example: IP          : 10.188.173.2
+I (2806) pppos_example: Netmask     : 255.255.255.255
+I (2816) pppos_example: Gateway     : 192.168.254.254
+I (2816) pppos_example: Name Server1: 211.136.112.50
+I (2826) pppos_example: Name Server2: 211.136.150.66
+I (2826) pppos_example: ~~~~~~~~~~~~~~
+I (2836) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
+I (2846) pppos_example: MQTT other event id: 7
+I (8156) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
+I (8826) pppos_example: MQTT_EVENT_CONNECTED
+I (8826) pppos_example: sent subscribe successful, msg_id=26237
+I (9526) pppos_example: MQTT_EVENT_SUBSCRIBED, msg_id=26237
+I (9526) pppos_example: sent publish successful, msg_id=0
+I (10326) pppos_example: MQTT_EVENT_DATA
+TOPIC=/topic/esp-pppos
+DATA=esp32-pppos
+I (10376) pppos_example: Modem PPP Stopped
+I (14526) pppos_example: Send send message [Welcome to ESP32!] ok
+I (15076) pppos_example: Power down
+```
+
+## Troubleshooting
+1. Why sending AT commands always failed and this example just keeping rebooting? e.g.
+
+```bash
+E (626) sim800: sim800_sync(293): send command failed
+E (626) sim800: sim800_init(628): sync failed
+```
+   * Make sure your modem module is in command mode stably before you run this example.
+
+(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
diff --git a/examples/protocols/pppos_client/components/modem/CMakeLists.txt b/examples/protocols/pppos_client/components/modem/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1b1272e
--- /dev/null
@@ -0,0 +1,12 @@
+set(COMPONENT_ADD_INCLUDEDIRS .)
+
+set(COMPONENT_SRCS  "src/esp_modem.c"
+                    "src/esp_modem_dce_service"
+                    "src/sim800.c"
+                    "src/bg96.c")
+
+set(COMPONENT_ADD_INCLUDEDIRS "include")
+
+set(COMPONENT_REQUIRES driver)
+
+register_component()
diff --git a/examples/protocols/pppos_client/components/modem/component.mk b/examples/protocols/pppos_client/components/modem/component.mk
new file mode 100644 (file)
index 0000000..24cab8b
--- /dev/null
@@ -0,0 +1,3 @@
+COMPONENT_ADD_INCLUDEDIRS := include
+
+COMPONENT_SRCDIRS := src
diff --git a/examples/protocols/pppos_client/components/modem/include/bg96.h b/examples/protocols/pppos_client/components/modem/include/bg96.h
new file mode 100644 (file)
index 0000000..f31d0a2
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2015-2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_modem_dce_service.h"
+#include "esp_modem.h"
+
+/**
+ * @brief Create and initialize BG96 object
+ *
+ * @param dte Modem DTE object
+ * @return modem_dce_t* Modem DCE object
+ */
+modem_dce_t *bg96_init(modem_dte_t *dte);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/include/esp_modem.h b/examples/protocols/pppos_client/components/modem/include/esp_modem.h
new file mode 100644 (file)
index 0000000..8ed0b4b
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright 2015-2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_modem_dce.h"
+#include "esp_modem_dte.h"
+#include "esp_event.h"
+#include "driver/uart.h"
+
+/**
+ * @brief Declare Event Base for ESP Modem
+ *
+ */
+ESP_EVENT_DECLARE_BASE(ESP_MODEM_EVENT);
+
+/**
+ * @brief ESP Modem Event
+ *
+ */
+typedef enum {
+    MODEM_EVENT_PPP_START,      /*!< ESP Modem Start PPP Session */
+    MODEM_EVENT_PPP_CONNECT,    /*!< ESP Modem Connect to PPP Server */
+    MODEM_EVENT_PPP_DISCONNECT, /*!< ESP Modem Disconnect from PPP Server */
+    MODEM_EVENT_PPP_STOP,       /*!< ESP Modem Stop PPP Session*/
+    MODEM_EVENT_UNKNOWN         /*!< ESP Modem Unknown Response */
+} esp_modem_event_t;
+
+/**
+ * @brief ESP Modem DTE Configuration
+ *
+ */
+typedef struct {
+    uart_port_t port_num;           /*!< UART port number */
+    uart_word_length_t data_bits;   /*!< Data bits of UART */
+    uart_stop_bits_t stop_bits;     /*!< Stop bits of UART */
+    uart_parity_t parity;           /*!< Parity type */
+    modem_flow_ctrl_t flow_control; /*!< Flow control type */
+    uint32_t baud_rate;             /*!< Communication baud rate */
+} esp_modem_dte_config_t;
+
+/**
+ * @brief ESP Modem DTE Default Configuration
+ *
+ */
+#define ESP_MODEM_DTE_DEFAULT_CONFIG()          \
+    {                                           \
+        .port_num = UART_NUM_1,                 \
+        .data_bits = UART_DATA_8_BITS,          \
+        .stop_bits = UART_STOP_BITS_1,          \
+        .parity = UART_PARITY_DISABLE,          \
+        .baud_rate = 115200,                    \
+        .flow_control = MODEM_FLOW_CONTROL_NONE \
+    }
+
+/**
+ * @brief Create and initialize Modem DTE object
+ *
+ * @param config configuration of ESP Modem DTE object
+ * @return modem_dte_t*
+ *      - Modem DTE object
+ */
+modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config);
+
+/**
+ * @brief Register event handler for ESP Modem event loop
+ *
+ * @param dte modem_dte_t type object
+ * @param handler event handler to register
+ * @param handler_args arguments for registered handler
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_ERR_NO_MEM on allocating memory for the handler failed
+ *      - ESP_ERR_INVALID_ARG on invalid combination of event base and event id
+ */
+esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args);
+
+/**
+ * @brief Unregister event handler for ESP Modem event loop
+ *
+ * @param dte modem_dte_t type object
+ * @param handler event handler to unregister
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG on invalid combination of event base and event id
+ */
+esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t handler);
+
+/**
+ * @brief PPPoS Client IP Information
+ *
+ */
+typedef struct {
+    ip4_addr_t ip;      /*!< IP Address */
+    ip4_addr_t netmask; /*!< Net Mask */
+    ip4_addr_t gw;      /*!< Gateway */
+    ip4_addr_t ns1;     /*!< Name Server1 */
+    ip4_addr_t ns2;     /*!< Name Server2 */
+} ppp_client_ip_info_t;
+
+/**
+ * @brief Setup PPP Session
+ *
+ * @param dte Modem DTE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_setup_ppp(modem_dte_t *dte);
+
+/**
+ * @brief Exit PPP Session
+ *
+ * @param dte Modem DTE Object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_exit_ppp(modem_dte_t *dte);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/include/esp_modem_dce.h b/examples/protocols/pppos_client/components/modem/include/esp_modem_dce.h
new file mode 100644 (file)
index 0000000..a0633cb
--- /dev/null
@@ -0,0 +1,99 @@
+// Copyright 2015-2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_types.h"
+#include "esp_err.h"
+#include "esp_modem_dte.h"
+
+typedef struct modem_dce modem_dce_t;
+typedef struct modem_dte modem_dte_t;
+
+/**
+ * @brief Result Code from DCE
+ *
+ */
+#define MODEM_RESULT_CODE_SUCCESS "OK"              /*!< Acknowledges execution of a command */
+#define MODEM_RESULT_CODE_CONNECT "CONNECT"         /*!< A connection has been established */
+#define MODEM_RESULT_CODE_RING "RING"               /*!< Detect an incoming call signal from network */
+#define MODEM_RESULT_CODE_NO_CARRIER "NO CARRIER"   /*!< Connection termincated or establish a connection failed */
+#define MODEM_RESULT_CODE_ERROR "ERROR"             /*!< Command not recognized, command line maximum length exceeded, parameter value invalid */
+#define MODEM_RESULT_CODE_NO_DIALTONE "NO DIALTONE" /*!< No dial tone detected */
+#define MODEM_RESULT_CODE_BUSY "BUSY"               /*!< Engaged signal detected */
+#define MODEM_RESULT_CODE_NO_ANSWER "NO ANSWER"     /*!< Wait for quiet answer */
+
+/**
+ * @brief Specific Length Constraint
+ *
+ */
+#define MODEM_MAX_NAME_LENGTH (32)     /*!< Max Module Name Length */
+#define MODEM_MAX_OPERATOR_LENGTH (32) /*!< Max Operator Name Length */
+#define MODEM_IMEI_LENGTH (15)         /*!< IMEI Number Length */
+#define MODEM_IMSI_LENGTH (15)         /*!< IMSI Number Length */
+
+/**
+ * @brief Specific Timeout Constraint, Unit: millisecond
+ *
+ */
+#define MODEM_COMMAND_TIMEOUT_DEFAULT (500)      /*!< Default timeout value for most commands */
+#define MODEM_COMMAND_TIMEOUT_OPERATOR (75000)   /*!< Timeout value for getting operator status */
+#define MODEM_COMMAND_TIMEOUT_MODE_CHANGE (3000) /*!< Timeout value for changing working mode */
+#define MODEM_COMMAND_TIMEOUT_HANG_UP (90000)    /*!< Timeout value for hang up */
+#define MODEM_COMMAND_TIMEOUT_POWEROFF (1000)    /*!< Timeout value for power down */
+
+/**
+ * @brief Working state of DCE
+ *
+ */
+typedef enum {
+    MODEM_STATE_PROCESSING, /*!< In processing */
+    MODEM_STATE_SUCCESS,    /*!< Process successfully */
+    MODEM_STATE_FAIL        /*!< Process failed */
+} modem_state_t;
+
+/**
+ * @brief DCE(Data Communication Equipment)
+ *
+ */
+struct modem_dce {
+    char imei[MODEM_IMEI_LENGTH + 1];                                                 /*!< IMEI number */
+    char imsi[MODEM_IMSI_LENGTH + 1];                                                 /*!< IMSI number */
+    char name[MODEM_MAX_NAME_LENGTH];                                                 /*!< Module name */
+    char oper[MODEM_MAX_OPERATOR_LENGTH];                                             /*!< Operator name */
+    modem_state_t state;                                                              /*!< Modem working state */
+    modem_mode_t mode;                                                                /*!< Working mode */
+    modem_dte_t *dte;                                                                 /*!< DTE which connect to DCE */
+    esp_err_t (*handle_line)(modem_dce_t *dce, const char *line);                     /*!< Handle line strategy */
+    esp_err_t (*sync)(modem_dce_t *dce);                                              /*!< Synchronization */
+    esp_err_t (*echo_mode)(modem_dce_t *dce, bool on);                                /*!< Echo command on or off */
+    esp_err_t (*store_profile)(modem_dce_t *dce);                                     /*!< Store user settings */
+    esp_err_t (*set_flow_ctrl)(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl);        /*!< Flow control on or off */
+    esp_err_t (*get_signal_quality)(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber); /*!< Get signal quality */
+    esp_err_t (*get_battery_status)(modem_dce_t *dce, uint32_t *bcs,
+                                    uint32_t *bcl, uint32_t *voltage);  /*!< Get battery status */
+    esp_err_t (*define_pdp_context)(modem_dce_t *dce, uint32_t cid,
+                                    const char *type, const char *apn); /*!< Set PDP Contex */
+    esp_err_t (*set_working_mode)(modem_dce_t *dce, modem_mode_t mode); /*!< Set working mode */
+    esp_err_t (*hang_up)(modem_dce_t *dce);                             /*!< Hang up */
+    esp_err_t (*power_down)(modem_dce_t *dce);                          /*!< Normal power down */
+    esp_err_t (*deinit)(modem_dce_t *dce);                              /*!< Deinitialize */
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/include/esp_modem_dce_service.h b/examples/protocols/pppos_client/components/modem/include/esp_modem_dce_service.h
new file mode 100644 (file)
index 0000000..9b9a3a6
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright 2015-2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_modem_dce.h"
+
+/**
+ * @brief Indicate that processing current command has done
+ *
+ * @param dce Modem DCE object
+ * @param state Modem state after processing
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static inline esp_err_t esp_modem_process_command_done(modem_dce_t *dce, modem_state_t state)
+{
+    dce->state = state;
+    return dce->dte->process_cmd_done(dce->dte);
+}
+
+/**
+ * @brief Strip the tailed "\r\n"
+ *
+ * @param str string to strip
+ * @param len length of string
+ */
+static inline void strip_cr_lf_tail(char *str, uint32_t len)
+{
+    if (str[len - 2] == '\r') {
+        str[len - 2] = '\0';
+    } else if (str[len - 1] == '\r') {
+        str[len - 1] = '\0';
+    }
+}
+
+/**
+ * @brief Default handler for response
+ * Some responses for command are simple, commonly will return OK when succeed of ERROR when failed
+ *
+ * @param dce Modem DCE object
+ * @param line line string
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_handle_response_default(modem_dce_t *dce, const char *line);
+
+/**
+ * @brief Syncronization
+ *
+ * @param dce Modem DCE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_sync(modem_dce_t *dce);
+
+/**
+ * @brief Enable or not echo mode of DCE
+ *
+ * @param dce Modem DCE object
+ * @param on true to enable echo mode, false to disable echo mode
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_echo(modem_dce_t *dce, bool on);
+
+/**
+ * @brief Store current parameter setting in the user profile
+ *
+ * @param dce Modem DCE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_store_profile(modem_dce_t *dce);
+
+/**
+ * @brief Set flow control mode of DCE in data mode
+ *
+ * @param dce Modem DCE object
+ * @param flow_ctrl flow control mode
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_set_flow_ctrl(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl);
+
+/**
+ * @brief Define PDP context
+ *
+ * @param dce Modem DCE object
+ * @param cid PDP context identifier
+ * @param type Protocol type
+ * @param apn Access point name
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn);
+
+/**
+ * @brief Hang up
+ *
+ * @param dce Modem DCE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+esp_err_t esp_modem_dce_hang_up(modem_dce_t *dce);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/include/esp_modem_dte.h b/examples/protocols/pppos_client/components/modem/include/esp_modem_dte.h
new file mode 100644 (file)
index 0000000..6664560
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_types.h"
+#include "esp_err.h"
+#include "esp_event.h"
+
+typedef struct modem_dte modem_dte_t;
+typedef struct modem_dce modem_dce_t;
+
+/**
+ * @brief Working mode of Modem
+ *
+ */
+typedef enum {
+    MODEM_COMMAND_MODE = 0, /*!< Command Mode */
+    MODEM_PPP_MODE          /*!< PPP Mode */
+} modem_mode_t;
+
+/**
+ * @brief Modem flow control type
+ *
+ */
+typedef enum {
+    MODEM_FLOW_CONTROL_NONE = 0,
+    MODEM_FLOW_CONTROL_SW,
+    MODEM_FLOW_CONTROL_HW
+} modem_flow_ctrl_t;
+
+/**
+ * @brief DTE(Data Terminal Equipment)
+ *
+ */
+struct modem_dte {
+    modem_flow_ctrl_t flow_ctrl;                                                    /*!< Flow control of DTE */
+    modem_dce_t *dce;                                                               /*!< DCE which connected to the DTE */
+    esp_err_t (*send_cmd)(modem_dte_t *dte, const char *command, uint32_t timeout); /*!< Send command to DCE */
+    int (*send_data)(modem_dte_t *dte, const char *data, uint32_t length);          /*!< Send data to DCE */
+    esp_err_t (*send_wait)(modem_dte_t *dte, const char *data, uint32_t length,
+                           const char *prompt, uint32_t timeout);      /*!< Wait for specific prompt */
+    esp_err_t (*change_mode)(modem_dte_t *dte, modem_mode_t new_mode); /*!< Changing working mode */
+    esp_err_t (*process_cmd_done)(modem_dte_t *dte);                   /*!< Callback when DCE process command done */
+    esp_err_t (*deinit)(modem_dte_t *dte);                             /*!< Deinitialize */
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/include/sim800.h b/examples/protocols/pppos_client/components/modem/include/sim800.h
new file mode 100644 (file)
index 0000000..f0455c7
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2015-2018 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.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp_modem_dce_service.h"
+#include "esp_modem.h"
+
+/**
+ * @brief Create and initialize SIM800 object
+ *
+ * @param dte Modem DTE object
+ * @return modem_dce_t* Modem DCE object
+ */
+modem_dce_t *sim800_init(modem_dte_t *dte);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/protocols/pppos_client/components/modem/src/bg96.c b/examples/protocols/pppos_client/components/modem/src/bg96.c
new file mode 100644 (file)
index 0000000..2625613
--- /dev/null
@@ -0,0 +1,477 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stdlib.h>
+#include <string.h>
+#include "esp_log.h"
+#include "bg96.h"
+
+#define MODEM_RESULT_CODE_POWERDOWN "POWERED DOWN"
+
+/**
+ * @brief Macro defined for error checking
+ *
+ */
+static const char *DCE_TAG = "bg96";
+#define DCE_CHECK(a, str, goto_tag, ...)                                              \
+    do                                                                                \
+    {                                                                                 \
+        if (!(a))                                                                     \
+        {                                                                             \
+            ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+            goto goto_tag;                                                            \
+        }                                                                             \
+    } while (0)
+
+/**
+ * @brief BG96 Modem
+ *
+ */
+typedef struct {
+    void *priv_resource; /*!< Private resource */
+    modem_dce_t parent;  /*!< DCE parent class */
+} bg96_modem_dce_t;
+
+/**
+ * @brief Handle response from AT+CSQ
+ */
+static esp_err_t bg96_handle_csq(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+CSQ", strlen("+CSQ"))) {
+        /* store value of rssi and ber */
+        uint32_t **csq = bg96_dce->priv_resource;
+        /* +CSQ: <rssi>,<ber> */
+        sscanf(line, "%*s%d,%d", csq[0], csq[1]);
+        err = ESP_OK;
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CBC
+ */
+static esp_err_t bg96_handle_cbc(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
+        /* store value of bcs, bcl, voltage */
+        uint32_t **cbc = bg96_dce->priv_resource;
+        /* +CBC: <bcs>,<bcl>,<voltage> */
+        sscanf(line, "%*s%d,%d,%d", cbc[0], cbc[1], cbc[2]);
+        err = ESP_OK;
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from +++
+ */
+static esp_err_t bg96_handle_exit_data_mode(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from ATD*99#
+ */
+static esp_err_t bg96_handle_atd_ppp(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_CONNECT)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CGMM
+ */
+static esp_err_t bg96_handle_cgmm(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->name, MODEM_MAX_NAME_LENGTH, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->name, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CGSN
+ */
+static esp_err_t bg96_handle_cgsn(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->imei, MODEM_IMEI_LENGTH + 1, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->imei, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CIMI
+ */
+static esp_err_t bg96_handle_cimi(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->imsi, MODEM_IMSI_LENGTH + 1, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->imsi, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+COPS?
+ */
+static esp_err_t bg96_handle_cops(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+COPS", strlen("+COPS"))) {
+        /* there might be some random spaces in operator's name, we can not use sscanf to parse the result */
+        /* strtok will break the string, we need to create a copy */
+        size_t len = strlen(line);
+        char *line_copy = malloc(len + 1);
+        strcpy(line_copy, line);
+        /* +COPS: <mode>[, <format>[, <oper>]] */
+        char *str_ptr = NULL;
+        char *p[3];
+        uint8_t i = 0;
+        /* strtok will broke string by replacing delimiter with '\0' */
+        p[i] = strtok_r(line_copy, ",", &str_ptr);
+        while (p[i]) {
+            p[++i] = strtok_r(NULL, ",", &str_ptr);
+        }
+        if (i >= 3) {
+            int len = snprintf(dce->oper, MODEM_MAX_OPERATOR_LENGTH, "%s", p[2]);
+            if (len > 2) {
+                /* Strip "\r\n" */
+                strip_cr_lf_tail(dce->oper, len);
+                err = ESP_OK;
+            }
+        }
+        free(line_copy);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+QPOWD=1
+ */
+static esp_err_t bg96_handle_power_down(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = ESP_OK;
+    } else if (strstr(line, MODEM_RESULT_CODE_POWERDOWN)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    }
+    return err;
+}
+
+/**
+ * @brief Get signal quality
+ *
+ * @param dce Modem DCE object
+ * @param rssi received signal strength indication
+ * @param ber bit error ratio
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_signal_quality(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber)
+{
+    modem_dte_t *dte = dce->dte;
+    bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
+    uint32_t *resource[2] = {rssi, ber};
+    bg96_dce->priv_resource = resource;
+    dce->handle_line = bg96_handle_csq;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CSQ\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire signal quality failed", err);
+    ESP_LOGD(DCE_TAG, "inquire signal quality ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get battery status
+ *
+ * @param dce Modem DCE object
+ * @param bcs Battery charge status
+ * @param bcl Battery connection level
+ * @param voltage Battery voltage
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
+{
+    modem_dte_t *dte = dce->dte;
+    bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
+    uint32_t *resource[3] = {bcs, bcl, voltage};
+    bg96_dce->priv_resource = resource;
+    dce->handle_line = bg96_handle_cbc;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire battery status failed", err);
+    ESP_LOGD(DCE_TAG, "inquire battery status ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Set Working Mode
+ *
+ * @param dce Modem DCE object
+ * @param mode woking mode
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
+{
+    modem_dte_t *dte = dce->dte;
+    switch (mode) {
+    case MODEM_COMMAND_MODE:
+        dce->handle_line = bg96_handle_exit_data_mode;
+        DCE_CHECK(dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter command mode failed", err);
+        ESP_LOGD(DCE_TAG, "enter command mode ok");
+        dce->mode = MODEM_COMMAND_MODE;
+        break;
+    case MODEM_PPP_MODE:
+        dce->handle_line = bg96_handle_atd_ppp;
+        DCE_CHECK(dte->send_cmd(dte, "ATD*99***1#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter ppp mode failed", err);
+        ESP_LOGD(DCE_TAG, "enter ppp mode ok");
+        dce->mode = MODEM_PPP_MODE;
+        break;
+    default:
+        ESP_LOGW(DCE_TAG, "unsupported working mode: %d", mode);
+        goto err;
+        break;
+    }
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Power down
+ *
+ * @param bg96_dce bg96 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_power_down(modem_dce_t *dce)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = bg96_handle_power_down;
+    DCE_CHECK(dte->send_cmd(dte, "AT+QPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "power down failed", err);
+    ESP_LOGD(DCE_TAG, "power down ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module name
+ *
+ * @param bg96_dce bg96 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_module_name(bg96_modem_dce_t *bg96_dce)
+{
+    modem_dte_t *dte = bg96_dce->parent.dte;
+    bg96_dce->parent.handle_line = bg96_handle_cgmm;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CGMM\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get module name failed", err);
+    ESP_LOGD(DCE_TAG, "get module name ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module IMEI number
+ *
+ * @param bg96_dce bg96 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_imei_number(bg96_modem_dce_t *bg96_dce)
+{
+    modem_dte_t *dte = bg96_dce->parent.dte;
+    bg96_dce->parent.handle_line = bg96_handle_cgsn;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CGSN\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get imei number failed", err);
+    ESP_LOGD(DCE_TAG, "get imei number ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module IMSI number
+ *
+ * @param bg96_dce bg96 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_imsi_number(bg96_modem_dce_t *bg96_dce)
+{
+    modem_dte_t *dte = bg96_dce->parent.dte;
+    bg96_dce->parent.handle_line = bg96_handle_cimi;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CIMI\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get imsi number failed", err);
+    ESP_LOGD(DCE_TAG, "get imsi number ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get Operator's name
+ *
+ * @param bg96_dce bg96 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t bg96_get_operator_name(bg96_modem_dce_t *bg96_dce)
+{
+    modem_dte_t *dte = bg96_dce->parent.dte;
+    bg96_dce->parent.handle_line = bg96_handle_cops;
+    DCE_CHECK(dte->send_cmd(dte, "AT+COPS?\r", MODEM_COMMAND_TIMEOUT_OPERATOR) == ESP_OK, "send command failed", err);
+    DCE_CHECK(bg96_dce->parent.state == MODEM_STATE_SUCCESS, "get network operator failed", err);
+    ESP_LOGD(DCE_TAG, "get network operator ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Deinitialize BG96 object
+ *
+ * @param dce Modem DCE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on fail
+ */
+static esp_err_t bg96_deinit(modem_dce_t *dce)
+{
+    bg96_modem_dce_t *bg96_dce = __containerof(dce, bg96_modem_dce_t, parent);
+    if (dce->dte) {
+        dce->dte->dce = NULL;
+    }
+    free(bg96_dce);
+    return ESP_OK;
+}
+
+modem_dce_t *bg96_init(modem_dte_t *dte)
+{
+    DCE_CHECK(dte, "DCE should bind with a DTE", err);
+    /* malloc memory for bg96_dce object */
+    bg96_modem_dce_t *bg96_dce = calloc(1, sizeof(bg96_modem_dce_t));
+    DCE_CHECK(bg96_dce, "calloc bg96_dce failed", err);
+    /* Bind DTE with DCE */
+    bg96_dce->parent.dte = dte;
+    dte->dce = &(bg96_dce->parent);
+    /* Bind methods */
+    bg96_dce->parent.handle_line = NULL;
+    bg96_dce->parent.sync = esp_modem_dce_sync;
+    bg96_dce->parent.echo_mode = esp_modem_dce_echo;
+    bg96_dce->parent.store_profile = esp_modem_dce_store_profile;
+    bg96_dce->parent.set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
+    bg96_dce->parent.define_pdp_context = esp_modem_dce_define_pdp_context;
+    bg96_dce->parent.hang_up = esp_modem_dce_hang_up;
+    bg96_dce->parent.get_signal_quality = bg96_get_signal_quality;
+    bg96_dce->parent.get_battery_status = bg96_get_battery_status;
+    bg96_dce->parent.set_working_mode = bg96_set_working_mode;
+    bg96_dce->parent.power_down = bg96_power_down;
+    bg96_dce->parent.deinit = bg96_deinit;
+    /* Sync between DTE and DCE */
+    DCE_CHECK(esp_modem_dce_sync(&(bg96_dce->parent)) == ESP_OK, "sync failed", err_io);
+    /* Close echo */
+    DCE_CHECK(esp_modem_dce_echo(&(bg96_dce->parent), false) == ESP_OK, "close echo mode failed", err_io);
+    /* Get Module name */
+    DCE_CHECK(bg96_get_module_name(bg96_dce) == ESP_OK, "get module name failed", err_io);
+    /* Get IMEI number */
+    DCE_CHECK(bg96_get_imei_number(bg96_dce) == ESP_OK, "get imei failed", err_io);
+    /* Get IMSI number */
+    DCE_CHECK(bg96_get_imsi_number(bg96_dce) == ESP_OK, "get imsi failed", err_io);
+    /* Get operator name */
+    DCE_CHECK(bg96_get_operator_name(bg96_dce) == ESP_OK, "get operator name failed", err_io);
+    return &(bg96_dce->parent);
+err_io:
+    free(bg96_dce);
+err:
+    return NULL;
+}
diff --git a/examples/protocols/pppos_client/components/modem/src/esp_modem.c b/examples/protocols/pppos_client/components/modem/src/esp_modem.c
new file mode 100644 (file)
index 0000000..9a6fc8a
--- /dev/null
@@ -0,0 +1,620 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "netif/ppp/pppapi.h"
+#include "netif/ppp/pppos.h"
+#include "lwip/dns.h"
+#include "tcpip_adapter.h"
+#include "esp_modem.h"
+#include "esp_log.h"
+#include "sdkconfig.h"
+
+#define ESP_MODEM_LINE_BUFFER_SIZE (CONFIG_UART_RX_BUFFER_SIZE / 2)
+#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
+
+#define MIN_PATTERN_INTERVAL (10000)
+#define MIN_POST_IDLE (10)
+#define MIN_PRE_IDLE (10)
+
+/**
+ * @brief Macro defined for error checking
+ *
+ */
+static const char *MODEM_TAG = "esp-modem";
+#define MODEM_CHECK(a, str, goto_tag, ...)                                              \
+    do                                                                                  \
+    {                                                                                   \
+        if (!(a))                                                                       \
+        {                                                                               \
+            ESP_LOGE(MODEM_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+            goto goto_tag;                                                              \
+        }                                                                               \
+    } while (0)
+
+ESP_EVENT_DEFINE_BASE(ESP_MODEM_EVENT);
+
+/**
+ * @brief ESP32 Modem DTE
+ *
+ */
+typedef struct {
+    uart_port_t uart_port;                  /*!< UART port */
+    uint8_t *buffer;                        /*!< Internal buffer to store response lines/data from DCE */
+    QueueHandle_t event_queue;              /*!< UART event queue handle */
+    esp_event_loop_handle_t event_loop_hdl; /*!< Event loop handle */
+    TaskHandle_t uart_event_task_hdl;       /*!< UART event task handle */
+    SemaphoreHandle_t process_sem;          /*!< Semaphore used for indicating processing status */
+    struct netif pppif;                     /*!< PPP network interface */
+    ppp_pcb *ppp;                           /*!< PPP control block */
+    modem_dte_t parent;                     /*!< DTE interface that should extend */
+} esp_modem_dte_t;
+
+/**
+ * @brief Handle one line in DTE
+ *
+ * @param esp_dte ESP modem DTE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t esp_dte_handle_line(esp_modem_dte_t *esp_dte)
+{
+    modem_dce_t *dce = esp_dte->parent.dce;
+    MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
+    const char *line = (const char *)(esp_dte->buffer);
+    /* Skip pure "\r\n" lines */
+    if (strlen(line) > 2) {
+        MODEM_CHECK(dce->handle_line, "no handler for line", err_handle);
+        MODEM_CHECK(dce->handle_line(dce, line) == ESP_OK, "handle line failed", err_handle);
+    }
+    return ESP_OK;
+err_handle:
+    /* Send MODEM_EVENT_UNKNOWN signal to event loop */
+    esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, MODEM_EVENT_UNKNOWN,
+                      (void *)line, strlen(line) + 1, pdMS_TO_TICKS(100));
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Handle when a pattern has been detected by UART
+ *
+ * @param esp_dte ESP32 Modem DTE object
+ */
+static void esp_handle_uart_pattern(esp_modem_dte_t *esp_dte)
+{
+    int pos = uart_pattern_pop_pos(esp_dte->uart_port);
+    int read_len = 0;
+    if (pos != -1) {
+        if (pos < ESP_MODEM_LINE_BUFFER_SIZE - 1) {
+            /* read one line(include '\n') */
+            read_len = pos + 1;
+        } else {
+            ESP_LOGW(MODEM_TAG, "ESP Modem Line buffer too small");
+            read_len = ESP_MODEM_LINE_BUFFER_SIZE - 1;
+        }
+        read_len = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, read_len, pdMS_TO_TICKS(100));
+        if (read_len) {
+            /* make sure the line is a standard string */
+            esp_dte->buffer[read_len] = '\0';
+            /* Send new line to handle */
+            esp_dte_handle_line(esp_dte);
+        } else {
+            ESP_LOGE(MODEM_TAG, "uart read bytes failed");
+        }
+    } else {
+        ESP_LOGW(MODEM_TAG, "Pattern Queue Size too small");
+        uart_flush(esp_dte->uart_port);
+    }
+}
+
+/**
+ * @brief Handle when new data received by UART
+ *
+ * @param esp_dte ESP32 Modem DTE object
+ */
+static void esp_handle_uart_data(esp_modem_dte_t *esp_dte)
+{
+    size_t length = 0;
+    uart_get_buffered_data_len(esp_dte->uart_port, &length);
+    length = MIN(ESP_MODEM_LINE_BUFFER_SIZE, length);
+    length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
+    /* pass input data to the lwIP core thread */
+    if (length) {
+        pppos_input_tcpip(esp_dte->ppp, esp_dte->buffer, length);
+    }
+}
+
+/**
+ * @brief UART Event Task Entry
+ *
+ * @param param task parameter
+ */
+static void uart_event_task_entry(void *param)
+{
+    esp_modem_dte_t *esp_dte = (esp_modem_dte_t *)param;
+    uart_event_t event;
+    while (1) {
+        if (xQueueReceive(esp_dte->event_queue, &event, pdMS_TO_TICKS(100))) {
+            switch (event.type) {
+            case UART_DATA:
+                esp_handle_uart_data(esp_dte);
+                break;
+            case UART_FIFO_OVF:
+                ESP_LOGW(MODEM_TAG, "HW FIFO Overflow");
+                uart_flush_input(esp_dte->uart_port);
+                xQueueReset(esp_dte->event_queue);
+                break;
+            case UART_BUFFER_FULL:
+                ESP_LOGW(MODEM_TAG, "Ring Buffer Full");
+                uart_flush_input(esp_dte->uart_port);
+                xQueueReset(esp_dte->event_queue);
+                break;
+            case UART_BREAK:
+                ESP_LOGW(MODEM_TAG, "Rx Break");
+                break;
+            case UART_PARITY_ERR:
+                ESP_LOGE(MODEM_TAG, "Parity Error");
+                break;
+            case UART_FRAME_ERR:
+                ESP_LOGE(MODEM_TAG, "Frame Error");
+                break;
+            case UART_PATTERN_DET:
+                esp_handle_uart_pattern(esp_dte);
+                break;
+            default:
+                ESP_LOGW(MODEM_TAG, "unknown uart event type: %d", event.type);
+                break;
+            }
+        }
+        /* Drive the event loop */
+        esp_event_loop_run(esp_dte->event_loop_hdl, pdMS_TO_TICKS(50));
+    }
+    vTaskDelete(NULL);
+}
+
+/**
+ * @brief Send command to DCE
+ *
+ * @param dte Modem DTE object
+ * @param command command string
+ * @param timeout timeout value, unit: ms
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t esp_modem_dte_send_cmd(modem_dte_t *dte, const char *command, uint32_t timeout)
+{
+    esp_err_t ret = ESP_FAIL;
+    modem_dce_t *dce = dte->dce;
+    MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
+    MODEM_CHECK(command, "command is NULL", err);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    /* Calculate timeout clock tick */
+    /* Reset runtime information */
+    dce->state = MODEM_STATE_PROCESSING;
+    /* Send command via UART */
+    uart_write_bytes(esp_dte->uart_port, command, strlen(command));
+    /* Check timeout */
+    MODEM_CHECK(xSemaphoreTake(esp_dte->process_sem, pdMS_TO_TICKS(timeout)) == pdTRUE, "process command timeout", err);
+    ret = ESP_OK;
+err:
+    dce->handle_line = NULL;
+    return ret;
+}
+
+/**
+ * @brief Send data to DCE
+ *
+ * @param dte Modem DTE object
+ * @param data data buffer
+ * @param length length of data to send
+ * @return int actual length of data that has been send out
+ */
+static int esp_modem_dte_send_data(modem_dte_t *dte, const char *data, uint32_t length)
+{
+    MODEM_CHECK(data, "data is NULL", err);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    return uart_write_bytes(esp_dte->uart_port, data, length);
+err:
+    return -1;
+}
+
+/**
+ * @brief Send data and wait for prompt from DCE
+ *
+ * @param dte Modem DTE object
+ * @param data data buffer
+ * @param length length of data to send
+ * @param prompt pointer of specific prompt
+ * @param timeout timeout value (unit: ms)
+ * @return esp_err_t
+ *      ESP_OK on success
+ *      ESP_FAIL on error
+ */
+static esp_err_t esp_modem_dte_send_wait(modem_dte_t *dte, const char *data, uint32_t length,
+        const char *prompt, uint32_t timeout)
+{
+    MODEM_CHECK(data, "data is NULL", err_param);
+    MODEM_CHECK(prompt, "prompt is NULL", err_param);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    // We'd better disable pattern detection here for a moment in case prompt string contains the pattern character
+    uart_disable_pattern_det_intr(esp_dte->uart_port);
+    // uart_disable_rx_intr(esp_dte->uart_port);
+    MODEM_CHECK(uart_write_bytes(esp_dte->uart_port, data, length) >= 0, "uart write bytes failed", err_write);
+    uint32_t len = strlen(prompt);
+    uint8_t *buffer = calloc(len + 1, sizeof(uint8_t));
+    int res = uart_read_bytes(esp_dte->uart_port, buffer, len, pdMS_TO_TICKS(timeout));
+    MODEM_CHECK(res >= len, "wait prompt [%s] timeout", err, prompt);
+    MODEM_CHECK(!strncmp(prompt, (const char *)buffer, len), "get wrong prompt: %s", err, buffer);
+    free(buffer);
+    uart_enable_pattern_det_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
+    return ESP_OK;
+err:
+    free(buffer);
+err_write:
+    uart_enable_pattern_det_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
+err_param:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Change Modem's working mode
+ *
+ * @param dte Modem DTE object
+ * @param new_mode new working mode
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t esp_modem_dte_change_mode(modem_dte_t *dte, modem_mode_t new_mode)
+{
+    modem_dce_t *dce = dte->dce;
+    MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    MODEM_CHECK(dce->mode != new_mode, "already in mode: %d", err, new_mode);
+    switch (new_mode) {
+    case MODEM_PPP_MODE:
+        MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err, new_mode);
+        uart_disable_pattern_det_intr(esp_dte->uart_port);
+        uart_enable_rx_intr(esp_dte->uart_port);
+        break;
+    case MODEM_COMMAND_MODE:
+        uart_disable_rx_intr(esp_dte->uart_port);
+        uart_flush(esp_dte->uart_port);
+        uart_enable_pattern_det_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
+        uart_pattern_queue_reset(esp_dte->uart_port, CONFIG_UART_PATTERN_QUEUE_SIZE);
+        MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err, new_mode);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+static esp_err_t esp_modem_dte_process_cmd_done(modem_dte_t *dte)
+{
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    return xSemaphoreGive(esp_dte->process_sem) == pdTRUE ? ESP_OK : ESP_FAIL;
+}
+
+/**
+ * @brief Deinitialize a Modem DTE object
+ *
+ * @param dte Modem DTE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t esp_modem_dte_deinit(modem_dte_t *dte)
+{
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    /* Delete UART event task */
+    vTaskDelete(esp_dte->uart_event_task_hdl);
+    /* Delete semaphore */
+    vSemaphoreDelete(esp_dte->process_sem);
+    /* Delete event loop */
+    esp_event_loop_delete(esp_dte->event_loop_hdl);
+    /* Uninstall UART Driver */
+    uart_driver_delete(esp_dte->uart_port);
+    /* Free memory */
+    free(esp_dte->buffer);
+    if (dte->dce) {
+        dte->dce->dte = NULL;
+    }
+    free(esp_dte);
+    return ESP_OK;
+}
+
+modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
+{
+    esp_err_t res;
+    /* malloc memory for esp_dte object */
+    esp_modem_dte_t *esp_dte = calloc(1, sizeof(esp_modem_dte_t));
+    MODEM_CHECK(esp_dte, "calloc esp_dte failed", err_dte_mem);
+    /* malloc memory to storing lines from modem dce */
+    esp_dte->buffer = calloc(1, ESP_MODEM_LINE_BUFFER_SIZE);
+    MODEM_CHECK(esp_dte->buffer, "calloc line memory failed", err_line_mem);
+    /* Set attributes */
+    esp_dte->uart_port = config->port_num;
+    esp_dte->parent.flow_ctrl = config->flow_control;
+    /* Bind methods */
+    esp_dte->parent.send_cmd = esp_modem_dte_send_cmd;
+    esp_dte->parent.send_data = esp_modem_dte_send_data;
+    esp_dte->parent.send_wait = esp_modem_dte_send_wait;
+    esp_dte->parent.change_mode = esp_modem_dte_change_mode;
+    esp_dte->parent.process_cmd_done = esp_modem_dte_process_cmd_done;
+    esp_dte->parent.deinit = esp_modem_dte_deinit;
+    /* Config UART */
+    uart_config_t uart_config = {
+        .baud_rate = config->baud_rate,
+        .data_bits = config->data_bits,
+        .parity = config->parity,
+        .stop_bits = config->stop_bits,
+        .flow_ctrl = (config->flow_control == MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS : UART_HW_FLOWCTRL_DISABLE
+    };
+    MODEM_CHECK(uart_param_config(esp_dte->uart_port, &uart_config) == ESP_OK, "config uart parameter failed", err_uart_config);
+    if (config->flow_control == MODEM_FLOW_CONTROL_HW) {
+        res = uart_set_pin(esp_dte->uart_port, CONFIG_MODEM_TX_PIN, CONFIG_MODEM_RX_PIN,
+                           CONFIG_MODEM_RTS_PIN, CONFIG_MODEM_CTS_PIN);
+    } else {
+        res = uart_set_pin(esp_dte->uart_port, CONFIG_MODEM_TX_PIN, CONFIG_MODEM_RX_PIN,
+                           UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
+    }
+    MODEM_CHECK(res == ESP_OK, "config uart gpio failed", err_uart_config);
+    /* Set flow control threshold */
+    if (config->flow_control == MODEM_FLOW_CONTROL_HW) {
+        res = uart_set_hw_flow_ctrl(esp_dte->uart_port, UART_HW_FLOWCTRL_CTS_RTS, UART_FIFO_LEN - 8);
+    } else if (config->flow_control == MODEM_FLOW_CONTROL_SW) {
+        res = uart_set_sw_flow_ctrl(esp_dte->uart_port, true, 8, UART_FIFO_LEN - 8);
+    }
+    MODEM_CHECK(res == ESP_OK, "config uart flow control failed", err_uart_config);
+    /* Install UART driver and get event queue used inside driver */
+    res = uart_driver_install(esp_dte->uart_port, CONFIG_UART_RX_BUFFER_SIZE, CONFIG_UART_TX_BUFFER_SIZE,
+                              CONFIG_UART_EVENT_QUEUE_SIZE, &(esp_dte->event_queue), 0);
+    MODEM_CHECK(res == ESP_OK, "install uart driver failed", err_uart_config);
+    /* Set pattern interrupt, used to detect the end of a line. */
+    res = uart_enable_pattern_det_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
+    /* Set pattern queue size */
+    res |= uart_pattern_queue_reset(esp_dte->uart_port, CONFIG_UART_PATTERN_QUEUE_SIZE);
+    MODEM_CHECK(res == ESP_OK, "config uart pattern failed", err_uart_pattern);
+    /* Create Event loop */
+    esp_event_loop_args_t loop_args = {
+        .queue_size = ESP_MODEM_EVENT_QUEUE_SIZE,
+        .task_name = NULL
+    };
+    MODEM_CHECK(esp_event_loop_create(&loop_args, &esp_dte->event_loop_hdl) == ESP_OK, "create event loop failed", err_eloop);
+    /* Create semaphore */
+    esp_dte->process_sem = xSemaphoreCreateBinary();
+    MODEM_CHECK(esp_dte->process_sem, "create process semaphore failed", err_sem);
+    /* Create UART Event task */
+    BaseType_t ret = xTaskCreate(uart_event_task_entry,             //Task Entry
+                                 "uart_event",                      //Task Name
+                                 CONFIG_UART_EVENT_TASK_STACK_SIZE, //Task Stack Size(Bytes)
+                                 esp_dte,                           //Task Parameter
+                                 CONFIG_UART_EVENT_TASK_PRIORITY,   //Task Priority
+                                 & (esp_dte->uart_event_task_hdl)   //Task Handler
+                                );
+    MODEM_CHECK(ret == pdTRUE, "create uart event task failed", err_tsk_create);
+    return &(esp_dte->parent);
+    /* Error handling */
+err_tsk_create:
+    vSemaphoreDelete(esp_dte->process_sem);
+err_sem:
+    esp_event_loop_delete(esp_dte->event_loop_hdl);
+err_eloop:
+    uart_disable_pattern_det_intr(esp_dte->uart_port);
+err_uart_pattern:
+    uart_driver_delete(esp_dte->uart_port);
+err_uart_config:
+    free(esp_dte->buffer);
+err_line_mem:
+    free(esp_dte);
+err_dte_mem:
+    return NULL;
+}
+
+esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args)
+{
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    return esp_event_handler_register_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_EVENT_ANY_ID, handler, handler_args);
+}
+
+esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t handler)
+{
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    return esp_event_handler_unregister_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_EVENT_ANY_ID, handler);
+}
+
+/**
+ * @brief PPP status callback which is called on PPP status change (up, down, â€¦) by lwIP core thread
+ *
+ * @param pcb PPP control block
+ * @param err_code Error code
+ * @param ctx Context of callback
+ */
+static void on_ppp_status_changed(ppp_pcb *pcb, int err_code, void *ctx)
+{
+    struct netif *pppif = ppp_netif(pcb);
+    modem_dte_t *dte = (modem_dte_t *)(ctx);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    ppp_client_ip_info_t ipinfo = {0};
+    switch (err_code) {
+    case PPPERR_NONE: /* Connected */
+        ipinfo.ip = pppif->ip_addr.u_addr.ip4;
+        ipinfo.gw = pppif->gw.u_addr.ip4;
+        ipinfo.netmask = pppif->netmask.u_addr.ip4;
+        ipinfo.ns1 = dns_getserver(0).u_addr.ip4;
+        ipinfo.ns2 = dns_getserver(1).u_addr.ip4;
+        esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, MODEM_EVENT_PPP_CONNECT, &ipinfo, sizeof(ipinfo), 0);
+        break;
+    case PPPERR_PARAM:
+        ESP_LOGE(MODEM_TAG, "Invalid parameter");
+        break;
+    case PPPERR_OPEN:
+        ESP_LOGE(MODEM_TAG, "Unable to open PPP session");
+        break;
+    case PPPERR_DEVICE:
+        ESP_LOGE(MODEM_TAG, "Invalid I/O device for PPP");
+        break;
+    case PPPERR_ALLOC:
+        ESP_LOGE(MODEM_TAG, "Unable to allocate resources");
+        break;
+    case PPPERR_USER: /* User interrupt */
+        esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, MODEM_EVENT_PPP_STOP, NULL, 0, 0);
+        /* Free the PPP control block */
+        pppapi_free(esp_dte->ppp);
+        break;
+    case PPPERR_CONNECT: /* Connection lost */
+        esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, MODEM_EVENT_PPP_DISCONNECT, NULL, 0, 0);
+        break;
+    case PPPERR_AUTHFAIL:
+        ESP_LOGE(MODEM_TAG, "Failed authentication challenge");
+        break;
+    case PPPERR_PROTOCOL:
+        ESP_LOGE(MODEM_TAG, "Failed to meet protocol");
+        break;
+    case PPPERR_PEERDEAD:
+        ESP_LOGE(MODEM_TAG, "Connection timeout");
+        break;
+    case PPPERR_IDLETIMEOUT:
+        ESP_LOGE(MODEM_TAG, "Idle Timeout");
+        break;
+    case PPPERR_CONNECTTIME:
+        ESP_LOGE(MODEM_TAG, "Max connect time reached");
+        break;
+    case PPPERR_LOOPBACK:
+        ESP_LOGE(MODEM_TAG, "Loopback detected");
+        break;
+    default:
+        ESP_LOGE(MODEM_TAG, "Unknown error code %d", err_code);
+        break;
+    }
+}
+
+#if PPP_NOTIFY_PHASE
+/**
+ * @brief Notify phase callback which is called on each PPP internal state change
+ *
+ * @param pcb PPP control block
+ * @param phase Phase ID
+ * @param ctx Context of callback
+ */
+static void on_ppp_notify_phase(ppp_pcb *pcb, u8_t phase, void *ctx)
+{
+    switch (phase) {
+    case PPP_PHASE_DEAD:
+        ESP_LOGD(MODEM_TAG, "Phase Dead");
+        break;
+    case PPP_PHASE_INITIALIZE:
+        ESP_LOGD(MODEM_TAG, "Phase Start");
+        break;
+    case PPP_PHASE_ESTABLISH:
+        ESP_LOGD(MODEM_TAG, "Phase Establish");
+        break;
+    case PPP_PHASE_AUTHENTICATE:
+        ESP_LOGD(MODEM_TAG, "Phase Authenticate");
+        break;
+    case PPP_PHASE_NETWORK:
+        ESP_LOGD(MODEM_TAG, "Phase Network");
+        break;
+    case PPP_PHASE_RUNNING:
+        ESP_LOGD(MODEM_TAG, "Phase Running");
+        break;
+    case PPP_PHASE_TERMINATE:
+        ESP_LOGD(MODEM_TAG, "Phase Terminate");
+        break;
+    case PPP_PHASE_DISCONNECT:
+        ESP_LOGD(MODEM_TAG, "Phase Disconnect");
+        break;
+    default:
+        ESP_LOGW(MODEM_TAG, "Phase Unknown: %d", phase);
+        break;
+    }
+}
+#endif
+
+/**
+ * @brief PPPoS serial output callback
+ *
+ * @param pcb PPP control block
+ * @param data Buffer to write to serial port
+ * @param len Length of the data buffer
+ * @param ctx Context of callback
+ * @return uint32_t Length of data successfully sent
+ */
+static uint32_t pppos_low_level_output(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ctx)
+{
+    modem_dte_t *dte = (modem_dte_t *)ctx;
+    return dte->send_data(dte, (const char *)data, len);
+}
+
+esp_err_t esp_modem_setup_ppp(modem_dte_t *dte)
+{
+    modem_dce_t *dce = dte->dce;
+    MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    /* Set PDP Context */
+    MODEM_CHECK(dce->define_pdp_context(dce, 1, "IP", CONFIG_ESP_MODEM_APN) == ESP_OK, "set MODEM APN failed", err);
+    /* Enter PPP mode */
+    MODEM_CHECK(dte->change_mode(dte, MODEM_PPP_MODE) == ESP_OK, "enter ppp mode failed", err);
+    /* Create PPPoS interface */
+    esp_dte->ppp = pppapi_pppos_create(&(esp_dte->pppif), pppos_low_level_output, on_ppp_status_changed, dte);
+    MODEM_CHECK(esp_dte->ppp, "create pppos interface failed", err);
+#if PPP_NOTIFY_PHASE
+    ppp_set_notify_phase_callback(esp_dte->ppp, on_ppp_notify_phase);
+#endif
+    /* Initiate PPP client connection */
+    /* Set default route */
+    MODEM_CHECK(pppapi_set_default(esp_dte->ppp) == ERR_OK, "set default route failed", err);
+    /* Ask the peer for up to 2 DNS server addresses */
+    ppp_set_usepeerdns(esp_dte->ppp, 1);
+    /* Auth configuration */
+#if PAP_SUPPORT
+    pppapi_set_auth(esp_dte->ppp, PPPAUTHTYPE_PAP, CONFIG_ESP_MODEM_PPP_AUTH_USERNAME, CONFIG_ESP_MODEM_PPP_AUTH_PASSWORD);
+#elif CHAP_SUPPORT
+    pppapi_set_auth(esp_dte->ppp, PPPAUTHTYPE_CHAP, CONFIG_ESP_MODEM_PPP_AUTH_USERNAME, CONFIG_ESP_MODEM_PPP_AUTH_PASSWORD);
+#else
+#error "Unsupported AUTH Negotiation"
+#endif
+    /* Initiate PPP negotiation, without waiting */
+    MODEM_CHECK(pppapi_connect(esp_dte->ppp, 0) == ERR_OK, "initiate ppp negotiation failed", err);
+    esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, MODEM_EVENT_PPP_START, NULL, 0, 0);
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_exit_ppp(modem_dte_t *dte)
+{
+    modem_dce_t *dce = dte->dce;
+    MODEM_CHECK(dce, "DTE has not yet bind with DCE", err);
+    esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
+    /* Shutdown of PPP protocols */
+    MODEM_CHECK(pppapi_close(esp_dte->ppp, 0) == ERR_OK, "close ppp connection failed", err);
+    /* Enter command mode */
+    MODEM_CHECK(dte->change_mode(dte, MODEM_COMMAND_MODE) == ESP_OK, "enter command mode failed", err);
+    /* Hang up */
+    MODEM_CHECK(dce->hang_up(dce) == ESP_OK, "hang up failed", err);
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
diff --git a/examples/protocols/pppos_client/components/modem/src/esp_modem_dce_service.c b/examples/protocols/pppos_client/components/modem/src/esp_modem_dce_service.c
new file mode 100644 (file)
index 0000000..ad59a8b
--- /dev/null
@@ -0,0 +1,126 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <string.h>
+#include "esp_log.h"
+#include "esp_modem_dce_service.h"
+
+/**
+ * @brief Macro defined for error checking
+ *
+ */
+static const char *DCE_TAG = "dce_service";
+#define DCE_CHECK(a, str, goto_tag, ...)                                              \
+    do                                                                                \
+    {                                                                                 \
+        if (!(a))                                                                     \
+        {                                                                             \
+            ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+            goto goto_tag;                                                            \
+        }                                                                             \
+    } while (0)
+
+esp_err_t esp_modem_dce_handle_response_default(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    }
+    return err;
+}
+
+esp_err_t esp_modem_dce_sync(modem_dce_t *dce)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    DCE_CHECK(dte->send_cmd(dte, "AT\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "sync failed", err);
+    ESP_LOGD(DCE_TAG, "sync ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_dce_echo(modem_dce_t *dce, bool on)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    if (on) {
+        DCE_CHECK(dte->send_cmd(dte, "ATE1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enable echo failed", err);
+        ESP_LOGD(DCE_TAG, "enable echo ok");
+    } else {
+        DCE_CHECK(dte->send_cmd(dte, "ATE0\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "disable echo failed", err);
+        ESP_LOGD(DCE_TAG, "disable echo ok");
+    }
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_dce_store_profile(modem_dce_t *dce)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    DCE_CHECK(dte->send_cmd(dte, "AT&W\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "save settings failed", err);
+    ESP_LOGD(DCE_TAG, "save settings ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_dce_set_flow_ctrl(modem_dce_t *dce, modem_flow_ctrl_t flow_ctrl)
+{
+    modem_dte_t *dte = dce->dte;
+    char command[16];
+    int len = snprintf(command, sizeof(command), "AT+IFC=%d,%d\r", dte->flow_ctrl, flow_ctrl);
+    DCE_CHECK(len < sizeof(command), "command too long: %s", err, command);
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    DCE_CHECK(dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "set flow control failed", err);
+    ESP_LOGD(DCE_TAG, "set flow control ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_dce_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn)
+{
+    modem_dte_t *dte = dce->dte;
+    char command[32];
+    int len = snprintf(command, sizeof(command), "AT+CGDCONT=%d,\"%s\",\"%s\"\r", cid, type, apn);
+    DCE_CHECK(len < sizeof(command), "command too long: %s", err, command);
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    DCE_CHECK(dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "define pdp context failed", err);
+    ESP_LOGD(DCE_TAG, "define pdp context ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+esp_err_t esp_modem_dce_hang_up(modem_dce_t *dce)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = esp_modem_dce_handle_response_default;
+    DCE_CHECK(dte->send_cmd(dte, "ATH\r", MODEM_COMMAND_TIMEOUT_HANG_UP) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "hang up failed", err);
+    ESP_LOGD(DCE_TAG, "hang up ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
diff --git a/examples/protocols/pppos_client/components/modem/src/sim800.c b/examples/protocols/pppos_client/components/modem/src/sim800.c
new file mode 100644 (file)
index 0000000..05151b7
--- /dev/null
@@ -0,0 +1,476 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stdlib.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_modem_dce_service.h"
+#include "sim800.h"
+
+#define MODEM_RESULT_CODE_POWERDOWN "POWER DOWN"
+
+/**
+ * @brief Macro defined for error checking
+ *
+ */
+static const char *DCE_TAG = "sim800";
+#define DCE_CHECK(a, str, goto_tag, ...)                                              \
+    do                                                                                \
+    {                                                                                 \
+        if (!(a))                                                                     \
+        {                                                                             \
+            ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+            goto goto_tag;                                                            \
+        }                                                                             \
+    } while (0)
+
+/**
+ * @brief SIM800 Modem
+ *
+ */
+typedef struct {
+    void *priv_resource; /*!< Private resource */
+    modem_dce_t parent;  /*!< DCE parent class */
+} sim800_modem_dce_t;
+
+/**
+ * @brief Handle response from AT+CSQ
+ */
+static esp_err_t sim800_handle_csq(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+CSQ", strlen("+CSQ"))) {
+        /* store value of rssi and ber */
+        uint32_t **csq = sim800_dce->priv_resource;
+        /* +CSQ: <rssi>,<ber> */
+        sscanf(line, "%*s%d,%d", csq[0], csq[1]);
+        err = ESP_OK;
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CBC
+ */
+static esp_err_t sim800_handle_cbc(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
+        /* store value of bcs, bcl, voltage */
+        uint32_t **cbc = sim800_dce->priv_resource;
+        /* +CBC: <bcs>,<bcl>,<voltage> */
+        sscanf(line, "%*s%d,%d,%d", cbc[0], cbc[1], cbc[2]);
+        err = ESP_OK;
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from +++
+ */
+static esp_err_t sim800_handle_exit_data_mode(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from ATD*99#
+ */
+static esp_err_t sim800_handle_atd_ppp(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_CONNECT)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CGMM
+ */
+static esp_err_t sim800_handle_cgmm(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->name, MODEM_MAX_NAME_LENGTH, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->name, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CGSN
+ */
+static esp_err_t sim800_handle_cgsn(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->imei, MODEM_IMEI_LENGTH + 1, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->imei, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CIMI
+ */
+static esp_err_t sim800_handle_cimi(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else {
+        int len = snprintf(dce->imsi, MODEM_IMSI_LENGTH + 1, "%s", line);
+        if (len > 2) {
+            /* Strip "\r\n" */
+            strip_cr_lf_tail(dce->imsi, len);
+            err = ESP_OK;
+        }
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+COPS?
+ */
+static esp_err_t sim800_handle_cops(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+COPS", strlen("+COPS"))) {
+        /* there might be some random spaces in operator's name, we can not use sscanf to parse the result */
+        /* strtok will break the string, we need to create a copy */
+        size_t len = strlen(line);
+        char *line_copy = malloc(len + 1);
+        strcpy(line_copy, line);
+        /* +COPS: <mode>[, <format>[, <oper>]] */
+        char *str_ptr = NULL;
+        char *p[3];
+        uint8_t i = 0;
+        /* strtok will broke string by replacing delimiter with '\0' */
+        p[i] = strtok_r(line_copy, ",", &str_ptr);
+        while (p[i]) {
+            p[++i] = strtok_r(NULL, ",", &str_ptr);
+        }
+        if (i >= 3) {
+            int len = snprintf(dce->oper, MODEM_MAX_OPERATOR_LENGTH, "%s", p[2]);
+            if (len > 2) {
+                /* Strip "\r\n" */
+                strip_cr_lf_tail(dce->oper, len);
+                err = ESP_OK;
+            }
+        }
+        free(line_copy);
+    }
+    return err;
+}
+
+/**
+ * @brief Handle response from AT+CPOWD=1
+ */
+static esp_err_t sim800_handle_power_down(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_POWERDOWN)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    }
+    return err;
+}
+
+/**
+ * @brief Get signal quality
+ *
+ * @param dce Modem DCE object
+ * @param rssi received signal strength indication
+ * @param ber bit error ratio
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_signal_quality(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber)
+{
+    modem_dte_t *dte = dce->dte;
+    sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
+    uint32_t *resource[2] = {rssi, ber};
+    sim800_dce->priv_resource = resource;
+    dce->handle_line = sim800_handle_csq;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CSQ\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire signal quality failed", err);
+    ESP_LOGD(DCE_TAG, "inquire signal quality ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get battery status
+ *
+ * @param dce Modem DCE object
+ * @param bcs Battery charge status
+ * @param bcl Battery connection level
+ * @param voltage Battery voltage
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
+{
+    modem_dte_t *dte = dce->dte;
+    sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
+    uint32_t *resource[3] = {bcs, bcl, voltage};
+    sim800_dce->priv_resource = resource;
+    dce->handle_line = sim800_handle_cbc;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "inquire battery status failed", err);
+    ESP_LOGD(DCE_TAG, "inquire battery status ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Set Working Mode
+ *
+ * @param dce Modem DCE object
+ * @param mode woking mode
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
+{
+    modem_dte_t *dte = dce->dte;
+    switch (mode) {
+    case MODEM_COMMAND_MODE:
+        dce->handle_line = sim800_handle_exit_data_mode;
+        DCE_CHECK(dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter command mode failed", err);
+        ESP_LOGD(DCE_TAG, "enter command mode ok");
+        dce->mode = MODEM_COMMAND_MODE;
+        break;
+    case MODEM_PPP_MODE:
+        dce->handle_line = sim800_handle_atd_ppp;
+        DCE_CHECK(dte->send_cmd(dte, "ATD*99#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
+        DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter ppp mode failed", err);
+        ESP_LOGD(DCE_TAG, "enter ppp mode ok");
+        dce->mode = MODEM_PPP_MODE;
+        break;
+    default:
+        ESP_LOGW(DCE_TAG, "unsupported working mode: %d", mode);
+        goto err;
+        break;
+    }
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Power down
+ *
+ * @param sim800_dce sim800 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_power_down(modem_dce_t *dce)
+{
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = sim800_handle_power_down;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF) == ESP_OK, "send command failed", err);
+    DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "power down failed", err);
+    ESP_LOGD(DCE_TAG, "power down ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module name
+ *
+ * @param sim800_dce sim800 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_module_name(sim800_modem_dce_t *sim800_dce)
+{
+    modem_dte_t *dte = sim800_dce->parent.dte;
+    sim800_dce->parent.handle_line = sim800_handle_cgmm;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CGMM\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get module name failed", err);
+    ESP_LOGD(DCE_TAG, "get module name ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module IMEI number
+ *
+ * @param sim800_dce sim800 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_imei_number(sim800_modem_dce_t *sim800_dce)
+{
+    modem_dte_t *dte = sim800_dce->parent.dte;
+    sim800_dce->parent.handle_line = sim800_handle_cgsn;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CGSN\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get imei number failed", err);
+    ESP_LOGD(DCE_TAG, "get imei number ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get DCE module IMSI number
+ *
+ * @param sim800_dce sim800 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_imsi_number(sim800_modem_dce_t *sim800_dce)
+{
+    modem_dte_t *dte = sim800_dce->parent.dte;
+    sim800_dce->parent.handle_line = sim800_handle_cimi;
+    DCE_CHECK(dte->send_cmd(dte, "AT+CIMI\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
+    DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get imsi number failed", err);
+    ESP_LOGD(DCE_TAG, "get imsi number ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Get Operator's name
+ *
+ * @param sim800_dce sim800 object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on error
+ */
+static esp_err_t sim800_get_operator_name(sim800_modem_dce_t *sim800_dce)
+{
+    modem_dte_t *dte = sim800_dce->parent.dte;
+    sim800_dce->parent.handle_line = sim800_handle_cops;
+    DCE_CHECK(dte->send_cmd(dte, "AT+COPS?\r", MODEM_COMMAND_TIMEOUT_OPERATOR) == ESP_OK, "send command failed", err);
+    DCE_CHECK(sim800_dce->parent.state == MODEM_STATE_SUCCESS, "get network operator failed", err);
+    ESP_LOGD(DCE_TAG, "get network operator ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+
+/**
+ * @brief Deinitialize SIM800 object
+ *
+ * @param dce Modem DCE object
+ * @return esp_err_t
+ *      - ESP_OK on success
+ *      - ESP_FAIL on fail
+ */
+static esp_err_t sim800_deinit(modem_dce_t *dce)
+{
+    sim800_modem_dce_t *sim800_dce = __containerof(dce, sim800_modem_dce_t, parent);
+    if (dce->dte) {
+        dce->dte->dce = NULL;
+    }
+    free(sim800_dce);
+    return ESP_OK;
+}
+
+modem_dce_t *sim800_init(modem_dte_t *dte)
+{
+    DCE_CHECK(dte, "DCE should bind with a DTE", err);
+    /* malloc memory for sim800_dce object */
+    sim800_modem_dce_t *sim800_dce = calloc(1, sizeof(sim800_modem_dce_t));
+    DCE_CHECK(sim800_dce, "calloc sim800_dce failed", err);
+    /* Bind DTE with DCE */
+    sim800_dce->parent.dte = dte;
+    dte->dce = &(sim800_dce->parent);
+    /* Bind methods */
+    sim800_dce->parent.handle_line = NULL;
+    sim800_dce->parent.sync = esp_modem_dce_sync;
+    sim800_dce->parent.echo_mode = esp_modem_dce_echo;
+    sim800_dce->parent.store_profile = esp_modem_dce_store_profile;
+    sim800_dce->parent.set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
+    sim800_dce->parent.define_pdp_context = esp_modem_dce_define_pdp_context;
+    sim800_dce->parent.hang_up = esp_modem_dce_hang_up;
+    sim800_dce->parent.get_signal_quality = sim800_get_signal_quality;
+    sim800_dce->parent.get_battery_status = sim800_get_battery_status;
+    sim800_dce->parent.set_working_mode = sim800_set_working_mode;
+    sim800_dce->parent.power_down = sim800_power_down;
+    sim800_dce->parent.deinit = sim800_deinit;
+    /* Sync between DTE and DCE */
+    DCE_CHECK(esp_modem_dce_sync(&(sim800_dce->parent)) == ESP_OK, "sync failed", err_io);
+    /* Close echo */
+    DCE_CHECK(esp_modem_dce_echo(&(sim800_dce->parent), false) == ESP_OK, "close echo mode failed", err_io);
+    /* Get Module name */
+    DCE_CHECK(sim800_get_module_name(sim800_dce) == ESP_OK, "get module name failed", err_io);
+    /* Get IMEI number */
+    DCE_CHECK(sim800_get_imei_number(sim800_dce) == ESP_OK, "get imei failed", err_io);
+    /* Get IMSI number */
+    DCE_CHECK(sim800_get_imsi_number(sim800_dce) == ESP_OK, "get imsi failed", err_io);
+    /* Get operator name */
+    DCE_CHECK(sim800_get_operator_name(sim800_dce) == ESP_OK, "get operator name failed", err_io);
+    return &(sim800_dce->parent);
+err_io:
+    free(sim800_dce);
+err:
+    return NULL;
+}
index af3153fa08c2936bffd868cf7b78813b0c6ab26c..d8b648f79f69f2607d142aa028983640e2bff0a1 100644 (file)
 menu "Example Configuration"
 
-config GSM_INTERNET_USER
-    string "GSM Internet User"
-       default ""
-       help
-               Network provider internet user.
-
-config GSM_INTERNET_PASSWORD
-    string "GSM Internet password"
-       default ""
-       help
-               Network provider internet password
-
-config GSM_APN
-    string "GSM Internet APN"
-    default "playmetric"
-    help
-       APN from network provider for internet access
-
-config UART1_TX_PIN
-    int "PPP serial TX GPIO"
-    default 17
-    range 0 31
-    help
-       Pin to configure for UART1 TX
-
-config UART1_RX_PIN
-    int "PPP serial RX GPIO"
-    default 16
-    range 0 31
-    help
-       Pin to configure for UART1 RX
-
-config UART1_RTS_PIN
-    int "PPP serial RTS GPIO"
-    default 18
-    range 0 31
-    help
-       Pin to configure for UART1 RTS
-
-config UART1_CTS_PIN
-    int "PPP serial CTS GPIO"
-    default 23
-    range 0 31
-    help
-       Pin to configure for UART1 CTS
+    choice ESP_MODEM_DEVICE
+        prompt "Choose supported modem device (DCE)"
+        default ESP_MODEM_DEVICE_BG96
+        help
+            Select modem device connected to the ESP DTE.
+        config ESP_MODEM_DEVICE_SIM800
+            bool "SIM800"
+            help
+                SIMCom SIM800L is a GSM/GPRS module.
+                It supports Quad-band 850/900/1800/1900MHz.
+        config ESP_MODEM_DEVICE_BG96
+            bool "BG96"
+            help
+                Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
+    endchoice
+
+    config ESP_MODEM_APN
+        string "Set Access Point Name (APN)"
+        default "CMNET"
+        help
+            Logical name which is used to select the GGSN or the external packet data network.
+
+    config ESP_MODEM_PPP_AUTH_USERNAME
+        string "Set username for authentication"
+        default "espressif"
+        help
+            Set username for PPP Authentication.
+
+    config ESP_MODEM_PPP_AUTH_PASSWORD
+        string "Set password for authentication"
+        default "esp32"
+        help
+            Set password for PPP Authentication.
+
+    config SEND_MSG
+        bool "Short message (SMS)"
+        default n
+        help
+            Select this, the modem will send a short message before power off.
+
+    if SEND_MSG
+    config SEND_MSG_PEER_PHONE_NUMBER
+        string "Peer Phone Number (with area code)"
+        default "+8610086"
+        help
+            Enter the peer phone number that you want to send message to.
+    endif
+
+    menu "UART Configuration"
+        config MODEM_TX_PIN
+            int "TXD Pin Number"
+            default 25
+            range 0 31
+            help
+                Pin number of UART TX.
+
+        config MODEM_RX_PIN
+            int "RXD Pin Number"
+            default 26
+            range 0 31
+            help
+                Pin number of UART RX.
+
+        config MODEM_RTS_PIN
+            int "RTS Pin Number"
+            default 27
+            range 0 31
+            help
+                Pin number of UART RTS.
+
+        config MODEM_CTS_PIN
+            int "CTS Pin Number"
+            default 23
+            range 0 31
+            help
+                Pin number of UART CTS.
+
+        config UART_EVENT_TASK_STACK_SIZE
+            int "UART Event Task Stack Size"
+            range 2000 6000
+            default 2048
+            help
+                Stack size of UART event task.
+
+        config UART_EVENT_TASK_PRIORITY
+            int "UART Event Task Priority"
+            range 3 22
+            default 5
+            help
+                Priority of UART event task.
+
+        config UART_EVENT_QUEUE_SIZE
+            int "UART Event Queue Size"
+            range 10 40
+            default 30
+            help
+                Length of UART event queue.
+
+        config UART_PATTERN_QUEUE_SIZE
+            int "UART Pattern Queue Size"
+            range 10 40
+            default 20
+            help
+                Length of UART pattern queue.
+
+        config UART_TX_BUFFER_SIZE
+            int "UART TX Buffer Size"
+            range 256 2048
+            default 512
+            help
+                Buffer size of UART TX buffer.
+
+        config UART_RX_BUFFER_SIZE
+            int "UART RX Buffer Size"
+            range 256 2048
+            default 1024
+            help
+                Buffer size of UART RX buffer.
+    endmenu
 
 endmenu
index 0c3c0858c8b4e2de5765307b280a1a5b8678e45d..1ef9d2c1f112b419028c71688a20c5ec94f8f7ff 100644 (file)
-/* PPPoS Client Example with GSM (tested with Telit GL865-DUAL-V3)
+/* 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/task.h"
 #include "freertos/event_groups.h"
-#include "esp_system.h"
-#include "esp_wifi.h"
-#include "esp_event_loop.h"
+#include "tcpip_adapter.h"
+#include "mqtt_client.h"
+#include "esp_modem.h"
 #include "esp_log.h"
-#include "nvs_flash.h"
-
-#include "driver/uart.h"
-
-#include "netif/ppp/pppos.h"
-#include "netif/ppp/pppapi.h"
-#include "lwip/err.h"
-#include "lwip/sockets.h"
-#include "lwip/sys.h"
-#include "lwip/netdb.h"
-#include "lwip/dns.h"
-
-/* The examples use simple GSM configuration that you can set via
-   'make menuconfig'.
+#include "sim800.h"
+#include "bg96.h"
+
+#define BROKER_URL "mqtt://iot.eclipse.org"
+
+static const char *TAG = "pppos_example";
+static EventGroupHandle_t event_group = NULL;
+static const int CONNECT_BIT = BIT0;
+static const int STOP_BIT = BIT1;
+static const int GOT_DATA_BIT = BIT2;
+
+#if CONFIG_SEND_MSG
+/**
+ * @brief This example will also show how to send short message using the infrastructure provided by esp modem library.
+ * @note Not all modem support SMG.
+ *
  */
-#define BUF_SIZE (1024)
-const char *PPP_User = CONFIG_GSM_INTERNET_USER;
-const char *PPP_Pass = CONFIG_GSM_INTERNET_PASSWORD;
-const char *PPP_ApnATReq = "AT+CGDCONT=1,\"IP\",\"" \
-                           CONFIG_GSM_APN \
-                           "\"";
-
-/* Pins used for serial communication with GSM module */
-#define UART1_TX_PIN CONFIG_UART1_TX_PIN
-#define UART1_RX_PIN CONFIG_UART1_RX_PIN
-#define UART1_RTS_PIN CONFIG_UART1_RTS_PIN
-#define UART1_CTS_PIN CONFIG_UART1_CTS_PIN
-
-/* UART */
-int uart_num = UART_NUM_1;
-
-/* The PPP control block */
-ppp_pcb *ppp;
-
-/* The PPP IP interface */
-struct netif ppp_netif;
-
-static const char *TAG = "example";
-
-typedef struct {
-    const char *cmd;
-    uint16_t cmdSize;
-    const char *cmdResponseOnOk;
-    uint32_t timeoutMs;
-} GSM_Cmd;
-
-#define GSM_OK_Str "OK"
-
-GSM_Cmd GSM_MGR_InitCmds[] = {
-    {
-        .cmd = "AT\r",
-        .cmdSize = sizeof("AT\r") - 1,
-        .cmdResponseOnOk = GSM_OK_Str,
-        .timeoutMs = 3000,
-    },
-    {
-        .cmd = "ATE0\r",
-        .cmdSize = sizeof("ATE0\r") - 1,
-        .cmdResponseOnOk = GSM_OK_Str,
-        .timeoutMs = 3000,
-    },
-    {
-        .cmd = "AT+CPIN?\r",
-        .cmdSize = sizeof("AT+CPIN?\r") - 1,
-        .cmdResponseOnOk = "CPIN: READY",
-        .timeoutMs = 3000,
-    },
-    {
-        //AT+CGDCONT=1,"IP","apn"
-        .cmd = "AT+CGDCONT=1,\"IP\",\"playmetric\"\r",
-        .cmdSize = sizeof("AT+CGDCONT=1,\"IP\",\"playmetric\"\r") - 1,
-        .cmdResponseOnOk = GSM_OK_Str,
-        .timeoutMs = 3000,
-    },
-    {
-        .cmd = "ATDT*99***1#\r",
-        .cmdSize = sizeof("ATDT*99***1#\r") - 1,
-        .cmdResponseOnOk = "CONNECT",
-        .timeoutMs = 30000,
+static esp_err_t example_default_handle(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
     }
-};
+    return err;
+}
 
-#define GSM_MGR_InitCmdsSize  (sizeof(GSM_MGR_InitCmds)/sizeof(GSM_Cmd))
+static esp_err_t example_handle_cmgs(modem_dce_t *dce, const char *line)
+{
+    esp_err_t err = ESP_FAIL;
+    if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
+    } else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
+        err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
+    } else if (!strncmp(line, "+CMGS", strlen("+CMGS"))) {
+        err = ESP_OK;
+    }
+    return err;
+}
 
-/* PPP status callback example */
-static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
+#define MODEM_SMS_MAX_LENGTH (128)
+#define MODEM_COMMAND_TIMEOUT_SMS_MS (120000)
+#define MODEM_PROMPT_TIMEOUT_MS (10)
+
+static esp_err_t example_send_message_text(modem_dce_t *dce, const char *phone_num, const char *text)
 {
-    struct netif *pppif = ppp_netif(pcb);
-    LWIP_UNUSED_ARG(ctx);
+    modem_dte_t *dte = dce->dte;
+    dce->handle_line = example_default_handle;
+    /* Set text mode */
+    if (dte->send_cmd(dte, "AT+CMGF=1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
+        ESP_LOGE(TAG, "send command failed");
+        goto err;
+    }
+    if (dce->state != MODEM_STATE_SUCCESS) {
+        ESP_LOGE(TAG, "set message format failed");
+        goto err;
+    }
+    ESP_LOGD(TAG, "set message format ok");
+    /* Specify character set */
+    dce->handle_line = example_default_handle;
+    if (dte->send_cmd(dte, "AT+CSCS=\"GSM\"\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
+        ESP_LOGE(TAG, "send command failed");
+        goto err;
+    }
+    if (dce->state != MODEM_STATE_SUCCESS) {
+        ESP_LOGE(TAG, "set character set failed");
+        goto err;
+    }
+    ESP_LOGD(TAG, "set character set ok");
+    /* send message */
+    char command[MODEM_SMS_MAX_LENGTH] = {0};
+    int length = snprintf(command, MODEM_SMS_MAX_LENGTH, "AT+CMGS=\"%s\"\r", phone_num);
+    /* set phone number and wait for "> " */
+    dte->send_wait(dte, command, length, "\r\n> ", MODEM_PROMPT_TIMEOUT_MS);
+    /* end with CTRL+Z */
+    snprintf(command, MODEM_SMS_MAX_LENGTH, "%s\x1A", text);
+    dce->handle_line = example_handle_cmgs;
+    if (dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_SMS_MS) != ESP_OK) {
+        ESP_LOGE(TAG, "send command failed");
+        goto err;
+    }
+    if (dce->state != MODEM_STATE_SUCCESS) {
+        ESP_LOGE(TAG, "send message failed");
+        goto err;
+    }
+    ESP_LOGD(TAG, "send message ok");
+    return ESP_OK;
+err:
+    return ESP_FAIL;
+}
+#endif
 
-    switch (err_code) {
-    case PPPERR_NONE: {
-        ESP_LOGI(TAG, "status_cb: Connected\n");
-#if PPP_IPV4_SUPPORT
-        ESP_LOGI(TAG, "   our_ipaddr  = %s\n", ipaddr_ntoa(&pppif->ip_addr));
-        ESP_LOGI(TAG, "   his_ipaddr  = %s\n", ipaddr_ntoa(&pppif->gw));
-        ESP_LOGI(TAG, "   netmask     = %s\n", ipaddr_ntoa(&pppif->netmask));
-#endif /* PPP_IPV4_SUPPORT */
-#if PPP_IPV6_SUPPORT
-        ESP_LOGI(TAG, "   our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
-#endif /* PPP_IPV6_SUPPORT */
+static void modem_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
+{
+    switch (event_id) {
+    case MODEM_EVENT_PPP_START:
+        ESP_LOGI(TAG, "Modem PPP Started");
         break;
-    }
-    case PPPERR_PARAM: {
-        ESP_LOGE(TAG, "status_cb: Invalid parameter\n");
+    case MODEM_EVENT_PPP_CONNECT:
+        ESP_LOGI(TAG, "Modem Connect to PPP Server");
+        ppp_client_ip_info_t *ipinfo = (ppp_client_ip_info_t *)(event_data);
+        ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
+        ESP_LOGI(TAG, "IP          : " IPSTR, IP2STR(&ipinfo->ip));
+        ESP_LOGI(TAG, "Netmask     : " IPSTR, IP2STR(&ipinfo->netmask));
+        ESP_LOGI(TAG, "Gateway     : " IPSTR, IP2STR(&ipinfo->gw));
+        ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&ipinfo->ns1));
+        ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&ipinfo->ns2));
+        ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
+        xEventGroupSetBits(event_group, CONNECT_BIT);
         break;
-    }
-    case PPPERR_OPEN: {
-        ESP_LOGE(TAG, "status_cb: Unable to open PPP session\n");
+    case MODEM_EVENT_PPP_DISCONNECT:
+        ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
         break;
-    }
-    case PPPERR_DEVICE: {
-        ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP\n");
+    case MODEM_EVENT_PPP_STOP:
+        ESP_LOGI(TAG, "Modem PPP Stopped");
+        xEventGroupSetBits(event_group, STOP_BIT);
         break;
-    }
-    case PPPERR_ALLOC: {
-        ESP_LOGE(TAG, "status_cb: Unable to allocate resources\n");
+    case MODEM_EVENT_UNKNOWN:
+        ESP_LOGW(TAG, "Unknow line received: %s", (char *)event_data);
         break;
-    }
-    case PPPERR_USER: {
-        ESP_LOGE(TAG, "status_cb: User interrupt\n");
+    default:
         break;
     }
-    case PPPERR_CONNECT: {
-        ESP_LOGE(TAG, "status_cb: Connection lost\n");
+}
+
+static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
+{
+    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 PPPERR_AUTHFAIL: {
-        ESP_LOGE(TAG, "status_cb: Failed authentication challenge\n");
+    case MQTT_EVENT_DISCONNECTED:
+        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
         break;
-    }
-    case PPPERR_PROTOCOL: {
-        ESP_LOGE(TAG, "status_cb: Failed to meet protocol\n");
+    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 PPPERR_PEERDEAD: {
-        ESP_LOGE(TAG, "status_cb: Connection timeout\n");
+    case MQTT_EVENT_UNSUBSCRIBED:
+        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
         break;
-    }
-    case PPPERR_IDLETIMEOUT: {
-        ESP_LOGE(TAG, "status_cb: Idle Timeout\n");
+    case MQTT_EVENT_PUBLISHED:
+        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
         break;
-    }
-    case PPPERR_CONNECTTIME: {
-        ESP_LOGE(TAG, "status_cb: Max connect time reached\n");
+    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 PPPERR_LOOPBACK: {
-        ESP_LOGE(TAG, "status_cb: Loopback detected\n");
+    case MQTT_EVENT_ERROR:
+        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
         break;
-    }
-    default: {
-        ESP_LOGE(TAG, "status_cb: Unknown error code %d\n", err_code);
+    default:
+        ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
         break;
     }
-    }
-
-    /*
-     * This should be in the switch case, this is put outside of the switch
-     * case for example readability.
-     */
-
-    if (err_code == PPPERR_NONE) {
-        return;
-    }
-
-    /* ppp_close() was previously called, don't reconnect */
-    if (err_code == PPPERR_USER) {
-        /* ppp_free(); -- can be called here */
-        return;
-    }
-
-
-    /*
-     * Try to reconnect in 30 seconds, if you need a modem chatscript you have
-     * to do a much better signaling here ;-)
-     */
-    //ppp_connect(pcb, 30);
-    /* OR ppp_listen(pcb); */
-}
-
-static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
-{
-    ESP_LOGI(TAG, "PPP tx len %d", len);
-    return uart_write_bytes(uart_num, (const char *)data, len);
-}
-
-static void pppos_client_task()
-{
-    char *data = (char *) malloc(BUF_SIZE);
-    uart_config_t uart_config = {
-        .baud_rate = 115200,
-        .data_bits = UART_DATA_8_BITS,
-        .parity = UART_PARITY_DISABLE,
-        .stop_bits = UART_STOP_BITS_1,
-        .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS
-    };
-    //Configure UART1 parameters
-    uart_param_config(uart_num, &uart_config);
-
-    // Configure UART1 pins (as set in example's menuconfig)
-    ESP_LOGI(TAG, "Configuring UART1 GPIOs: TX:%d RX:%d RTS:%d CTS: %d",
-             UART1_TX_PIN, UART1_RX_PIN, UART1_RTS_PIN, UART1_CTS_PIN);
-    uart_set_pin(uart_num, UART1_TX_PIN, UART1_RX_PIN, UART1_RTS_PIN, UART1_CTS_PIN);
-    uart_driver_install(uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0);
-
-    while (1) {
-        //init gsm
-        int gsmCmdIter = 0;
-        while (1) {
-            ESP_LOGI(TAG, "%s", GSM_MGR_InitCmds[gsmCmdIter].cmd);
-            uart_write_bytes(uart_num, (const char *)GSM_MGR_InitCmds[gsmCmdIter].cmd,
-                             GSM_MGR_InitCmds[gsmCmdIter].cmdSize);
-
-            int timeoutCnt = 0;
-            while (1) {
-                memset(data, 0, BUF_SIZE);
-                int len = uart_read_bytes(uart_num, (uint8_t *)data, BUF_SIZE, 500 / portTICK_RATE_MS);
-                if (len > 0) {
-                    ESP_LOGI(TAG, "%s", data);
-                }
-
-                timeoutCnt += 500;
-                if (strstr(data, GSM_MGR_InitCmds[gsmCmdIter].cmdResponseOnOk) != NULL) {
-                    break;
-                }
-
-                if (timeoutCnt > GSM_MGR_InitCmds[gsmCmdIter].timeoutMs) {
-                    ESP_LOGE(TAG, "Gsm Init Error");
-                    return;
-                }
-            }
-            gsmCmdIter++;
-
-            if (gsmCmdIter >= GSM_MGR_InitCmdsSize) {
-                break;
-            }
-        }
-
-        ESP_LOGI(TAG, "Gsm init end");
-
-        ppp = pppapi_pppos_create(&ppp_netif,
-                                  ppp_output_callback, ppp_status_cb, NULL);
-
-        ESP_LOGI(TAG, "After pppapi_pppos_create");
-
-        if (ppp == NULL) {
-            ESP_LOGE(TAG, "Error init pppos");
-            return;
-        }
-
-        pppapi_set_default(ppp);
-
-        ESP_LOGI(TAG, "After pppapi_set_default");
-
-        pppapi_set_auth(ppp, PPPAUTHTYPE_PAP, PPP_User, PPP_Pass);
-
-        ESP_LOGI(TAG, "After pppapi_set_auth");
-
-        pppapi_connect(ppp, 0);
-
-        ESP_LOGI(TAG, "After pppapi_connect");
-
-        while (1) {
-            memset(data, 0, BUF_SIZE);
-            int len = uart_read_bytes(uart_num, (uint8_t *)data, BUF_SIZE, 10 / portTICK_RATE_MS);
-            if (len > 0) {
-                ESP_LOGI(TAG, "PPP rx len %d", len);
-                pppos_input_tcpip(ppp, (u8_t *)data, len);
-            }
-        }
-
-    }
+    return ESP_OK;
 }
 
 void app_main()
 {
     tcpip_adapter_init();
-    xTaskCreate(&pppos_client_task, "pppos_client_task", 2048, NULL, 5, NULL);
+    event_group = xEventGroupCreate();
+    /* create dte object */
+    esp_modem_dte_config_t config = ESP_MODEM_DTE_DEFAULT_CONFIG();
+    modem_dte_t *dte = esp_modem_dte_init(&config);
+    /* Register event handler */
+    ESP_ERROR_CHECK(esp_modem_add_event_handler(dte, modem_event_handler, NULL));
+    /* create dce object */
+#if CONFIG_ESP_MODEM_DEVICE_SIM800
+    modem_dce_t *dce = sim800_init(dte);
+#elif CONFIG_ESP_MODEM_DEVICE_BG96
+    modem_dce_t *dce = bg96_init(dte);
+#else
+#error "Unsupported DCE"
+#endif
+    ESP_ERROR_CHECK(dce->set_flow_ctrl(dce, MODEM_FLOW_CONTROL_NONE));
+    ESP_ERROR_CHECK(dce->store_profile(dce));
+    /* Print Module ID, Operator, IMEI, IMSI */
+    ESP_LOGI(TAG, "Module: %s", dce->name);
+    ESP_LOGI(TAG, "Operator: %s", dce->oper);
+    ESP_LOGI(TAG, "IMEI: %s", dce->imei);
+    ESP_LOGI(TAG, "IMSI: %s", dce->imsi);
+    /* Get signal quality */
+    uint32_t rssi = 0, ber = 0;
+    ESP_ERROR_CHECK(dce->get_signal_quality(dce, &rssi, &ber));
+    ESP_LOGI(TAG, "rssi: %d, ber: %d", rssi, ber);
+    /* Get battery voltage */
+    uint32_t voltage = 0, bcs = 0, bcl = 0;
+    ESP_ERROR_CHECK(dce->get_battery_status(dce, &bcs, &bcl, &voltage));
+    ESP_LOGI(TAG, "Battery voltage: %d mV", voltage);
+    /* Setup PPP environment */
+    esp_modem_setup_ppp(dte);
+    /* Wait for IP address */
+    xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
+    /* Config MQTT */
+    esp_mqtt_client_config_t mqtt_config = {
+        .uri = BROKER_URL,
+        .event_handle = mqtt_event_handler,
+    };
+    esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
+    esp_mqtt_client_start(mqtt_client);
+    xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
+    esp_mqtt_client_destroy(mqtt_client);
+    /* Exit PPP mode */
+    ESP_ERROR_CHECK(esp_modem_exit_ppp(dte));
+    xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
+#if CONFIG_SEND_MSG
+    const char *message = "Welcome to ESP32!";
+    ESP_ERROR_CHECK(example_send_message_text(dce, CONFIG_SEND_MSG_PEER_PHONE_NUMBER, message));
+    ESP_LOGI(TAG, "Send send message [%s] ok", message);
+#endif
+    /* Power down module */
+    ESP_ERROR_CHECK(dce->power_down(dce));
+    ESP_LOGI(TAG, "Power down");
+    ESP_ERROR_CHECK(dce->deinit(dce));
+    ESP_ERROR_CHECK(dte->deinit(dte));
 }
index ad5b9364f2cd1afa9d346af173e0f3d660db8807..91833a05fe5a2b9e4254b193a9e70b3e14477800 100644 (file)
@@ -1,5 +1,5 @@
 # Override some defaults to enable PPP
 CONFIG_PPP_SUPPORT=y
+CONFIG_PPP_NOTIFY_PHASE_SUPPORT=y
 CONFIG_PPP_PAP_SUPPORT=y
-CONFIG_PPP_DEBUG_ON=y
-CONFIG_TCPIP_TASK_STACK_SIZE=4096
\ No newline at end of file
+CONFIG_TCPIP_TASK_STACK_SIZE=4096