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
pull/3010/head
morris 2018-11-23 11:53:20 +08:00
rodzic 07645955a2
commit 2e1f98f8f5
18 zmienionych plików z 2713 dodań i 350 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,3 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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));
}

Wyświetl plik

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