mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 09:39:10 -04:00
modem: support modem facility and PPPoS
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
This commit is contained in:
parent
07645955a2
commit
2e1f98f8f5
@ -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
|
||||
|
@ -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,
|
||||
@ -11,21 +11,21 @@
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
@ -316,7 +316,7 @@
|
||||
* 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.
|
||||
@ -576,6 +576,11 @@
|
||||
|
||||
#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.
|
||||
*/
|
||||
@ -759,7 +764,7 @@
|
||||
#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
|
||||
|
||||
|
@ -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.)
|
||||
|
@ -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()
|
@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
477
examples/protocols/pppos_client/components/modem/src/bg96.c
Normal file
477
examples/protocols/pppos_client/components/modem/src/bg96.c
Normal file
@ -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;
|
||||
}
|
620
examples/protocols/pppos_client/components/modem/src/esp_modem.c
Normal file
620
examples/protocols/pppos_client/components/modem/src/esp_modem.c
Normal file
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
476
examples/protocols/pppos_client/components/modem/src/sim800.c
Normal file
476
examples/protocols/pppos_client/components/modem/src/sim800.c
Normal file
@ -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;
|
||||
}
|
@ -1,49 +1,123 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config GSM_INTERNET_USER
|
||||
string "GSM Internet User"
|
||||
default ""
|
||||
help
|
||||
Network provider internet user.
|
||||
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 GSM_INTERNET_PASSWORD
|
||||
string "GSM Internet password"
|
||||
default ""
|
||||
help
|
||||
Network provider internet password
|
||||
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 GSM_APN
|
||||
string "GSM Internet APN"
|
||||
default "playmetric"
|
||||
help
|
||||
APN from network provider for internet access
|
||||
config ESP_MODEM_PPP_AUTH_USERNAME
|
||||
string "Set username for authentication"
|
||||
default "espressif"
|
||||
help
|
||||
Set username for PPP Authentication.
|
||||
|
||||
config UART1_TX_PIN
|
||||
int "PPP serial TX GPIO"
|
||||
default 17
|
||||
range 0 31
|
||||
help
|
||||
Pin to configure for UART1 TX
|
||||
config ESP_MODEM_PPP_AUTH_PASSWORD
|
||||
string "Set password for authentication"
|
||||
default "esp32"
|
||||
help
|
||||
Set password for PPP Authentication.
|
||||
|
||||
config UART1_RX_PIN
|
||||
int "PPP serial RX GPIO"
|
||||
default 16
|
||||
range 0 31
|
||||
help
|
||||
Pin to configure for UART1 RX
|
||||
config SEND_MSG
|
||||
bool "Short message (SMS)"
|
||||
default n
|
||||
help
|
||||
Select this, the modem will send a short message before power off.
|
||||
|
||||
config UART1_RTS_PIN
|
||||
int "PPP serial RTS GPIO"
|
||||
default 18
|
||||
range 0 31
|
||||
help
|
||||
Pin to configure for UART1 RTS
|
||||
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
|
||||
|
||||
config UART1_CTS_PIN
|
||||
int "PPP serial CTS GPIO"
|
||||
default 23
|
||||
range 0 31
|
||||
help
|
||||
Pin to configure for UART1 CTS
|
||||
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
|
||||
|
@ -1,296 +1,240 @@
|
||||
/* 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 "sim800.h"
|
||||
#include "bg96.h"
|
||||
|
||||
#include "driver/uart.h"
|
||||
#define BROKER_URL "mqtt://iot.eclipse.org"
|
||||
|
||||
#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"
|
||||
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;
|
||||
|
||||
/* The examples use simple GSM configuration that you can set via
|
||||
'make menuconfig'.
|
||||
#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,
|
||||
}
|
||||
};
|
||||
|
||||
#define GSM_MGR_InitCmdsSize (sizeof(GSM_MGR_InitCmds)/sizeof(GSM_Cmd))
|
||||
|
||||
/* PPP status callback example */
|
||||
static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
|
||||
static esp_err_t example_default_handle(modem_dce_t *dce, const char *line)
|
||||
{
|
||||
struct netif *pppif = ppp_netif(pcb);
|
||||
LWIP_UNUSED_ARG(ctx);
|
||||
|
||||
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 */
|
||||
break;
|
||||
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);
|
||||
}
|
||||
case PPPERR_PARAM: {
|
||||
ESP_LOGE(TAG, "status_cb: Invalid parameter\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_OPEN: {
|
||||
ESP_LOGE(TAG, "status_cb: Unable to open PPP session\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_DEVICE: {
|
||||
ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_ALLOC: {
|
||||
ESP_LOGE(TAG, "status_cb: Unable to allocate resources\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_USER: {
|
||||
ESP_LOGE(TAG, "status_cb: User interrupt\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECT: {
|
||||
ESP_LOGE(TAG, "status_cb: Connection lost\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_AUTHFAIL: {
|
||||
ESP_LOGE(TAG, "status_cb: Failed authentication challenge\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PROTOCOL: {
|
||||
ESP_LOGE(TAG, "status_cb: Failed to meet protocol\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PEERDEAD: {
|
||||
ESP_LOGE(TAG, "status_cb: Connection timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_IDLETIMEOUT: {
|
||||
ESP_LOGE(TAG, "status_cb: Idle Timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECTTIME: {
|
||||
ESP_LOGE(TAG, "status_cb: Max connect time reached\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_LOOPBACK: {
|
||||
ESP_LOGE(TAG, "status_cb: Loopback detected\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ESP_LOGE(TAG, "status_cb: Unknown error code %d\n", err_code);
|
||||
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); */
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
|
||||
static esp_err_t example_handle_cmgs(modem_dce_t *dce, const char *line)
|
||||
{
|
||||
ESP_LOGI(TAG, "PPP tx len %d", len);
|
||||
return uart_write_bytes(uart_num, (const char *)data, len);
|
||||
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;
|
||||
}
|
||||
|
||||
static void pppos_client_task()
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
modem_dte_t *dte = dce->dte;
|
||||
dce->handle_line = example_default_handle;
|
||||
/* Set text mode */
|
||||
if (dte->send_cmd(dte, "AT+CMGF=1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "send command failed");
|
||||
goto err;
|
||||
}
|
||||
if (dce->state != MODEM_STATE_SUCCESS) {
|
||||
ESP_LOGE(TAG, "set message format failed");
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGD(TAG, "set message format ok");
|
||||
/* Specify character set */
|
||||
dce->handle_line = example_default_handle;
|
||||
if (dte->send_cmd(dte, "AT+CSCS=\"GSM\"\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "send command failed");
|
||||
goto err;
|
||||
}
|
||||
if (dce->state != MODEM_STATE_SUCCESS) {
|
||||
ESP_LOGE(TAG, "set character set failed");
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGD(TAG, "set character set ok");
|
||||
/* send message */
|
||||
char command[MODEM_SMS_MAX_LENGTH] = {0};
|
||||
int length = snprintf(command, MODEM_SMS_MAX_LENGTH, "AT+CMGS=\"%s\"\r", phone_num);
|
||||
/* set phone number and wait for "> " */
|
||||
dte->send_wait(dte, command, length, "\r\n> ", MODEM_PROMPT_TIMEOUT_MS);
|
||||
/* end with CTRL+Z */
|
||||
snprintf(command, MODEM_SMS_MAX_LENGTH, "%s\x1A", text);
|
||||
dce->handle_line = example_handle_cmgs;
|
||||
if (dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_SMS_MS) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "send command failed");
|
||||
goto err;
|
||||
}
|
||||
if (dce->state != MODEM_STATE_SUCCESS) {
|
||||
ESP_LOGE(TAG, "send message failed");
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGD(TAG, "send message ok");
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void modem_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
switch (event_id) {
|
||||
case MODEM_EVENT_PPP_START:
|
||||
ESP_LOGI(TAG, "Modem PPP Started");
|
||||
break;
|
||||
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 MODEM_EVENT_PPP_DISCONNECT:
|
||||
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
|
||||
break;
|
||||
case MODEM_EVENT_PPP_STOP:
|
||||
ESP_LOGI(TAG, "Modem PPP Stopped");
|
||||
xEventGroupSetBits(event_group, STOP_BIT);
|
||||
break;
|
||||
case MODEM_EVENT_UNKNOWN:
|
||||
ESP_LOGW(TAG, "Unknow line received: %s", (char *)event_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "esp32-pppos", 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
xEventGroupSetBits(event_group, GOT_DATA_BIT);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
@ -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
|
||||
CONFIG_TCPIP_TASK_STACK_SIZE=4096
|
||||
|
Loading…
x
Reference in New Issue
Block a user