esp_modem: Refactor the modem to a standalone managed component

This commit is contained in:
David Cermak
2020-11-18 21:20:35 +01:00
parent 5565a09ed1
commit c8e89098bd
63 changed files with 5081 additions and 2180 deletions

90
esp_modem/.gitignore vendored Normal file
View File

@ -0,0 +1,90 @@
.config
*.o
*.pyc
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Components Unit Test Apps files
components/**/build
components/**/sdkconfig
components/**/sdkconfig.old
# Example project files
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build
# Doc build artifacts
docs/_build/
docs/doxygen_sqlite3.db
# Downloaded font files
docs/_static/DejaVuSans.ttf
docs/_static/NotoSansSC-Regular.otf
# Unit test app files
tools/unit-test-app/sdkconfig
tools/unit-test-app/sdkconfig.old
tools/unit-test-app/build
tools/unit-test-app/builds
tools/unit-test-app/output
tools/unit-test-app/test_configs
# Unit Test CMake compile log folder
log_ut_cmake
# test application build files
tools/test_apps/**/build
tools/test_apps/**/sdkconfig
tools/test_apps/**/sdkconfig.old
# IDF monitor test
tools/test_idf_monitor/outputs
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
test_multi_heap_host
# VS Code Settings
.vscode/
# VIM files
*.swp
*.swo
# Clion IDE CMake build & config
.idea/
cmake-build-*/
# Results for the checking of the Python coding style and static analysis
.mypy_cache
flake8_output.txt
# ESP-IDF default build directory name
build
# lock files for examples and components
dependencies.lock

View File

@ -1,12 +1,22 @@
set(srcs "src/esp_modem.c"
"src/esp_modem_dce_service"
"src/esp_modem_dce_command_lib.c"
"src/esp_modem_dce_common_commands.c"
"src/esp_modem_dce.c"
"src/esp_modem_dte.c"
"src/esp_modem_netif.c"
"src/esp_modem_compat.c"
"src/sim800.c"
"src/sim7600.c"
"src/bg96.c")
"src/esp_modem_recov_helper.c"
"src/esp_sim800.c"
"src/esp_sim7600.c"
"src/esp_bg96.c")
set(include_dirs "include")
if(CONFIG_MODEM_LEGACY_API)
list(APPEND srcs "src/esp_modem_compat.c")
list(APPEND include_dirs "include_compat")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS private_include
REQUIRES driver)

View File

@ -1,9 +1,18 @@
menu "ESP-MODEM"
config EXAMPLE_COMPONENT_MODEM_APN
string "Set Access Point Name (APN)"
default "CMNET"
config MODEM_LEGACY_API
bool "Enable Legacy API"
default y
help
Logical name which is used to select the GGSN or the external packet data network.
Set to true if the original pppos_client API from the example's internal
component should be used.....
config MODEM_PPP_APN
string "Local netif hostname"
default 'internet'
depends on MODEM_LEGACY_API
help
APN (Access Point Name), a logical name of a network
the modem connects to in the PPP mode
endmenu

74
esp_modem/README.md Normal file
View File

@ -0,0 +1,74 @@
# ESP MODEM
This component is used to communicate with modems in command and data modes.
It abstracts the UART I/O processing into a DTE (data terminal equipment) entity which is configured and setup separately.
On top of the DTE, the command and data processing is performed in a DCE (data communication equipment) unit.
```
+-----+
| DTE |--+
+-----+ | +-------+
+-->| |
+-----+ | |
| DTE |----->| modem |---> modem events
+-----+ | |
+->| |
+------+ | +-------+
| PPP |--+ | |
| netif|-----------------> network events
+------+ | |
| |
start-ppp----+ |
stop-ppp---------+
```
## Start-up sequence
To initialize the modem we typically:
* create DTE with desired UART parameters
* create DCE with desired command palette
* create PPP netif with desired network parameters
* attach the DTE, DCE and PPP together to start the esp-modem
* configure event handlers for the modem and the netif
Then we can start and stop PPP mode using esp-modem API, receive events from the `ESP_MODEM` base, as well as from netif (IP events).
### DTE
Responsibilities of the DTE unit are
* sending/receiving commands to/from UART
* sending/receiving data to/from UART
* changing data/command mode on physical layer
### DCE
Responsibilities of the DCE unit are
* definition of available commands
* cooperate with DTE on changing data/command mode
#### DCE configuration
DCE unit configuration structure contains:
* device -- sets up predefined commands for this specific modem device
* pdp context -- used to setup cellular network
* populate command list -- uses runtime configurable linked list of commands. New commands could be added and/or
existing commands could be modified. This might be useful for some applications, which have to (re)define
and execute multiple AT commands. For other applications, which typically just connect to network, this option
might be preferably turned off (as the consumes certain amount of data memory)
### PPP-netif
The modem-netif attaches the network interface (which was created outside of esp-modem) to the DTE and
serves as a glue layer between esp-netif and esp-modem.
### Additional units
ESP-MODEM provides also provides a helper module to define a custom retry/reset strategy using:
* a very simple GPIO pulse generator, which could be typically used to reset or power up/down the module
* a command executor abstraction, that helps with retrying sending commands in case of a failure or a timeout
and run user defined recovery actions.
## Modification of existing DCE
In order to support an arbitrary modem, device or introduce a new command we typically have to either modify the DCE,
adding a new or altering an existing command or creating a new "subclass" of the existing DCE variants.

View File

@ -0,0 +1,8 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ap-to-pppos)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ap-to-pppos
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,11 @@
# PPPoS simple client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example focuses on the networking part, enables forwarding packets between network interfaces. It creates a WiFi soft AP, which uses NAT to forward packets to and from the PPP network
interface.
## How to use this example
See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples.

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "ap2pppos_example_main.c"
"modem_board.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,26 @@
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_WIFI_CHANNEL
int "WiFi Channel"
range 1 13
default 1
help
WiFi channel (network channel) for the example to use.
config ESP_MAX_STA_CONN
int "Maximal STA connections"
default 4
help
Max number of the STA connects to AP.
endmenu

View File

@ -0,0 +1,193 @@
/* softAP to PPPoS 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 "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_modem.h"
#include "lwip/lwip_napt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
static const char *TAG = "ap-2-pppos";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int DISCONNECT_BIT = BIT1;
static void on_modem_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == IP_EVENT) {
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, 0, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, 1, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
xEventGroupSetBits(event_group, DISCONNECT_BIT);
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
} else if (event_base == ESP_MODEM_EVENT) {
ESP_LOGD(TAG, "Modem event! %d", event_id);
}
}
static esp_err_t set_dhcps_dns(esp_netif_t *netif, uint32_t addr)
{
esp_netif_dns_info_t dns;
dns.ip.u_addr.ip4.addr = addr;
dns.ip.type = IPADDR_TYPE_V4;
dhcps_offer_t dhcps_dns_value = OFFER_DNS;
ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value)));
ESP_ERROR_CHECK(esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns));
return ESP_OK;
}
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
void wifi_init_softap(void)
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
esp_modem_dce_t *sim7600_board_create(esp_modem_dce_config_t *config);
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
event_group = xEventGroupCreate();
// init the DTE
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.event_task_stack_size = 4096;
dte_config.rx_buffer_size = 16384;
dte_config.tx_buffer_size = 2048;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet23");
dce_config.populate_command_list = true;
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
// Initialize esp-modem units, DTE, DCE, ppp-netif
esp_modem_dte_t *dte = esp_modem_dte_new(&dte_config);
esp_modem_dce_t *dce = sim7600_board_create(&dce_config);
esp_netif_t *ppp_netif = esp_netif_new(&ppp_netif_config);
assert(ppp_netif);
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, on_modem_event, ESP_EVENT_ANY_ID, dte));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_modem_event, dte));
ESP_ERROR_CHECK(esp_modem_default_attach(dte, dce, ppp_netif));
ESP_ERROR_CHECK(esp_modem_default_start(dte)); // use retry
ESP_ERROR_CHECK(esp_modem_start_ppp(dte));
/* Wait for the first connection */
EventBits_t bits;
do {
bits = xEventGroupWaitBits(event_group, (CONNECT_BIT | DISCONNECT_BIT), pdTRUE, pdFALSE, portMAX_DELAY);
if (bits&DISCONNECT_BIT) {
}
} while ((bits&CONNECT_BIT) == 0);
/* Init the AP with NAT enabled */
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
assert(ap_netif);
esp_netif_dns_info_t dns;
ESP_ERROR_CHECK(esp_netif_get_dns_info(ppp_netif, ESP_NETIF_DNS_MAIN, &dns));
set_dhcps_dns(ap_netif, dns.ip.u_addr.ip4.addr);
wifi_init_softap();
ip_napt_enable(_g_esp_netif_soft_ap_ip.ip.addr, 1);
/* Provide recovery if disconnection of some kind registered */
while (DISCONNECT_BIT&xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY)) {
// restart the modem PPP mode
ESP_ERROR_CHECK(esp_modem_stop_ppp(dte));
ESP_ERROR_CHECK(esp_modem_start_ppp(dte));
}
}

View File

@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@ -0,0 +1,7 @@
version: "0.0.2"
targets:
- esp32
description: pppos_client to AP
dependencies:
espressif/esp_modem:
version: "~0.0.2"

View File

@ -0,0 +1,162 @@
/* softAP to PPPoS Example (modem_board)
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 <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_modem_recov_helper.h"
#include "esp_modem_dce.h"
#include "esp_modem_dce_common_commands.h"
#define ESP_MODEM_EXAMPLE_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
static const char *TAG = "sim7600_board";
typedef struct {
esp_modem_dce_t parent;
esp_modem_recov_gpio_t *power_pin;
esp_modem_recov_gpio_t *reset_pin;
esp_err_t (*reset)(esp_modem_dce_t *dce);
esp_err_t (*power_down)(esp_modem_dce_t *dce);
esp_modem_recov_resend_t *re_sync;
esp_modem_recov_resend_t *re_store_profile;
} sim7600_board_t;
esp_err_t sim7600_board_handle_powerup(esp_modem_dce_t *dce, const char *line)
{
if (strstr(line, "PB DONE")) {
ESP_LOGI(TAG, "Board ready after hard reset/power-cycle");
}
return ESP_OK;
}
esp_err_t sim7600_board_deinit(esp_modem_dce_t *dce)
{
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
board->power_pin->destroy(board->power_pin);
board->power_pin->destroy(board->reset_pin);
esp_err_t err = esp_modem_command_list_deinit(&board->parent);
if (err == ESP_OK) {
free(dce);
}
return err;
}
esp_err_t sim7600_board_reset(esp_modem_dce_t *dce)
{
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
ESP_LOGI(TAG, "sim7600_board_reset!");
dce->handle_line = sim7600_board_handle_powerup;
board->power_pin->pulse(board->reset_pin);
return ESP_OK;
}
esp_err_t sim7600_board_power_up(esp_modem_dce_t *dce)
{
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
ESP_LOGI(TAG, "sim7600_board_power_up!");
dce->handle_line = sim7600_board_handle_powerup;
board->power_pin->pulse(board->power_pin);
return ESP_OK;
}
esp_err_t sim7600_board_power_down(esp_modem_dce_t *dce)
{
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
ESP_LOGI(TAG, "sim7600_board_power_down!");
/* power down sequence (typical values for SIM7600 Toff=min2.5s, Toff-status=26s) */
dce->handle_line = sim7600_board_handle_powerup;
board->power_pin->pulse_special(board->power_pin, 3000, 26000);
return ESP_OK;
}
static esp_err_t my_recov(esp_modem_recov_resend_t *retry_cmd, esp_err_t err, int timeouts, int errors)
{
esp_modem_dce_t *dce = retry_cmd->dce;
ESP_LOGI(TAG, "Current timeouts: %d and errors: %d", timeouts, errors);
if (err == ESP_ERR_TIMEOUT) {
if (timeouts < 2) {
// first timeout, try to exit data mode and sync again
dce->set_command_mode(dce, NULL, NULL);
esp_modem_dce_sync(dce, NULL, NULL);
} else if (timeouts < 3) {
// try to reset with GPIO if resend didn't help
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
board->reset(dce);
} else {
// otherwise power-cycle the board
sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
board->power_down(dce);
esp_modem_dce_sync(dce, NULL, NULL);
}
} else {
// check if a PIN needs to be supplied in case of a failure
bool ready = false;
esp_modem_dce_read_pin(dce, NULL, &ready);
if (!ready) {
esp_modem_dce_set_pin(dce, "1234", NULL);
}
vTaskDelay(1000 / portTICK_RATE_MS);
esp_modem_dce_read_pin(dce, NULL, &ready);
if (!ready) {
return ESP_FAIL;
}
}
return ESP_OK;
}
static DEFINE_RETRY_CMD(re_sync_fn, re_sync, sim7600_board_t)
static DEFINE_RETRY_CMD(re_store_profile_fn, re_store_profile, sim7600_board_t)
esp_err_t sim7600_board_start_up(esp_modem_dce_t *dce)
{
// sim7600_board_t *board = __containerof(dce, sim7600_board_t, parent);
ESP_MODEM_EXAMPLE_CHECK(re_sync_fn(dce, NULL, NULL) == ESP_OK, "sending sync failed", err);
ESP_MODEM_EXAMPLE_CHECK(dce->set_echo(dce, (void*)false, NULL) == ESP_OK, "set_echo failed", err);
ESP_MODEM_EXAMPLE_CHECK(dce->set_flow_ctrl(dce, (void*)ESP_MODEM_FLOW_CONTROL_NONE, NULL) == ESP_OK, "set_flow_ctrl failed", err);
ESP_MODEM_EXAMPLE_CHECK(dce->store_profile(dce, NULL, NULL) == ESP_OK, "store_profile failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_modem_dce_t *sim7600_board_create(esp_modem_dce_config_t *config)
{
sim7600_board_t *board = calloc(1, sizeof(sim7600_board_t));
ESP_MODEM_EXAMPLE_CHECK(board, "failed to allocate board-sim7600 object", err);
ESP_MODEM_EXAMPLE_CHECK(esp_modem_dce_init(&board->parent, config) == ESP_OK, "Failed to init sim7600", err);
/* power on sequence (typical values for SIM7600 Ton=500ms, Ton-status=16s) */
board->power_pin = esp_modem_recov_gpio_new( /*gpio_num*/ 12, /*inactive_level*/ 1, /*active_width*/
500, /*inactive_width*/ 16000);
/* reset sequence (typical values for SIM7600 Treset=200ms, wait 10s after reset */
board->reset_pin = esp_modem_recov_gpio_new( /*gpio_num*/ 13, /*inactive_level*/ 1, /*active_width*/
200, /*inactive_width*/ 10000);
board->parent.deinit = sim7600_board_deinit;
board->reset = sim7600_board_reset;
board->power_down = sim7600_board_power_down;
board->re_sync = esp_modem_recov_resend_new(&board->parent, board->parent.sync, my_recov, 5, 1);
board->parent.start_up = sim7600_board_start_up;
board->re_store_profile = esp_modem_recov_resend_new(&board->parent, board->parent.store_profile, my_recov, 2, 3);
board->parent.store_profile = re_store_profile_fn;
return &board->parent;
err:
return NULL;
}

View File

@ -0,0 +1,12 @@
# Override some defaults to enable PPP
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_PAP_SUPPORT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
# Do not enable IPV6 in dte<->dce link local
CONFIG_LWIP_PPP_ENABLE_IPV6=n
# Disable legacy API
CONFIG_MODEM_LEGACY_API=n
# Enable NAPT
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y

View File

@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modem-console)

View File

@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := modem-console
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,10 @@
# PPPoS simple client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example is mainly targets experimenting with a modem device, sending custom commands and switching to PPP mode using esp-console, command line API.
## How to use this example
See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples.

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "modem_console_main.c"
"httpget_handle.c"
"ping_handle.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,108 @@
/* Modem console 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 <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
#include "esp_http_client.h"
static const char *TAG = "modem_console_httpget";
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if ((bool)evt->user_data &&
!esp_http_client_is_chunked_response(evt->client)) {
ESP_LOG_BUFFER_HEXDUMP(TAG, evt->data, evt->data_len, ESP_LOG_INFO);
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static struct {
struct arg_str *host;
struct arg_lit *hex;
struct arg_end *end;
} http_args;
static int do_http_client(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&http_args);
if (nerrors != 0) {
arg_print_errors(stderr, http_args.end, argv[0]);
return 1;
}
esp_http_client_config_t config = {
.event_handler = http_event_handler,
};
if (http_args.host->count > 0) {
config.url = http_args.host->sval[0];
} else {
config.url = "http://httpbin.org/get";
}
if (http_args.hex->count > 0) {
// show hex data from http-get
config.user_data = (void*)true;
}
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
return 0;
}
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
return 1;
}
void modem_console_register_http(void)
{
http_args.host = arg_str0(NULL, NULL, "<host>", "address or host-name to send GET request (defaults to http://httpbin.org/get)");
http_args.hex = arg_litn("p", "print-hex", 0, 1, "print hex output"),
http_args.end = arg_end(1);
const esp_console_cmd_t http_cmd = {
.command = "httpget",
.help = "http get command to test data mode",
.hint = NULL,
.func = &do_http_client,
.argtable = &http_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&http_cmd));
}

View File

@ -0,0 +1,307 @@
/* Modem console 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 <stdio.h>
#include <string.h>
#include <esp_modem_dce_common_commands.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "argtable3/argtable3.h"
#include "esp_modem.h"
#include "esp_log.h"
// utilities to check network connectivity
void modem_console_register_http(void);
void modem_console_register_ping(void);
static esp_modem_dce_t *s_dce = NULL;
static const char *TAG = "modem_console";
static struct {
struct arg_str *command;
struct arg_int *param_int;
struct arg_str *param_str;
struct arg_str *param_pdp;
struct arg_str *param_bool;
struct arg_str *param;
struct arg_str *result;
struct arg_end *end;
} at_args;
static struct {
struct arg_str *param;
struct arg_end *end;
} modem_args;
static struct {
struct arg_str *command;
struct arg_int *timeout;
struct arg_str *pattern;
struct arg_lit *no_cr;
struct arg_end *end;
} generic_at_args;
static char s_common_in_str[100]; // used as common string input param holder
static char s_common_out_str[100]; // used as output string/command result holder
static esp_err_t handle_line_pattern(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "handle_line_pattern: DCE response: %s\n", line);
if (strstr(line, dce->handle_line_ctx)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
}
return err;
}
static int do_dce(int argc, char **argv)
{
// specific DCE generic command params
static bool bool_result;
static char pdp_type[10];
static char pdp_apn[10];
static esp_modem_dce_pdp_ctx_t pdp = { .type = pdp_type, .apn = pdp_apn };
static esp_modem_dce_csq_ctx_t csq;
static esp_modem_dce_cbc_ctx_t cbc;
int nerrors = arg_parse(argc, argv, (void **) &at_args);
if (nerrors != 0) {
arg_print_errors(stderr, at_args.end, argv[0]);
return 1;
}
void * command_param = NULL;
void * command_result = NULL;
// parse input params
if (at_args.param_int->count > 0) {
command_param = (void*)(at_args.param_int->ival[0]);
} else if (at_args.param_bool->count > 0) {
const char * bool_in_str = at_args.param_bool->sval[0];
s_common_out_str[0] = '\0';
command_result = s_common_out_str;
if (strstr(bool_in_str,"true") || strstr(bool_in_str,"1")) {
command_param = (void*)true;
} else {
command_param = (void*)false;
}
} else if (at_args.param_pdp->count > 0) {
// parse out three comma separated sub-arguments
sscanf(at_args.param_pdp->sval[0], "%d,%s", &pdp.cid, pdp_type);
char *str_apn = strchr(pdp_type, ',');
if (str_apn) {
strncpy(pdp_apn, str_apn + 1, sizeof(pdp_apn));
str_apn[0] = '\0';
}
command_param = &pdp;
} else if (at_args.param_str->count > 0) {
strncpy(s_common_in_str, at_args.param_str->sval[0], sizeof(s_common_in_str));
command_param = s_common_in_str;
} else if (at_args.param->count > 0) { // default param is treated as string
strncpy(s_common_in_str, at_args.param->sval[0], sizeof(s_common_in_str));
command_param = s_common_in_str;
}
// parse output params
if (at_args.result->count > 0) {
const char *res = at_args.result->sval[0];
if (strstr(res, "csq")) {
command_result = &csq;
}else if (strstr(res, "cbc")) {
command_result = &cbc;
} else if (strstr(res, "str")) {
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
} else if (strstr(res, "bool")) {
command_result = &bool_result;
} else {
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
}
}
// by default (if no param/result provided) expect string output
if (command_param == NULL && command_result == NULL) {
s_common_out_str[0] = '\0';
command_param = (void*)sizeof(s_common_out_str);
command_result = s_common_out_str;
}
esp_err_t err = esp_modem_command_list_run(s_dce, at_args.command->sval[0], command_param, command_result);
if (err == ESP_OK) {
printf("Command %s succeeded\n", at_args.command->sval[0]);
if (command_result == s_common_out_str && s_common_out_str[0] != '\0') {
ESP_LOGI(TAG, "Command string output: %s", s_common_out_str);
} else if (command_result == &csq) {
ESP_LOGI(TAG, "Command CSQ output: rssi:%d, ber:%d", csq.rssi, csq.ber);
} else if (command_result == &cbc) {
ESP_LOGI(TAG, "Command battery output:%d mV", cbc.battery_status);
} else if (command_result == &bool_result) {
ESP_LOGI(TAG, "Command bool output: %s", bool_result ? "true" : "false");
}
return 0;
}
ESP_LOGE(TAG, "Command %s failed with %d", at_args.command->sval[0], err);
return 1;
}
static int do_at_command(int argc, char **argv)
{
int timeout = 1000;
int nerrors = arg_parse(argc, argv, (void **)&generic_at_args);
if (nerrors != 0) {
arg_print_errors(stderr, generic_at_args.end, argv[0]);
return 1;
}
esp_modem_dce_handle_line_t handle_line = esp_modem_dce_handle_response_default;
strncpy(s_common_in_str, generic_at_args.command->sval[0], sizeof(s_common_in_str) - 2);
if (generic_at_args.no_cr->count == 0) {
size_t cmd_len = strlen(generic_at_args.command->sval[0]);
s_common_in_str[cmd_len] = '\r';
s_common_in_str[cmd_len + 1] = '\0';
}
if (generic_at_args.timeout->count > 0) {
timeout = generic_at_args.timeout->ival[0];
}
if (generic_at_args.pattern->count > 0) {
strncpy(s_common_out_str, generic_at_args.pattern->sval[0], sizeof(s_common_out_str));
handle_line = handle_line_pattern;
}
if (esp_modem_dce_generic_command(s_dce, s_common_in_str, timeout, handle_line, s_common_out_str) == ESP_OK) {
return 0;
}
return 1;
}
static int do_modem_lifecycle(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&modem_args);
if (nerrors != 0) {
arg_print_errors(stderr, modem_args.end, argv[0]);
return 1;
}
if (modem_args.param->count > 0) {
esp_err_t err = ESP_FAIL;
if (strstr(modem_args.param->sval[0], "PPP")) {
err = esp_modem_start_ppp(s_dce->dte);
} else if (strstr(modem_args.param->sval[0], "CMD")) {
err = esp_modem_stop_ppp(s_dce->dte);
} else {
return 1;
}
if (err == ESP_OK) {
ESP_LOGI(TAG, "set_working_mode %s succeeded", at_args.param->sval[0]);
} else {
ESP_LOGI(TAG, "set_working_mode %s failed with %d", at_args.param->sval[0], err);
}
return 0;
}
return 1;
}
static void register_dce(void)
{
at_args.command = arg_str1(NULL, NULL, "<command>", "Symbolic name of DCE command");
at_args.param_int = arg_int0("i", "int", "<num>", "Input parameter of integer type");
at_args.param_str = arg_str0("s", "str", "<str>", "Input parameter of string type");
at_args.param_pdp = arg_str0("p", "pdp", "<pdp>", "Comma separated string with PDP context");
at_args.param_bool = arg_str0("b", "bool", "<true/false>", "Input parameter of bool type");
at_args.param = arg_str0(NULL, NULL, "<param>", "Default input argument treated as string");
at_args.result = arg_str0("o", "out", "<type>", "Type of output parameter");
at_args.end = arg_end(1);
const esp_console_cmd_t at_cmd = {
.command = "dce",
.help = "send symbolic command to the modem",
.hint = NULL,
.func = &do_dce,
.argtable = &at_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&at_cmd));
}
static void register_at_command(void)
{
generic_at_args.command = arg_str1(NULL, NULL, "<command>", "AT command to send to the modem");
generic_at_args.timeout = arg_int0("t", "timeout", "<timeout>", "command timeout");
generic_at_args.pattern = arg_str0("p", "pattern", "<pattern>", "command response to wait for");
generic_at_args.no_cr = arg_litn("n", "no-cr", 0, 1, "not add trailing CR to the command");
generic_at_args.end = arg_end(1);
const esp_console_cmd_t at_command = {
.command = "at",
.help = "send generic AT command to the modem",
.hint = NULL,
.func = &do_at_command,
.argtable = &generic_at_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&at_command));
}
static void register_modem_lifecycle(void)
{
modem_args.param = arg_str1(NULL, NULL, "<mode>", "PPP or CMD");
modem_args.end = arg_end(1);
const esp_console_cmd_t modem_cmd = {
.command = "modem",
.help = "set modem mode",
.hint = NULL,
.func = &do_modem_lifecycle,
.argtable = &modem_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&modem_cmd));
}
static esp_console_repl_t *s_repl = NULL;
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// init the DTE
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.event_task_stack_size = 4096;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("internet");
dce_config.populate_command_list = true;
esp_netif_config_t ppp_netif_config = ESP_NETIF_DEFAULT_PPP();
esp_modem_dte_t *dte = esp_modem_dte_new(&dte_config);
s_dce = esp_modem_dce_new(&dce_config);
assert(s_dce != NULL);
esp_netif_t *esp_netif = esp_netif_new(&ppp_netif_config);
assert(esp_netif);
ESP_ERROR_CHECK(esp_modem_default_attach(dte, s_dce, esp_netif));
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
register_dce();
register_at_command();
register_modem_lifecycle();
modem_console_register_http();
modem_console_register_ping();
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(s_repl));
}

View File

@ -0,0 +1,141 @@
/* Ping handle 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 <stdio.h>
#include <string.h>
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
#include "ping/ping_sock.h"
#include "lwip/netdb.h"
static const char *TAG = "modem_console_ping";
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
ESP_LOGE(TAG, "From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
if (IP_IS_V4(&target_addr)) {
ESP_LOGI(TAG, "\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
ESP_LOGI(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
ESP_LOGI(TAG, "%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
esp_ping_delete_session(hdl);
}
static struct {
struct arg_dbl *timeout;
struct arg_int *count;
struct arg_str *host;
struct arg_end *end;
} ping_args;
static int do_ping_cmd(int argc, char **argv)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
if (ping_args.timeout->count > 0) {
config.timeout_ms = (uint32_t)(ping_args.timeout->dval[0] * 1000);
}
if (ping_args.count->count > 0) {
config.count = (uint32_t)(ping_args.count->ival[0]);
}
// parse IP address
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
/* convert domain name to IP address */
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
} else {
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
}
freeaddrinfo(res);
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
void modem_console_register_ping(void)
{
ping_args.timeout = arg_dbl0("W", "timeout", "<t>", "Time to wait for a response, in seconds");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {
.command = "ping",
.help = "send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &do_ping_cmd,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&ping_cmd));
}

View File

@ -0,0 +1,8 @@
# Override some defaults to enable PPP
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_PAP_SUPPORT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
# Do not enable IPV6 in dte<->dce link local
CONFIG_LWIP_PPP_ENABLE_IPV6=n
# Disable legacy API
CONFIG_MODEM_LEGACY_API=n

View File

@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pppos_client)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := pppos_client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,10 @@
# PPPoS simple client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows how to act as a MQTT client after the PPPoS channel created by using [ESP-MQTT](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/mqtt.html) APIs.
## How to use this example
See the README.md file in the upper level `pppos` directory for more information about the PPPoS examples.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "pppos_client_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,143 @@
menu "Example Configuration"
choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
help
Select modem device connected to the ESP DTE.
config EXAMPLE_MODEM_DEVICE_SIM800
bool "SIM800"
help
SIMCom SIM800L is a GSM/GPRS module.
It supports Quad-band 850/900/1800/1900MHz.
config EXAMPLE_MODEM_DEVICE_BG96
bool "BG96"
help
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
config EXAMPLE_MODEM_DEVICE_SIM7600
bool "SIM7600"
help
SIM7600 is Multi-Band LTE-TDD/LTE-FDD/HSPA+ and GSM/GPRS/EDGE module
endchoice
config EXAMPLE_MODEM_PPP_APN
string "Set MODEM APN"
default "internet"
help
Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_MODEM_LEGACY_API
bool "Use Modem legacy API"
default y
select MODEM_LEGACY_API
help
Set this to true to use backward compatible API to the original modem
component in example/protocol folder in IDFv4.2 and below
config EXAMPLE_MODEM_PPP_AUTH_USERNAME
string "Set username for authentication"
default "espressif"
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
help
Set username for PPP Authentication.
config EXAMPLE_MODEM_PPP_AUTH_PASSWORD
string "Set password for authentication"
default "esp32"
depends on !EXAMPLE_MODEM_PPP_AUTH_NONE
help
Set password for PPP Authentication.
config EXAMPLE_MODEM_PPP_AUTH_NONE
bool "Skip PPP authentication"
default n
help
Set to true for the PPP client to skip authentication
config EXAMPLE_SEND_MSG
bool "Short message (SMS)"
default n
help
Select this, the modem will send a short message before power off.
if EXAMPLE_SEND_MSG
config EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER
string "Peer Phone Number (with area code)"
default "+8610086"
help
Enter the peer phone number that you want to send message to.
endif
menu "UART Configuration"
config EXAMPLE_MODEM_UART_TX_PIN
int "TXD Pin Number"
default 25
range 0 31
help
Pin number of UART TX.
config EXAMPLE_MODEM_UART_RX_PIN
int "RXD Pin Number"
default 26
range 0 31
help
Pin number of UART RX.
config EXAMPLE_MODEM_UART_RTS_PIN
int "RTS Pin Number"
default 27
range 0 31
help
Pin number of UART RTS.
config EXAMPLE_MODEM_UART_CTS_PIN
int "CTS Pin Number"
default 23
range 0 31
help
Pin number of UART CTS.
config EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE
int "UART Event Task Stack Size"
range 2000 6000
default 2048
help
Stack size of UART event task.
config EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY
int "UART Event Task Priority"
range 3 22
default 5
help
Priority of UART event task.
config EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE
int "UART Event Queue Size"
range 10 40
default 30
help
Length of UART event queue.
config EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE
int "UART Pattern Queue Size"
range 10 40
default 20
help
Length of UART pattern queue.
config EXAMPLE_MODEM_UART_TX_BUFFER_SIZE
int "UART TX Buffer Size"
range 256 2048
default 512
help
Buffer size of UART TX buffer.
config EXAMPLE_MODEM_UART_RX_BUFFER_SIZE
int "UART RX Buffer Size"
range 256 2048
default 1024
help
Buffer size of UART RX buffer.
endmenu
endmenu

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,422 @@
/* 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/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_log.h"
#if defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
#include "sim800.h"
#include "bg96.h"
#include "sim7600.h"
#endif
#define BROKER_URL "mqtt://mqtt.eclipse.org"
static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int STOP_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
#if CONFIG_EXAMPLE_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.
*
*/
static esp_err_t example_default_handle(esp_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;
}
static esp_err_t example_handle_cmgs(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CMGS", strlen("+CMGS"))) {
err = ESP_OK;
}
return err;
}
#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 *user_dce, const char *phone_num, const char *text)
{
esp_modem_dce_t *dce = &user_dce->parent;
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 ESP_MODEM_EVENT_PPP_START:
ESP_LOGI(TAG, "Modem PPP Started");
break;
case ESP_MODEM_EVENT_PPP_STOP:
ESP_LOGI(TAG, "Modem PPP Stopped");
xEventGroupSetBits(event_group, STOP_BIT);
break;
case ESP_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;
}
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = event_data;
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
}
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, 0, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, 1, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config);
void app_main(void)
{
/* Init and register system/core components */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
event_group = xEventGroupCreate();
/* Configure the DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
/* setup UART specific configuration based on kconfig options */
dte_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
dte_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
dte_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
dte_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
dte_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
dte_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
dte_config.pattern_queue_size = CONFIG_EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE;
dte_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
dte_config.event_task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE;
dte_config.event_task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
dte_config.line_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
/* Configure the DCE */
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
/* Configure the PPP netif */
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
/* Run the modem demo app */
return modem_test_app(&dte_config, &dce_config,&netif_ppp_config);
}
#if !defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config)
{
/* create dte object */
esp_modem_dte_t *dte = esp_modem_dte_new(dte_config);
assert(dte != NULL);
/* create dce object */
#if CONFIG_EXAMPLE_MODEM_DEVICE_SIM800
dce_config->device = ESP_MODEM_DEVICE_SIM800;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96
dce_config->device = ESP_MODEM_DEVICE_BG96;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600
dce_config->device = ESP_MODEM_DEVICE_SIM7600;
#else
#error "Unsupported DCE"
#endif
esp_modem_dce_t *dce = esp_modem_dce_new(dce_config);
assert(dce != NULL);
/* create netif object */
esp_netif_t *esp_netif = esp_netif_new(ppp_config);
assert(esp_netif);
#if !defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_NONE) && (defined(CONFIG_LWIP_PPP_PAP_SUPPORT) || defined(CONFIG_LWIP_PPP_CHAP_SUPPORT))
#if CONFIG_LWIP_PPP_PAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#else
#error "Unsupported AUTH Negotiation"
#endif
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
/* attach the DCE, DTE, netif to initialize the modem */
ESP_ERROR_CHECK(esp_modem_default_attach(dte, dce, esp_netif));
while (1) {
ESP_ERROR_CHECK(esp_modem_default_start(dte));
/* Start PPP mode */
ESP_ERROR_CHECK(esp_modem_start_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_stop_ppp(dte));
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "Restart after 60 seconds");
vTaskDelay(pdMS_TO_TICKS(60000));
}
/* Default destroy all modem sub-units attached to it (DTE, DCE, netif) */
ESP_ERROR_CHECK(esp_modem_default_destroy(dte));
}
#else // defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config)
{
/* create dte object */
modem_dte_t *dte = esp_modem_dte_init(dte_config);
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
// Init netif object
esp_netif_t *esp_netif = esp_netif_new(ppp_config);
assert(esp_netif);
void *modem_netif_adapter = esp_modem_netif_setup(dte);
esp_modem_netif_set_default_handlers(modem_netif_adapter, esp_netif);
while (1) {
modem_dce_t *dce = NULL;
/* create dce object */
#if CONFIG_EXAMPLE_MODEM_DEVICE_SIM800
dce = sim800_init(dte);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96
dce = bg96_init(dte);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600
dce = sim7600_init(dte);
#else
#error "Unsupported DCE"
#endif
assert(dce != NULL);
ESP_ERROR_CHECK(dce->set_flow_ctrl(dce, ESP_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 PPPoS network parameters */
#if !defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_NONE) && (defined(CONFIG_LWIP_PPP_PAP_SUPPORT) || defined(CONFIG_LWIP_PPP_CHAP_SUPPORT))
#if CONFIG_LWIP_PPP_PAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#else
#error "Unsupported AUTH Negotiation"
#endif
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
/* attach the modem to the network interface */
esp_netif_attach(esp_netif, modem_netif_adapter);
/* 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_stop_ppp(dte));
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
#if CONFIG_EXAMPLE_SEND_MSG
const char *message = "Welcome to ESP32!";
ESP_ERROR_CHECK(example_send_message_text(dce, CONFIG_EXAMPLE_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_LOGI(TAG, "Restart after 60 seconds");
vTaskDelay(pdMS_TO_TICKS(60000));
}
/* Unregister events, destroy the netif adapter and destroy its esp-netif instance */
esp_modem_netif_clear_default_handlers(modem_netif_adapter);
esp_modem_netif_teardown(modem_netif_adapter);
esp_netif_destroy(esp_netif);
ESP_ERROR_CHECK(dte->deinit(dte));
}
#endif // CONFIG_EXAMPLE_MODEM_LEGACY_API

View File

@ -0,0 +1,7 @@
# Override some defaults to enable PPP
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y
CONFIG_LWIP_PPP_PAP_SUPPORT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
# Do not enable IPV6 in dte<->dce link local
CONFIG_LWIP_PPP_ENABLE_IPV6=n

View File

@ -0,0 +1,3 @@
version: "0.0.6"
name: esp_modem
description: esp modem

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2020 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.
@ -17,11 +17,15 @@
extern "C" {
#endif
#include "esp_modem_dce.h"
#include "esp_modem_dte.h"
#include "esp_event.h"
#include "driver/uart.h"
#include "esp_modem_compat.h"
/**
* @brief Fowrard declare DTE and DCE objects
*
*/
typedef struct esp_modem_dte esp_modem_dte_t;
typedef struct esp_modem_dce esp_modem_dce_t;
/**
* @brief Declare Event Base for ESP Modem
@ -39,6 +43,25 @@ typedef enum {
ESP_MODEM_EVENT_UNKNOWN = 4 /*!< ESP Modem Unknown Response */
} esp_modem_event_t;
/**
* @defgroup ESP_MODEM_DTE_TYPES DTE Types
* @brief Configuration and related types used to init and setup a new DTE object
*/
/** @addtogroup ESP_MODEM_DTE_TYPES
* @{
*/
/**
* @brief Modem flow control type
*
*/
typedef enum {
ESP_MODEM_FLOW_CONTROL_NONE = 0,
ESP_MODEM_FLOW_CONTROL_SW,
ESP_MODEM_FLOW_CONTROL_HW
} esp_modem_flow_ctrl_t;
/**
* @brief ESP Modem DTE Configuration
*
@ -48,7 +71,7 @@ typedef struct {
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 */
esp_modem_flow_ctrl_t flow_control; /*!< Flow control type */
uint32_t baud_rate; /*!< Communication baud rate */
int tx_io_num; /*!< TXD Pin Number */
int rx_io_num; /*!< RXD Pin Number */
@ -63,12 +86,6 @@ typedef struct {
int line_buffer_size; /*!< Line buffer size for command mode */
} esp_modem_dte_config_t;
/**
* @brief Type used for reception callback
*
*/
typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *context);
/**
* @brief ESP Modem DTE Default Configuration
*
@ -80,7 +97,7 @@ typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *contex
.stop_bits = UART_STOP_BITS_1, \
.parity = UART_PARITY_DISABLE, \
.baud_rate = 115200, \
.flow_control = MODEM_FLOW_CONTROL_NONE,\
.flow_control = ESP_MODEM_FLOW_CONTROL_NONE,\
.tx_io_num = 25, \
.rx_io_num = 26, \
.rts_io_num = 27, \
@ -94,6 +111,80 @@ typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *contex
.line_buffer_size = 512 \
}
/**
* @}
*/
/**
* @defgroup ESP_MODEM_DCE_TYPES DCE Types
* @brief Configuration and related types used to init and setup a new DCE object
*/
/** @addtogroup ESP_MODEM_DCE_TYPES
* @{
*/
/**
* @brief PDP context type used as an input parameter to esp_modem_dce_set_pdp_context
* also used as a part of configuration structure
*/
typedef struct esp_modem_dce_pdp_ctx_s {
size_t cid; /*!< PDP context identifier */
const char *type; /*!< Protocol type */
const char *apn; /*!< Modem APN (Access Point Name, a logical name to choose data network) */
} esp_modem_dce_pdp_ctx_t;
/**
* @brief Devices that the DCE will act as
*
*/
typedef enum esp_modem_dce_device_e {
ESP_MODEM_DEVICE_UNSPECIFIED,
ESP_MODEM_DEVICE_SIM800,
ESP_MODEM_DEVICE_SIM7600,
ESP_MODEM_DEVICE_BG96,
} esp_modem_dce_device_t;
/**
* @brief DCE's configuration structure
*/
typedef struct esp_modem_dce_config_s {
esp_modem_dce_pdp_ctx_t pdp_context; /*!< modem PDP context including APN */
bool populate_command_list; /*!< use command list interface: Setting this to true creates
a list of supported AT commands enabling sending
these commands, but will occupy data memory */
esp_modem_dce_device_t device; /*!< predefined device enum that the DCE will initialise as */
} esp_modem_dce_config_t;
/**
* @brief Default configuration of DCE unit of ESP-MODEM
*
*/
#define ESP_MODEM_DCE_DEFAULT_CONFIG(APN) \
{ \
.pdp_context = { \
.cid = 1, \
.type = "IP", \
.apn = APN }, \
.populate_command_list = false,\
.device = ESP_MODEM_DEVICE_UNSPECIFIED \
}
/**
* @}
*/
/**
* @defgroup ESP_MODEM_DTE_DCE DCE and DCE object init and setup API
* @brief Creating and init objects of DTE and DCE
*/
/** @addtogroup ESP_MODEM_DTE_DCE
* @{
*/
/**
* @brief Create and initialize Modem DTE object
*
@ -101,7 +192,42 @@ typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *contex
* @return modem_dte_t*
* - Modem DTE object
*/
modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config);
esp_modem_dte_t *esp_modem_dte_new(const esp_modem_dte_config_t *config);
/**
* @brief Create and initialize Modem DCE object
*
* @param config configuration of ESP Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
esp_modem_dce_t *esp_modem_dce_new(esp_modem_dce_config_t *config);
/**
* @brief Initialize the DCE object that has already been created
*
* This API is typically used to initialize extended DCE object,
* "sub-class" of esp_modem_dce_t
*
* @param config Configuration for DCE object
* @return
* - ESP_OK on success
* - ESP_FAIL on error (init issue, set specific command issue)
* - ESP_ERR_INVALID_ARG on invalid parameters
*/
esp_err_t esp_modem_dce_init(esp_modem_dce_t *dce, esp_modem_dce_config_t *config);
/**
* @}
*/
/**
* @defgroup ESP_MODEM_EVENTS Event handling API
*/
/** @addtogroup ESP_MODEM_EVENTS
* @{
*/
/**
* @brief Register event handler for ESP Modem event loop
@ -114,7 +240,7 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config);
* - 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_set_event_handler(modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args);
esp_err_t esp_modem_set_event_handler(esp_modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args);
/**
* @brief Unregister event handler for ESP Modem event loop
@ -125,7 +251,21 @@ esp_err_t esp_modem_set_event_handler(modem_dte_t *dte, esp_event_handler_t hand
* - 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);
esp_err_t esp_modem_remove_event_handler(esp_modem_dte_t *dte, esp_event_handler_t handler);
/**
* @}
*/
/**
* @defgroup ESP_MODEM_LIFECYCLE Modem lifecycle API
* @brief Basic modem API to start/stop the PPP mode
*/
/** @addtogroup ESP_MODEM_LIFECYCLE
* @{
*/
/**
* @brief Setup PPP Session
@ -135,7 +275,7 @@ esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t h
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_start_ppp(modem_dte_t *dte);
esp_err_t esp_modem_start_ppp(esp_modem_dte_t *dte);
/**
* @brief Exit PPP Session
@ -145,29 +285,49 @@ esp_err_t esp_modem_start_ppp(modem_dte_t *dte);
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_stop_ppp(modem_dte_t *dte);
esp_err_t esp_modem_stop_ppp(esp_modem_dte_t *dte);
/**
* @brief Setup on reception callback
* @brief Basic start of the modem. This API performs default dce's start_up() function
*
* @param dte ESP Modem DTE object
* @param receive_cb Function pointer to the reception callback
* @param receive_cb_ctx Contextual pointer to be passed to the reception callback
*
* @return ESP_OK on success
* @param dte Modem DTE Object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_INVALID_ARG on invalid arguments
*/
esp_err_t esp_modem_set_rx_cb(modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx);
esp_err_t esp_modem_default_start(esp_modem_dte_t *dte);
/**
* @brief Notify the modem, that ppp netif has closed
* @brief Basic attach operation of modem sub-elements
*
* @note This API should only be used internally by the modem-netif layer
* This API binds the supplied DCE and netif to the modem's DTE and initializes the modem
*
* @param dte ESP Modem DTE object
*
* @return ESP_OK on success
* @param dte Modem DTE Object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_notify_ppp_netif_closed(modem_dte_t *dte);
esp_err_t esp_modem_default_attach(esp_modem_dte_t *dte, esp_modem_dce_t *dce, esp_netif_t* ppp_netif);
/**
* @brief Basic destroy operation of the modem DTE and all the sub-elements bound to it
*
* This API deletes the DCE, modem netif adapter as well as the esp_netif supplied to
* esp_modem_default_attach(). Then it deletes the DTE itself.
*
* @param dte Modem DTE Object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_INVALID_ARG on invalid arguments
*/
esp_err_t esp_modem_default_destroy(esp_modem_dte_t *dte);
/**
* @}
*/
#ifdef __cplusplus
}

View File

@ -1,62 +0,0 @@
// Copyright 2019 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 "lwip/ip.h"
/**
* @brief ESP Modem Event backward compatible version
*/
typedef enum {
MODEM_EVENT_PPP_START = 0x100,
MODEM_EVENT_PPP_CONNECT = 0x101,
MODEM_EVENT_PPP_DISCONNECT = 0x102,
MODEM_EVENT_PPP_STOP = 0x103,
MODEM_EVENT_UNKNOWN = 0x104,
} esp_modem_compat_event_t;
/**
* @brief PPPoS Client IP Information backward compatible version
*
*/
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 Backward compatible version of esp_modem_set_event_handler()
*/
esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args) __attribute__ ((deprecated));
/**
* @brief Backward compatible version of creating esp-netif(PPP) and attaching to esp_modem_start_ppp()
*/
esp_err_t esp_modem_setup_ppp(modem_dte_t *dte) __attribute__ ((deprecated));
/**
* @brief Backward compatible version of deleting esp-netif and esp_modem_stop_ppp()
*/
esp_err_t esp_modem_exit_ppp(modem_dte_t *dte) __attribute__ ((deprecated));
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -19,81 +19,218 @@ extern "C" {
#include "esp_types.h"
#include "esp_err.h"
#include "esp_modem.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
*
* @brief Forward declaration of the command list object, which (if enabled) is used
* to populate a command palette and access commands by its symbolic name
*/
#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 (5000) /*!< 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 */
struct esp_modem_dce_cmd_list;
/**
* @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;
ESP_MODEM_STATE_PROCESSING, /*!< In processing */
ESP_MODEM_STATE_SUCCESS, /*!< Process successfully */
ESP_MODEM_STATE_FAIL /*!< Process failed */
} esp_modem_state_t;
/**
* @brief Generic command type used in DCE unit
*/
typedef esp_err_t (*dce_command_t)(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Type of line handlers called fro DTE upon line response reception
*/
typedef esp_err_t (*esp_modem_dce_handle_line_t)(esp_modem_dce_t *dce, const char *line);
/**
* @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 */
struct esp_modem_dce {
esp_modem_state_t state; /*!< Modem working state */
esp_modem_mode_t mode; /*!< Working mode */
esp_modem_dte_t *dte; /*!< DTE which connect to DCE */
struct esp_modem_dce_cmd_list *dce_cmd_list;
esp_modem_dce_config_t config;
esp_modem_dce_handle_line_t handle_line; /*!< Handle line strategy */
void *handle_line_ctx; /*!< DCE context reserved for handle_line
processing */
// higher level actions DCE unit can take
esp_err_t (*set_working_mode)(esp_modem_dce_t *dce, esp_modem_mode_t mode); /*!< Set working mode */
esp_err_t (*deinit)(esp_modem_dce_t *dce); /*!< Destroys the DCE */
esp_err_t (*start_up)(esp_modem_dce_t *dce); /*!< Start-up sequence */
// list of essential commands for esp-modem basic work
dce_command_t hang_up; /*!< generic command for hang-up */
dce_command_t set_pdp_context; /*!< generic command for pdp context */
dce_command_t set_data_mode; /*!< generic command for data mode */
dce_command_t resume_data_mode; /*!< generic command to resume already dialed data mode */
dce_command_t set_command_mode; /*!< generic command for command mode */
dce_command_t set_echo; /*!< generic command for echo mode */
dce_command_t sync; /*!< generic command for sync */
dce_command_t set_flow_ctrl; /*!< generic command for flow-ctrl */
dce_command_t store_profile; /*!< generic command for store-profile */
};
// DCE commands building blocks
/**
* @brief Sending generic command to DCE
*
* @param[in] dce Modem DCE object
* @param[in] command String command
* @param[in] timeout Command timeout in ms
* @param[in] handle_line Function ptr which processes the command response
* @param[in] ctx Function ptr context
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_generic_command(esp_modem_dce_t *dce, const char * command, uint32_t timeout, esp_modem_dce_handle_line_t handle_line, void *ctx);
/**
* @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
*/
esp_err_t esp_modem_process_command_done(esp_modem_dce_t *dce, esp_modem_state_t state);
/**
* @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(esp_modem_dce_t *dce, const char *line);
// DCE higher level commands
/**
* @brief Set Working Mode
*
* @param dce Modem DCE object
* @param mode working mode
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_set_working_mode(esp_modem_dce_t *dce, esp_modem_mode_t mode);
/**
* @brief Default start-up sequence, which sets the modem into an operational mode
*
* @param dce Modem DCE object
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_default_start_up(esp_modem_dce_t *dce);
/**
* @brief Destroys the DCE
*
* @param dce Modem DCE object
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_default_destroy(esp_modem_dce_t *dce);
/**
* @brief Initializes the DCE
*
* @param dce Modem DCE object
* @param config DCE configuration structure
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_default_init(esp_modem_dce_t *dce, esp_modem_dce_config_t* config);
/**
* @brief Sets the DCE parameters. This API updates runtime DCE config parameters,
* typically used to update PDP context data.
*
* @param dce Modem DCE object
* @param config DCE configuration structure
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_set_params(esp_modem_dce_t *dce, esp_modem_dce_config_t* config);
// list command operations
/**
* @brief Executes a specific command from the list
*
* @param dce Modem DCE object
* @param command Symbolic name of the command to execute
* @param param Generic parameter to the command
* @param result Generic output parameter
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_command_list_run(esp_modem_dce_t *dce, const char * command, void * param, void* result);
/**
* @brief Deinitialize the command list
*
* @param dce Modem DCE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_command_list_deinit(esp_modem_dce_t *dce);
/**
* @brief Initializes default command list with predefined command palette
*
* @param dce Modem DCE object
*
* @return ESP_OK on success, ESP_FAIL on error
*/
esp_err_t esp_modem_set_default_command_list(esp_modem_dce_t *dce);
/**
* @brief Add or set specific command to the command list
*
* @param dce Modem DCE object
* @param command_id Command symbolic name
* @param command Generic command function pointer
*
* @return ESP_OK on success, ESP_FAIL on error
*/
esp_err_t esp_modem_command_list_set_cmd(esp_modem_dce_t *dce, const char * command_id, dce_command_t command);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,359 @@
// Copyright 2020 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 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 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_RESET (60000) /*!< Timeout value for reset command */
#define MODEM_COMMAND_TIMEOUT_MODE_CHANGE (5000) /*!< Timeout value for changing working mode */
#define MODEM_COMMAND_TIMEOUT_POWEROFF (1000) /*!< Timeout value for power down */
/**
* @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 Synchronization
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_sync(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Enable or not echo mode of DCE
*
* @param dce Modem DCE object
* @param[in] param bool casted to (void*): true to enable echo mode, false to disable echo mode
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_echo(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Store current parameter setting in the user profile
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_store_profile(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Set flow control mode of DCE in data mode
*
* @param[in] dce Modem DCE object
* @param[in] param esp_modem_flow_ctrl_t casted to (void*): flow control mode
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_flow_ctrl(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Define PDP context
*
* @param[in] dce Modem DCE object
* @param[in] param esp_modem_dce_pdp_ctx_t type defining PDP context
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_pdp_context(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Hang up
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_hang_up(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Signal strength structure used as a result to esp_modem_dce_get_signal_quality() API
*/
typedef struct esp_modem_dce_csq_ctx_s {
int rssi; //!< Signal strength indication
int ber; //!< Channel bit error rate
} esp_modem_dce_csq_ctx_t;
/**
* @brief Get signal quality
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result esp_modem_dce_csq_ctx_t type returning rssi and ber values
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_signal_quality(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Voltage status structure used as a result to esp_modem_dce_get_battery_status() API
*/
typedef struct esp_modem_dce_cbc_ctx_s {
int battery_status; //!< current status in mV
int bcs; //!< charge status (-1-Not available, 0-Not charging, 1-Charging, 2-Charging done)
int bcl; //!< 1-100% battery capacity, -1-Not available
} esp_modem_dce_cbc_ctx_t;
/**
* @brief Get battery status
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result esp_modem_dce_cbc_ctx_t type returning battery status and other fields if available
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_battery_status(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Get DCE module IMEI number
*
* @param[in] dce Modem DCE object
* @param[in] param size_t of output string length (casted to void*), max size the resultant string
* @param[out] result pointer to the string where the resultant IMEI number gets copied (if size param fits)
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_imei_number(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Get DCE module IMSI number
*
* @param[in] dce Modem DCE object
* @param[in] param size_t of output string length (casted to void*), max size the resultant string
* @param[out] result pointer to the string where the resultant IMSI number gets copied (if size param fits)
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_imsi_number(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Get DCE module name
*
* @param[in] dce Modem DCE object
* @param[in] param size_t of output string length (casted to void*), max size the resultant string
* @param[out] result pointer to the string where the resultant module name gets copied (if size param fits)
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_module_name(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Get operator name
*
* @param[in] dce Modem DCE object
* @param[in] param size_t of output string length (casted to void*), max size the resultant string
* @param[out] result pointer to the string where the resultant operator name gets copied (if size param fits)
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_get_operator_name(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Switch to data mode
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_data_mode(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Resume the data mode when PPP has already been started, but switched back to command
* mode (typically using the `+++` PPP escape sequence)
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_resume_data_mode(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Switch to command mode
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_command_mode(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Power-down the module
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_power_down(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Reset the module
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_reset(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Checks if the module waits for entering the PIN
*
* @param[in] dce Modem DCE object
* @param[in] param None
* @param[out] result pointer to bool indicating READY if set to true
* or the module is waiting for PIN if set to false
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_read_pin(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Enters the PIN number
*
* @param[in] dce Modem DCE object
* @param[in] param 4 character string pointer to the PIN
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_pin(esp_modem_dce_t *dce, void *param, void *result);
/**
* @brief Sets the DCE to temporarily use the baudrate specified
*
* @param[in] dce Modem DCE object
* @param[in] param string pointer to char representation of the baudrate
* @param[out] result None
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
* - ESP_ERR_TIMEOUT if timeout while waiting for expected response
*/
esp_err_t esp_modem_dce_set_baud_temp(esp_modem_dce_t *dce, void *param, void *result);

View File

@ -1,131 +0,0 @@
// 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

View File

@ -1,4 +1,4 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2018-2020 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.
@ -21,46 +21,81 @@ extern "C" {
#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_TRANSITION_MODE /*!< Transition Mode betwen data and command mode indicating that
ESP_MODEM_COMMAND_MODE = 0, /*!< Command Mode */
ESP_MODEM_PPP_MODE, /*!< PPP Mode */
ESP_MODEM_TRANSITION_MODE /*!< Transition Mode between data and command mode indicating that
the modem is not yet ready for sending commands nor data */
} 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;
} esp_modem_mode_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,
struct esp_modem_dte {
esp_modem_flow_ctrl_t flow_ctrl; /*!< Flow control of DTE */
esp_modem_dce_t *dce; /*!< DCE which connected to the DTE */
struct esp_modem_netif_driver_s *netif_adapter;
esp_err_t (*send_cmd)(esp_modem_dte_t *dte, const char *command, uint32_t timeout); /*!< Send command to DCE */
int (*send_data)(esp_modem_dte_t *dte, const char *data, uint32_t length); /*!< Send data to DCE */
esp_err_t (*send_wait)(esp_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 */
esp_err_t (*change_mode)(esp_modem_dte_t *dte, esp_modem_mode_t new_mode); /*!< Changing working mode */
esp_err_t (*process_cmd_done)(esp_modem_dte_t *dte); /*!< Callback when DCE process command done */
esp_err_t (*deinit)(esp_modem_dte_t *dte); /*!< Deinitialize */
};
/**
* @brief Type used for reception callback
*
*/
typedef esp_err_t (*esp_modem_on_receive)(void *buffer, size_t len, void *context);
/**
* @brief Setup on reception callback
*
* @param dte ESP Modem DTE object
* @param receive_cb Function pointer to the reception callback
* @param receive_cb_ctx Contextual pointer to be passed to the reception callback
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_set_rx_cb(esp_modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx);
/**
* @brief Notify the modem, that ppp netif has closed
*
* @note This API should only be used internally by the modem-netif layer
*
* @param dte ESP Modem DTE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_notify_ppp_netif_closed(esp_modem_dte_t *dte);
/**
* @brief Notify the modem, that all the modem units (DTE, DCE, PPP) has
* been properly initialized and DTE loop can safely start
*
* @param dte ESP Modem DTE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_notify_initialized(esp_modem_dte_t *dte);
/**
* @brief Configure runtime parameters for the DTE. Currently supports only the baud rate to be set
*
* @param dte ESP Modem DTE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_dte_set_params(esp_modem_dte_t *dte, const esp_modem_dte_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2020 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.
@ -17,6 +17,21 @@
extern "C" {
#endif
/**
* @brief modem netif adapter type
*
*/
typedef struct esp_modem_netif_driver_s esp_modem_netif_driver_t;
/**
* @defgroup ESP_MODEM_NETIF Modem netif adapter API
* @brief network interface adapter for esp-modem
*/
/** @addtogroup ESP_MODEM_NETIF
* @{
*/
/**
* @brief Creates handle to esp_modem used as an esp-netif driver
*
@ -24,21 +39,22 @@ extern "C" {
*
* @return opaque pointer to esp-modem IO driver used to attach to esp-netif
*/
void *esp_modem_netif_setup(modem_dte_t *dte);
esp_modem_netif_driver_t *esp_modem_netif_new(esp_modem_dte_t *dte);
/**
* @brief Destroys the esp-netif driver handle
* @brief Destroys the esp-netif driver handle as well as the internal netif
* object attached to it
*
* @param h pointer to the esp-netif adapter for esp-modem
*/
void esp_modem_netif_teardown(void *h);
void esp_modem_netif_destroy(esp_modem_netif_driver_t *h);
/**
* @brief Clears default handlers for esp-modem lifecycle
*
* @param h pointer to the esp-netif adapter for esp-modem
*/
esp_err_t esp_modem_netif_clear_default_handlers(void *h);
esp_err_t esp_modem_netif_clear_default_handlers(esp_modem_netif_driver_t *h);
/**
* @brief Setups default handlers for esp-modem lifecycle
@ -46,7 +62,41 @@ esp_err_t esp_modem_netif_clear_default_handlers(void *h);
* @param h pointer to the esp-netif adapter for esp-modem
* @param esp_netif pointer corresponding esp-netif instance
*/
esp_err_t esp_modem_netif_set_default_handlers(void *h, esp_netif_t * esp_netif);
esp_err_t esp_modem_netif_set_default_handlers(esp_modem_netif_driver_t *h, esp_netif_t * esp_netif);
/**
* @}
*/
/**
* @defgroup ESP_MODEM_NETIF_LEGACY Modem netif adapter legacy API
* @brief Legacy API for modem netif
*/
/** @addtogroup ESP_MODEM_NETIF_LEGACY
* @{
*/
/**
* @brief Destroys the esp-netif driver handle the same way
* as esp_modem_netif_destroy()
*
* @note This API is only provided for legacy API
*/
void esp_modem_netif_teardown(esp_modem_netif_driver_t *h);
/**
* @brief The same as `esp_modem_netif_new()`, but autostarts the netif
* on esp_netif_attach().
*
* @note This API is only provided for legacy API
*/
esp_modem_netif_driver_t *esp_modem_netif_setup(esp_modem_dte_t *dte);
/**
* @}
*/
#ifdef __cplusplus
}

View File

@ -0,0 +1,89 @@
// Copyright 2020 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_err.h"
#include "esp_modem_dce.h"
/**
* @brief Utility macro to define a retry method
*
*/
#define DEFINE_RETRY_CMD(name, retry, super_type) \
esp_err_t name(esp_modem_dce_t *dce, void *param, void *result) \
{ \
super_type *super = __containerof(dce, super_type, parent); \
return super->retry->run(super->retry, param, result); \
}
/**
* @brief GPIO helper object used to pull modem IOs
*
*/
typedef struct esp_modem_gpio_s {
int gpio_num;
int inactive_level;
int active_width_ms;
int inactive_width_ms;
void (*pulse)(struct esp_modem_gpio_s *pin);
void (*pulse_special)(struct esp_modem_gpio_s *pin, int active_width_ms, int inactive_width_ms);
void (*destroy)(struct esp_modem_gpio_s *pin);
} esp_modem_recov_gpio_t;
/**
* @brief Recovery helper object used to resend commands if failed or timeouted
*
*/
typedef struct esp_modem_retry_s esp_modem_recov_resend_t;
/**
* @brief User recovery function to be called upon modem command failed
*
*/
typedef esp_err_t (*esp_modem_retry_fn_t)(esp_modem_recov_resend_t *retry_cmd, esp_err_t current_err, int timeouts, int errors);
/**
* @brief Recovery helper object
*
*/
struct esp_modem_retry_s {
const char *command;
esp_err_t (*run)(struct esp_modem_retry_s *retry, void *param, void *result);
dce_command_t orig_cmd;
esp_modem_retry_fn_t recover;
esp_modem_dce_t *dce;
int retries_after_timeout; //!< Retry strategy: numbers of resending the same command on timeout
int retries_after_error; //!< Retry strategy: numbers of resending the same command on error
void (*destroy)(struct esp_modem_retry_s *this_recov);
};
/**
* @brief Create new resend object
*
*/
esp_modem_recov_resend_t *esp_modem_recov_resend_new(esp_modem_dce_t *dce, dce_command_t orig_cmd, esp_modem_retry_fn_t recover, int max_timeouts, int max_errors);
/**
* @brief Create new gpio object
*
*/
esp_modem_recov_gpio_t *esp_modem_recov_gpio_new(int gpio_num, int inactive_level, int active_width_ms, int inactive_width_ms);

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -13,21 +13,4 @@
// 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
#include "esp_modem_compat.h"

View File

@ -0,0 +1,71 @@
// Copyright 2020 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.h"
#include "esp_modem_dte.h"
#include "esp_modem_dce.h"
/**
* @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 */
#define esp_modem_dte_init esp_modem_dte_new
typedef esp_modem_dte_t modem_dte_t;
typedef struct modem_dce modem_dce_t;
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 */
esp_modem_state_t state; /*!< Modem working state */
esp_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, esp_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, esp_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 */
esp_modem_dce_t parent;
};
modem_dce_t *sim800_init(modem_dte_t *dte);
modem_dce_t *sim7600_init(modem_dte_t *dte);
modem_dce_t *bg96_init(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -13,21 +13,4 @@
// 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 SIM7600 object
*
* @param dte Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
modem_dce_t *sim7600_init(modem_dte_t *dte);
#ifdef __cplusplus
}
#endif
#include "esp_modem_compat.h"

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -13,21 +13,4 @@
// 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
#include "esp_modem_compat.h"

View File

@ -0,0 +1,62 @@
// Copyright 2020 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.h"
/**
* @brief Finds the command par its symbolic name
*
* @param dce Modem DCE object
* @param command Symbolic name of the command
*
* @return Function pointer to the
*/
dce_command_t esp_modem_dce_find_command(esp_modem_dce_t *dce, const char *command);
/**
* @brief Delete specific command from the list
*
* @param dce Modem DCE object
* @param command Symbolic name of the command to delete
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_dce_delete_command(esp_modem_dce_t *dce, const char *command_id);
/**
* @brief Deletes all commands in the list
*
* @param dce Modem DCE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_dce_delete_all_commands(esp_modem_dce_t *dce);
/**
* @brief Creates internal structure for holding the command list
*
* @return Pointer to the command list struct on success, NULL otherwise
*/
struct esp_modem_dce_cmd_list *esp_modem_command_list_create(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,51 @@
// Copyright 2020 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.h"
/**
* @brief Specific init of SIM800 device
*
* @param dce Modem DCE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_sim800_specific_init(esp_modem_dce_t *dce);
/**
* @brief Specific init of SIM7600 device
*
* @param dce Modem DCE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_sim7600_specific_init(esp_modem_dce_t *dce);
/**
* @brief Specific init of BG96 device
*
* @param dce Modem DCE object
*
* @return ESP_OK on success
*/
esp_err_t esp_modem_bg96_specific_init(esp_modem_dce_t *dce);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,57 @@
// Copyright 2020 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_log.h"
#include "esp_modem.h"
#include "esp_modem_dte.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
/**
* @brief Main lifecycle states of the esp-modem
*
* The bits below are used to indicate events in process_group
* field of the esp_modem_dte_internal_t
*/
#define ESP_MODEM_START_BIT BIT0
#define ESP_MODEM_COMMAND_BIT BIT1
#define ESP_MODEM_STOP_PPP_BIT BIT2
#define ESP_MODEM_STOP_BIT BIT3
/**
* @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 */
EventGroupHandle_t process_group; /*!< Event group used for indicating DTE state changes */
esp_modem_dte_t parent; /*!< DTE interface that should extend */
esp_modem_on_receive receive_cb; /*!< ptr to data reception */
void *receive_cb_ctx; /*!< ptr to rx fn context data */
int line_buffer_size; /*!< line buffer size in command mode */
int pattern_queue_size; /*!< UART pattern queue size */
} esp_modem_dte_internal_t;
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -13,25 +13,36 @@
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_log.h"
#include "esp_modem.h"
/**
* @brief Macro defined for error checking
*
*/
#define DCE_CHECK(a, str, goto_tag, ...) \
* @brief Macro defined for error checking
*
*/
#define ESP_MODEM_ERR_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ESP_LOGE(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 common modem delay function
*
*/
static inline void esp_modem_wait_ms(size_t time)
{
vTaskDelay(pdMS_TO_TICKS(time));
}
#ifdef __cplusplus
}
#endif

View File

@ -1,472 +0,0 @@
// 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"
#include "bg96_private.h"
#define MODEM_RESULT_CODE_POWERDOWN "POWERED DOWN"
static const char *DCE_TAG = "bg96";
/**
* @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:
vTaskDelay(pdMS_TO_TICKS(1000)); // spec: 1s delay for the modem to recognize the escape sequence
dce->handle_line = bg96_handle_exit_data_mode;
if (dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) != ESP_OK) {
// "+++" Could fail if we are already in the command mode.
// in that case we ignore the timout and re-sync the modem
ESP_LOGI(DCE_TAG, "Sending \"+++\" command failed");
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);
} else {
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);
if (dce->state != MODEM_STATE_SUCCESS) {
// Initiate PPP mode could fail, if we've already "dialed" the data call before.
// in that case we retry with "ATO" to just resume the data mode
ESP_LOGD(DCE_TAG, "enter ppp mode failed, retry with ATO");
dce->handle_line = bg96_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATO\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;
}

25
esp_modem/src/esp_bg96.c Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2015-2020 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 "esp_modem_internal.h"
static const char *TAG = "bg96";
esp_err_t esp_modem_bg96_specific_init(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(dce, "failed to specific init with zero dce", err_params);
// BG96 specifics is the same as the default DCE, as of now
return ESP_OK;
err_params:
return ESP_ERR_INVALID_ARG;
}

View File

@ -12,524 +12,41 @@
// 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 "esp_modem.h"
#include "esp_modem_dce.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_modem_internal.h"
#include "esp_modem_dte_internal.h"
#include "esp_modem_device_specific_dce.h"
#include "esp_modem_netif.h"
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
#define MIN_PATTERN_INTERVAL (9)
#define MIN_POST_IDLE (0)
#define MIN_PRE_IDLE (0)
/**
* @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)
static const char *TAG = "esp-modem";
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 */
SemaphoreHandle_t exit_sem; /*!< Semaphore used for indicating PPP mode has stopped */
modem_dte_t parent; /*!< DTE interface that should extend */
esp_modem_on_receive receive_cb; /*!< ptr to data reception */
void *receive_cb_ctx; /*!< ptr to rx fn context data */
int line_buffer_size; /*!< line buffer size in commnad mode */
int pattern_queue_size; /*!< UART pattern queue size */
} esp_modem_dte_t;
/**
* @brief Returns true if the supplied string contains only CR or LF
*
* @param str string to check
* @param len length of string
*/
static inline bool is_only_cr_lf(const char *str, uint32_t len)
esp_err_t esp_modem_set_event_handler(esp_modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args)
{
for (int i=0; i<len; ++i) {
if (str[i] != '\r' && str[i] != '\n') {
return false;
}
}
return true;
}
esp_err_t esp_modem_set_rx_cb(modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
esp_dte->receive_cb_ctx = receive_cb_ctx;
esp_dte->receive_cb = receive_cb;
return ESP_OK;
}
/**
* @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);
size_t len = strlen(line);
/* Skip pure "\r\n" lines */
if (len > 2 && !is_only_cr_lf(line, len)) {
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 ESP_MODEM_EVENT_UNKNOWN signal to event loop */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_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_dte->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_dte->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 {
size_t length = 0;
uart_get_buffered_data_len(esp_dte->uart_port, &length);
ESP_LOGW(MODEM_TAG, "Pattern not found in the pattern queue, uart data length = %d", length);
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
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);
if (esp_dte->parent.dce->mode != MODEM_PPP_MODE) {
// Check if matches the pattern to process the data as pattern
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
if (pos > -1) {
esp_handle_uart_pattern(esp_dte);
return;
}
// Read the data and process it using `handle_line` logic
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
esp_dte->buffer[length] = '\0';
if (esp_dte->parent.dce->handle_line) {
/* Send new line to handle if handler registered */
esp_dte_handle_line(esp_dte);
}
return;
}
length = MIN(esp_dte->line_buffer_size, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
/* pass the input data to configured callback */
if (length) {
esp_dte->receive_cb(esp_dte->buffer, length, esp_dte->receive_cb_ctx);
}
}
/**
* @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);
if (esp_dte->parent.dce->mode == MODEM_TRANSITION_MODE) {
ESP_LOGD(MODEM_TAG, "Not sending data in transition mode");
return -1;
}
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_baud_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_baud_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_mode_t current_mode = dce->mode;
MODEM_CHECK(current_mode != new_mode, "already in mode: %d", err, new_mode);
dce->mode = MODEM_TRANSITION_MODE; // mode switching will be finished in set_working_mode() on success
// (or restored on failure)
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_restore_mode, new_mode);
uart_disable_pattern_det_intr(esp_dte->uart_port);
uart_enable_rx_intr(esp_dte->uart_port);
break;
case MODEM_COMMAND_MODE:
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
uart_disable_rx_intr(esp_dte->uart_port);
uart_flush(esp_dte->uart_port);
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
uart_pattern_queue_reset(esp_dte->uart_port, esp_dte->pattern_queue_size);
break;
default:
break;
}
return ESP_OK;
err_restore_mode:
dce->mode = current_mode;
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 semaphores */
vSemaphoreDelete(esp_dte->process_sem);
vSemaphoreDelete(esp_dte->exit_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->line_buffer_size = config->line_buffer_size;
esp_dte->buffer = calloc(1, config->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,
.source_clk = UART_SCLK_REF_TICK,
.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->tx_io_num, config->rx_io_num,
config->rts_io_num, config->cts_io_num);
} else {
res = uart_set_pin(esp_dte->uart_port, config->tx_io_num, config->rx_io_num,
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->rx_buffer_size, config->tx_buffer_size,
config->event_queue_size, &(esp_dte->event_queue), 0);
MODEM_CHECK(res == ESP_OK, "install uart driver failed", err_uart_config);
res = uart_set_rx_timeout(esp_dte->uart_port, 1);
MODEM_CHECK(res == ESP_OK, "set rx timeout failed", err_uart_config);
/* Set pattern interrupt, used to detect the end of a line. */
res = uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
/* Set pattern queue size */
esp_dte->pattern_queue_size = config->pattern_queue_size;
res |= uart_pattern_queue_reset(esp_dte->uart_port, config->pattern_queue_size);
/* Starting in command mode -> explicitly disable RX interrupt */
uart_disable_rx_intr(esp_dte->uart_port);
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_sem1);
esp_dte->exit_sem = xSemaphoreCreateBinary();
MODEM_CHECK(esp_dte->exit_sem, "create exit semaphore failed", err_sem);
/* Create UART Event task */
BaseType_t ret = xTaskCreate(uart_event_task_entry, //Task Entry
"uart_event", //Task Name
config->event_task_stack_size, //Task Stack Size(Bytes)
esp_dte, //Task Parameter
config->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->exit_sem);
err_sem:
vSemaphoreDelete(esp_dte->process_sem);
err_sem1:
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_set_event_handler(modem_dte_t *dte, esp_event_handler_t handler, int32_t event_id, void *handler_args)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
return esp_event_handler_register_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, event_id, handler, handler_args);
}
esp_err_t esp_modem_remove_event_handler(modem_dte_t *dte, esp_event_handler_t handler)
esp_err_t esp_modem_remove_event_handler(esp_modem_dte_t *dte, esp_event_handler_t handler)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
return esp_event_handler_unregister_with(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_EVENT_ANY_ID, handler);
}
esp_err_t esp_modem_start_ppp(modem_dte_t *dte)
esp_err_t esp_modem_start_ppp(esp_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_EXAMPLE_COMPONENT_MODEM_APN) == ESP_OK, "set MODEM APN failed", err);
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
/* Enter PPP mode */
MODEM_CHECK(dte->change_mode(dte, MODEM_PPP_MODE) == ESP_OK, "enter ppp mode failed", err);
ESP_MODEM_ERR_CHECK(dte->change_mode(dte, ESP_MODEM_PPP_MODE) == ESP_OK, "enter ppp mode failed", err);
/* post PPP mode started event */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_MODEM_EVENT_PPP_START, NULL, 0, 0);
@ -538,29 +55,131 @@ err:
return ESP_FAIL;
}
esp_err_t esp_modem_stop_ppp(modem_dte_t *dte)
esp_err_t esp_modem_stop_ppp(esp_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);
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
/* Enter command mode */
MODEM_CHECK(dte->change_mode(dte, MODEM_COMMAND_MODE) == ESP_OK, "enter command mode failed", err);
ESP_MODEM_ERR_CHECK(dte->change_mode(dte, ESP_MODEM_COMMAND_MODE) == ESP_OK, "enter command mode failed", err);
/* post PPP mode stopped event */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_MODEM_EVENT_PPP_STOP, NULL, 0, 0);
/* Hang up */
MODEM_CHECK(dce->hang_up(dce) == ESP_OK, "hang up failed", err);
/* wait for the PPP mode to exit gracefully */
if (xSemaphoreTake(esp_dte->exit_sem, pdMS_TO_TICKS(20000)) != pdTRUE) {
ESP_LOGW(MODEM_TAG, "Failed to exit the PPP mode gracefully");
EventBits_t bits = xEventGroupWaitBits(esp_dte->process_group, ESP_MODEM_STOP_PPP_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(20000));
if (!(bits & ESP_MODEM_STOP_PPP_BIT)) {
ESP_LOGW(TAG, "Failed to exit the PPP mode gracefully");
}
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_notify_ppp_netif_closed(modem_dte_t *dte)
esp_err_t esp_modem_notify_ppp_netif_closed(esp_modem_dte_t *dte)
{
esp_modem_dte_t *esp_dte = __containerof(dte, esp_modem_dte_t, parent);
return xSemaphoreGive(esp_dte->exit_sem) == pdTRUE ? ESP_OK : ESP_FAIL;
}
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
EventBits_t bits = xEventGroupSetBits(esp_dte->process_group, ESP_MODEM_STOP_PPP_BIT);
return bits & ESP_MODEM_STOP_BIT ? ESP_FAIL : ESP_OK; // set error if the group indicated MODEM_STOP condition
}
esp_err_t esp_modem_notify_initialized(esp_modem_dte_t *dte)
{
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
EventBits_t bits = xEventGroupSetBits(esp_dte->process_group, ESP_MODEM_START_BIT);
return bits & ESP_MODEM_START_BIT ? ESP_OK : ESP_FAIL; // START bit should be set (since it's not auto-cleared)
// report error otherwise
}
esp_err_t esp_modem_default_destroy(esp_modem_dte_t *dte)
{
ESP_MODEM_ERR_CHECK(dte, "Cannot destroy NULL dte", err_params);
esp_modem_netif_driver_t *netif_adapter = dte->netif_adapter;
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce && netif_adapter, "Cannot destroy dce or netif_adapter", err_params);
ESP_MODEM_ERR_CHECK(esp_modem_netif_clear_default_handlers(netif_adapter) == ESP_OK,
"modem_netif failed to clread handlers", err);
esp_modem_netif_destroy(netif_adapter);
dte->netif_adapter = NULL;
ESP_MODEM_ERR_CHECK(dce->deinit(dce) == ESP_OK, "failed to deinit dce", err);
dte->dce = NULL;
ESP_MODEM_ERR_CHECK(dte->deinit(dte) == ESP_OK, "failed to deinit ", err);
return ESP_OK;
err:
return ESP_FAIL;
err_params:
return ESP_ERR_INVALID_ARG;
}
esp_err_t esp_modem_default_start(esp_modem_dte_t *dte)
{
ESP_MODEM_ERR_CHECK(dte, "failed to start zero DTE", err_params);
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce, "failed to start zero DCE", err_params);
return dce->start_up(dce);
err_params:
return ESP_ERR_INVALID_ARG;
}
esp_err_t esp_modem_default_attach(esp_modem_dte_t *dte, esp_modem_dce_t *dce, esp_netif_t* ppp_netif)
{
/* Bind DTE with DCE */
dce->dte = dte;
dte->dce = dce;
/* Init and bind DTE with the PPP netif adapter */
esp_modem_netif_driver_t *modem_netif_adapter = esp_modem_netif_new(dte);
ESP_MODEM_ERR_CHECK(esp_modem_netif_set_default_handlers(modem_netif_adapter, ppp_netif) == ESP_OK,
"modem_netif failed to set handlers", err);
ESP_MODEM_ERR_CHECK(esp_netif_attach(ppp_netif, modem_netif_adapter) == ESP_OK,
"attach netif to modem adapter failed", err);
ESP_MODEM_ERR_CHECK(esp_modem_notify_initialized(dte) == ESP_OK, "DTE init notification failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_modem_dce_t *esp_modem_dce_new(esp_modem_dce_config_t *config)
{
ESP_MODEM_ERR_CHECK(config, "failed to init with zero configuration", err);
esp_modem_dce_t *dce = calloc(1, sizeof(esp_modem_dce_t));
ESP_MODEM_ERR_CHECK(dce, "calloc of esp_modem_dce_t failed", err);
ESP_MODEM_ERR_CHECK(esp_modem_dce_init(dce, config) == ESP_OK, "esp_modem_dce_init has failed", err);
return dce;
err:
return NULL;
}
esp_err_t esp_modem_dce_init(esp_modem_dce_t *dce, esp_modem_dce_config_t *config)
{
esp_err_t err = ESP_OK;
/* init the default DCE first */
ESP_MODEM_ERR_CHECK(dce && config, "failed to init with zero dce or configuration", err_params);
ESP_MODEM_ERR_CHECK(esp_modem_dce_default_init(dce, config) == ESP_OK, "dce default init has failed", err);
if (config->populate_command_list) {
ESP_MODEM_ERR_CHECK(esp_modem_set_default_command_list(dce) == ESP_OK, "esp_modem_dce_set_default_commands failed", err);
}
switch (config->device) {
case ESP_MODEM_DEVICE_SIM800:
err = esp_modem_sim800_specific_init(dce);
break;
case ESP_MODEM_DEVICE_SIM7600:
err = esp_modem_sim7600_specific_init(dce);
break;
case ESP_MODEM_DEVICE_BG96:
err = esp_modem_bg96_specific_init(dce);
break;
default:
case ESP_MODEM_DEVICE_UNSPECIFIED:
break;
}
ESP_MODEM_ERR_CHECK(err == ESP_OK, "dce specific initialization has failed for %d type device", err, config->device);
return ESP_OK;
err:
return ESP_FAIL;
err_params:
return ESP_ERR_INVALID_ARG;
}

View File

@ -1,4 +1,4 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
// Copyright 2020 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.
@ -11,94 +11,237 @@
// 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_netif.h"
#include "esp_netif_ppp.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include <stdlib.h>
#include "esp_log.h"
#include "esp_modem.h"
#include "esp_modem_dte.h"
#include "esp_modem_dce.h"
#include "esp_modem_compat.h"
#include "esp_modem_dce_common_commands.h"
/**
* @brief Error check macro
*
*/
#define ESP_MODEM_COMPAT_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
static const char *TAG = "esp-modem-compat";
static void on_modem_compat_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
/**
* @note Below are the backward compatible functions defined using esp_modem_dce framework
*
*/
static esp_err_t compat_hang_up(modem_dce_t *dce)
{
int32_t compat_event_id = MODEM_EVENT_UNKNOWN;
switch (event_id) {
case ESP_MODEM_EVENT_PPP_START:
compat_event_id = MODEM_EVENT_PPP_START;
break;
case ESP_MODEM_EVENT_PPP_STOP:
compat_event_id = MODEM_EVENT_PPP_STOP;
break;
default:
break;
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->hang_up(esp_dce, NULL, NULL);
}
static esp_err_t compat_echo(modem_dce_t *dce, bool on)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->set_echo(esp_dce, (void*)on, NULL);
}
static esp_err_t compat_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn)
{
esp_modem_dce_pdp_ctx_t pdp = { .type = type, .cid = cid, .apn = apn };
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->set_pdp_context(esp_dce, &pdp, NULL);
}
static esp_err_t compat_sync(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->sync(esp_dce, NULL, NULL);
}
static esp_err_t compat_set_flow_ctrl(modem_dce_t *dce, esp_modem_flow_ctrl_t flow_ctrl)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->set_flow_ctrl(esp_dce, (void*)flow_ctrl, NULL);
}
static esp_err_t compat_store_profile(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_dce->store_profile(esp_dce, NULL, NULL);
}
static esp_err_t compat_get_signal_quality(modem_dce_t *dce, uint32_t *rssi, uint32_t *ber)
{
esp_modem_dce_csq_ctx_t result;
esp_modem_dce_t* esp_dce = &dce->parent;
esp_err_t err = esp_modem_command_list_run(esp_dce, "get_signal_quality", NULL, &result);
if (err == ESP_OK) {
*rssi = result.rssi;
*ber = result.ber;
}
esp_event_post(ESP_MODEM_EVENT, compat_event_id, NULL, 0, 0);
return err;
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
static esp_err_t compat_get_battery_status(modem_dce_t *dce, uint32_t *bcs, uint32_t *bcl, uint32_t *voltage)
{
ESP_LOGI(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ppp_client_ip_info_t ipinfo = {0};
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
esp_netif_t *netif = event->esp_netif;
ipinfo.ip.addr = event->ip_info.ip.addr;
ipinfo.gw.addr = event->ip_info.gw.addr;
ipinfo.netmask.addr = event->ip_info.netmask.addr;
esp_netif_get_dns_info(netif, 0, &dns_info);
ipinfo.ns1.addr = dns_info.ip.u_addr.ip4.addr;
ipinfo.ns2.addr = dns_info.ip.u_addr.ip4.addr;
esp_event_post(ESP_MODEM_EVENT, MODEM_EVENT_PPP_CONNECT, &ipinfo, sizeof(ipinfo), 0);
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
esp_event_post(ESP_MODEM_EVENT, MODEM_EVENT_PPP_DISCONNECT, NULL, 0, 0);
esp_modem_dce_cbc_ctx_t result;
esp_modem_dce_t* esp_dce = &dce->parent;
esp_err_t err = esp_modem_command_list_run(esp_dce, "get_battery_status", NULL, &result);
if (err == ESP_OK) {
*bcs = result.bcs;
*bcl = result.bcl;
*voltage = result.battery_status;
}
return err;
}
esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t handler, void *handler_args)
static esp_err_t compat_set_working_mode(modem_dce_t *dce, esp_modem_mode_t mode)
{
// event loop has to be created when using this API -- create and ignore failure if already created
esp_event_loop_create_default();
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_START, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_CONNECT, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_DISCONNECT, handler, handler_args));
ESP_ERROR_CHECK(esp_event_handler_register(ESP_MODEM_EVENT, MODEM_EVENT_PPP_STOP, handler, handler_args));
return esp_modem_set_event_handler(dte, on_modem_compat_handler, ESP_EVENT_ANY_ID, handler_args);
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_dce_set_working_mode(esp_dce, mode);
}
esp_err_t esp_modem_setup_ppp(modem_dte_t *dte)
static esp_err_t compat_get_module_name(modem_dce_t *dce)
{
#if CONFIG_LWIP_PPP_PAP_SUPPORT && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#elif defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
#error "Unsupported AUTH Negotiation while AUTH_USERNAME and PASSWORD defined"
#endif
// Init netif object
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&cfg);
assert(esp_netif);
// event loop has to be created when using this API -- create and ignore failure if already created
esp_event_loop_create_default();
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
#if defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME) && defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD)
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
void *modem_netif_adapter = esp_modem_netif_setup(dte);
esp_modem_netif_set_default_handlers(modem_netif_adapter, esp_netif);
/* attach the modem to the network interface */
return esp_netif_attach(esp_netif, modem_netif_adapter);
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_command_list_run(esp_dce, "get_module_name", (void *) sizeof(dce->name), dce->name);
}
esp_err_t esp_modem_exit_ppp(modem_dte_t *dte)
static esp_err_t compat_get_imei_number(modem_dce_t *dce)
{
// Note: A minor memory leak is expected when using esp-modem-compat
return esp_modem_stop_ppp(dte);
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_command_list_run(esp_dce, "get_imei_number", (void *) sizeof(dce->imei), dce->imei);
}
static esp_err_t compat_get_operator_name(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_command_list_run(esp_dce, "get_operator_name", (void *) sizeof(dce->oper), dce->oper);
}
static esp_err_t compat_get_imsi_number(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_command_list_run(esp_dce, "get_imsi_number", (void *) sizeof(dce->imsi), dce->imsi);
}
static esp_err_t compat_power_down(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
return esp_modem_command_list_run(esp_dce, "power_down", (void *) sizeof(dce->imsi), dce->imsi);
}
/**
* @brief Compatibility deinitialize
*
* @param dce Modem DCE object
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on fail
*/
static esp_err_t compat_deinit(modem_dce_t *dce)
{
esp_modem_dce_t* esp_dce = &dce->parent;
esp_err_t err = esp_modem_command_list_deinit(esp_dce);
if (err == ESP_OK) {
free(dce);
}
return err;
}
/**
* @brief Compatibility init
*
*/
static modem_dce_t *esp_modem_compat_init(modem_dte_t *dte, esp_modem_dce_device_t device)
{
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_MODEM_PPP_APN);
dce_config.device = device;
dce_config.populate_command_list = true;
modem_dce_t *dce = calloc(1, sizeof(modem_dce_t));
if (dce == NULL) {
return NULL;
}
if (esp_modem_dce_init(&dce->parent, &dce_config) != ESP_OK) {
free(dce);
return NULL;
}
dce->sync = compat_sync;
dce->echo_mode = compat_echo;
dce->store_profile = compat_store_profile;
dce->set_flow_ctrl = compat_set_flow_ctrl;
dce->define_pdp_context = compat_define_pdp_context;
dce->hang_up = compat_hang_up;
dce->get_signal_quality = compat_get_signal_quality;
dce->get_battery_status = compat_get_battery_status;
dce->set_working_mode = compat_set_working_mode;
dce->power_down = compat_power_down;
dce->deinit = compat_deinit;
esp_modem_dce_t* esp_dce = &dce->parent;
/* Bind DTE with DCE */
esp_dce->dte = dte;
dte->dce = esp_dce;
/* All units initialized, notify the modem before sending commands */
ESP_MODEM_COMPAT_CHECK(esp_modem_notify_initialized(dte) == ESP_OK, "modem start notification failed", err);
ESP_MODEM_COMPAT_CHECK(compat_sync(dce) == ESP_OK, "sync failed", err);
/* Close echo */
ESP_MODEM_COMPAT_CHECK(compat_echo(dce, false) == ESP_OK, "close echo mode failed", err);
bool ready;
ESP_ERROR_CHECK(esp_modem_command_list_run(esp_dce, "read_pin", NULL, &ready));
if (!ready) {
ESP_LOGE(TAG, "PIN not ready man");
ESP_ERROR_CHECK(esp_modem_command_list_run(esp_dce, "set_pin", "1234", NULL));
}
/* Get Module name */
ESP_MODEM_COMPAT_CHECK(compat_get_module_name(dce) == ESP_OK, "get module name failed", err);
/* Get IMEI number */
ESP_MODEM_COMPAT_CHECK(compat_get_imei_number(dce) == ESP_OK, "get imei number failed", err);
/* Get IMSI number */
ESP_MODEM_COMPAT_CHECK(compat_get_imsi_number(dce) == ESP_OK, "get imsi number failed", err);
/* Get operator name */
ESP_MODEM_COMPAT_CHECK(compat_get_operator_name(dce) == ESP_OK, "get operator name failed", err);
return dce;
err:
return NULL;
}
/**
* @brief Legacy init of SIM800 module
*
*/
modem_dce_t *sim800_init(modem_dte_t *dte)
{
return esp_modem_compat_init(dte, ESP_MODEM_DEVICE_SIM800);
}
/**
* @brief Legacy init of BG96 module
*
*/
modem_dce_t *bg96_init(modem_dte_t *dte)
{
return esp_modem_compat_init(dte, ESP_MODEM_DEVICE_BG96);
}
/**
* @brief Legacy init of SIM7600 module
*
*/
modem_dce_t *sim7600_init(modem_dte_t *dte)
{
return esp_modem_compat_init(dte, ESP_MODEM_DEVICE_SIM7600);
}

View File

@ -0,0 +1,190 @@
// Copyright 2015-2020 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_modem_dce.h"
#include "esp_modem_dce_command_lib.h"
#include "esp_modem_dce_common_commands.h"
#include "esp_modem_internal.h"
#include "esp_log.h"
static const char *TAG = "esp_modem_dce";
esp_err_t esp_modem_dce_generic_command(esp_modem_dce_t *dce, const char * command, uint32_t timeout, esp_modem_dce_handle_line_t handle_line, void *ctx)
{
esp_modem_dte_t *dte = dce->dte;
ESP_LOGD(TAG, "%s(%d): Sending command:%s\n", __func__, __LINE__, command );
dce->handle_line = handle_line;
dce->handle_line_ctx = ctx;
if (dte->send_cmd(dte, command, timeout) != ESP_OK) {
ESP_LOGW(TAG, "%s(%d): Command:%s response timeout", __func__, __LINE__, command );
return ESP_ERR_TIMEOUT;
}
if (dce->state == ESP_MODEM_STATE_FAIL) {
ESP_LOGW(TAG, "%s(%d): Command:%s\n...failed", __func__, __LINE__, command );
return ESP_FAIL;
}
ESP_LOGD(TAG, "%s(%d): Command:%s\n succeeded", __func__, __LINE__, command );
return ESP_OK;
}
esp_err_t esp_modem_dce_set_params(esp_modem_dce_t *dce, esp_modem_dce_config_t* config)
{
// save the config
memcpy(&dce->config, config, sizeof(esp_modem_dce_config_t));
return ESP_OK;
}
esp_err_t esp_modem_dce_default_init(esp_modem_dce_t *dce, esp_modem_dce_config_t* config)
{
// Check parameters
ESP_MODEM_ERR_CHECK(dce && config, "dce object or configuration is NULL", err);
// Set default commands needed for the DCE to operate
// note: command links will be overwritten if cmd-list enabled
dce->set_data_mode = esp_modem_dce_set_data_mode;
dce->resume_data_mode = esp_modem_dce_resume_data_mode;
dce->set_command_mode = esp_modem_dce_set_command_mode;
dce->set_pdp_context = esp_modem_dce_set_pdp_context;
dce->hang_up = esp_modem_dce_hang_up;
dce->set_echo = esp_modem_dce_set_echo;
dce->sync = esp_modem_dce_sync;
dce->set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
dce->store_profile = esp_modem_dce_store_profile;
ESP_MODEM_ERR_CHECK(esp_modem_dce_set_params(dce, config) == ESP_OK, "Failed to configure dce object", err);
// set DCE basic API
dce->start_up = esp_modem_dce_default_start_up;
dce->deinit = esp_modem_dce_default_destroy;
dce->set_working_mode = esp_modem_dce_set_working_mode;
// initialize the list if enabled
if (config->populate_command_list) {
dce->dce_cmd_list = esp_modem_command_list_create();
ESP_MODEM_ERR_CHECK(dce->dce_cmd_list, "Allocation of dce internal object has failed", err);
}
return ESP_OK;
err:
return ESP_ERR_NO_MEM;
}
esp_err_t esp_modem_dce_default_destroy(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(esp_modem_command_list_deinit(dce) == ESP_OK, "failed", err);
free(dce);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_handle_response_default(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
esp_err_t esp_modem_process_command_done(esp_modem_dce_t *dce, esp_modem_state_t state)
{
dce->state = state;
return dce->dte->process_cmd_done(dce->dte);
}
static esp_err_t esp_modem_switch_to_command_mode(esp_modem_dce_t *dce)
{
esp_modem_wait_ms(1000); // 1s delay for the device to recognize the data escape sequence
if (dce->set_command_mode(dce, NULL, NULL) != ESP_OK) {
// exiting data mode could fail if the modem is already in command mode via PPP netif closed
ESP_MODEM_ERR_CHECK(dce->sync(dce, NULL, NULL) == ESP_OK, "sync after PPP exit failed", err);
} else {
// hang-up if exit PPP succeeded
dce->mode = ESP_MODEM_COMMAND_MODE;
ESP_MODEM_ERR_CHECK(dce->hang_up(dce, NULL, NULL) == ESP_OK, "hang-up after PPP exit failed", err);
}
dce->mode = ESP_MODEM_COMMAND_MODE;
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t esp_modem_switch_to_data_mode(esp_modem_dce_t *dce)
{
// before going to data mode, set the PDP data context
ESP_MODEM_ERR_CHECK(dce->set_pdp_context(dce, &dce->config.pdp_context, NULL) == ESP_OK, "setting pdp context failed", err);
// now set the data mode
if (dce->set_data_mode(dce, NULL, NULL) != ESP_OK) {
// Initiate PPP mode could fail, if we've already "dialed" the data call before.
// in that case we retry to just resume the data mode
ESP_LOGD(TAG, "set_data_mode, retry with resume_data_mode");
ESP_MODEM_ERR_CHECK(dce->resume_data_mode(dce, NULL, NULL) == ESP_OK, "setting data mode failed", err);
}
dce->mode = ESP_MODEM_PPP_MODE;
return ESP_OK;
err:
return ESP_FAIL;
}
/**
* @brief Set Working Mode
*
* @param dce Modem DCE object
* @param mode working mode
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t esp_modem_dce_set_working_mode(esp_modem_dce_t *dce, esp_modem_mode_t mode)
{
switch (mode) {
case ESP_MODEM_COMMAND_MODE:
ESP_MODEM_ERR_CHECK(esp_modem_switch_to_command_mode(dce) == ESP_OK, "Setting command mode failed", err);
break;
case ESP_MODEM_PPP_MODE:
ESP_MODEM_ERR_CHECK(esp_modem_switch_to_data_mode(dce) == ESP_OK, "Setting data mode failed", err);
break;
default:
ESP_LOGW(TAG, "unsupported working mode: %d", mode);
goto err;
}
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t esp_modem_dce_default_start_up(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(dce->sync(dce, NULL, NULL) == ESP_OK, "sending sync failed", err);
ESP_MODEM_ERR_CHECK(dce->set_echo(dce, (void*)false, NULL) == ESP_OK, "set_echo failed", err);
// TODO: remove!
bool ready;
// ESP_ERROR_CHECK(esp_modem_command_list_run(dce, "read_pin", NULL, &ready));
ESP_ERROR_CHECK(esp_modem_dce_read_pin(dce, NULL, &ready));
if (!ready) {
ESP_LOGE(TAG, "PIN not ready man");
// ESP_ERROR_CHECK(esp_modem_command_list_run(dce, "set_pin", "1234", NULL));
ESP_ERROR_CHECK(esp_modem_dce_set_pin(dce, "1234", NULL));
}
// TODO: remove!
ESP_MODEM_ERR_CHECK(dce->set_flow_ctrl(dce, (void*)ESP_MODEM_FLOW_CONTROL_NONE, NULL) == ESP_OK, "set_flow_ctrl failed", err);
ESP_MODEM_ERR_CHECK(dce->store_profile(dce, NULL, NULL) == ESP_OK, "store_profile failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}

View File

@ -0,0 +1,204 @@
// Copyright 2015-2020 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_command_lib.h"
#include "esp_modem_internal.h"
#include "esp_modem_dce_common_commands.h"
static const char *TAG = "esp_modem_command_lib";
typedef struct cmd_item_s cmd_item_t;
/**
* struct for one item in command list
*/
struct cmd_item_s {
const char *command; //!< command name
dce_command_t function; //!< function pointer
SLIST_ENTRY(cmd_item_s) next; //!< next command in the list
};
/**
* private struct defined for dce internal object
*/
struct esp_modem_dce_cmd_list {
SLIST_HEAD(cmd_list_, cmd_item_s) command_list;
};
/**
* @brief List of common AT commands in the library
*
*/
static const cmd_item_t s_command_list[] = {
{ .command = "sync", .function = esp_modem_dce_sync },
{ .command = "get_imei_number", .function = esp_modem_dce_get_imei_number },
{ .command = "get_imsi_number", .function = esp_modem_dce_get_imsi_number },
{ .command = "get_module_name", .function = esp_modem_dce_get_module_name },
{ .command = "get_operator_name", .function = esp_modem_dce_get_operator_name },
{ .command = "set_echo", .function = esp_modem_dce_set_echo },
{ .command = "store_profile", .function = esp_modem_dce_store_profile },
{ .command = "set_flow_ctrl", .function = esp_modem_dce_set_flow_ctrl },
{ .command = "set_pdp_context", .function = esp_modem_dce_set_pdp_context },
{ .command = "hang_up", .function = esp_modem_dce_hang_up },
{ .command = "get_signal_quality", .function = esp_modem_dce_get_signal_quality },
{ .command = "set_data_mode", .function = esp_modem_dce_set_data_mode },
{ .command = "resume_data_mode", .function = esp_modem_dce_resume_data_mode },
{ .command = "set_command_mode", .function = esp_modem_dce_set_command_mode },
{ .command = "get_battery_status", .function = esp_modem_dce_get_battery_status },
{ .command = "power_down", .function = esp_modem_dce_power_down },
{ .command = "reset", .function = esp_modem_dce_reset },
{ .command = "set_pin", .function = esp_modem_dce_set_pin },
{ .command = "read_pin", .function = esp_modem_dce_read_pin },
{ .command = "set_baud", .function = esp_modem_dce_set_baud_temp },
};
static esp_err_t update_internal_command_refs(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(dce->set_data_mode = esp_modem_dce_find_command(dce, "set_data_mode"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->resume_data_mode = esp_modem_dce_find_command(dce, "resume_data_mode"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->set_command_mode = esp_modem_dce_find_command(dce, "set_command_mode"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->set_pdp_context = esp_modem_dce_find_command(dce, "set_pdp_context"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->hang_up = esp_modem_dce_find_command(dce, "hang_up"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->set_echo = esp_modem_dce_find_command(dce, "set_echo"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->sync = esp_modem_dce_find_command(dce, "sync"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->set_flow_ctrl = esp_modem_dce_find_command(dce, "set_flow_ctrl"), "cmd not found", err);
ESP_MODEM_ERR_CHECK(dce->store_profile = esp_modem_dce_find_command(dce, "store_profile"), "cmd not found", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t esp_modem_dce_init_command_list(esp_modem_dce_t *dce, size_t commands, const cmd_item_t *command_list)
{
if (commands < 1 || command_list == NULL || dce->dce_cmd_list == NULL) {
return ESP_ERR_INVALID_ARG;
}
SLIST_INIT(&dce->dce_cmd_list->command_list);
for (int i=0; i < commands; ++i) {
cmd_item_t *new_item = calloc(1, sizeof(struct cmd_item_s));
new_item->command = command_list[i].command;
new_item->function = command_list[i].function;
SLIST_INSERT_HEAD(&dce->dce_cmd_list->command_list, new_item, next);
}
return ESP_OK;
}
esp_err_t esp_modem_set_default_command_list(esp_modem_dce_t *dce)
{
esp_err_t err = esp_modem_dce_init_command_list(dce, sizeof(s_command_list) / sizeof(cmd_item_t), s_command_list);
if (err == ESP_OK) {
return update_internal_command_refs(dce);
}
return err;
}
esp_err_t esp_modem_command_list_run(esp_modem_dce_t *dce, const char * command, void * param, void* result)
{
if (dce == NULL || dce->dce_cmd_list == NULL) {
return ESP_ERR_INVALID_ARG;
}
cmd_item_t *item;
SLIST_FOREACH(item, &dce->dce_cmd_list->command_list, next) {
if (strcmp(item->command, command) == 0) {
return item->function(dce, param, result);
}
}
return ESP_ERR_NOT_FOUND;
}
dce_command_t esp_modem_dce_find_command(esp_modem_dce_t *dce, const char * command)
{
if (dce == NULL || dce->dce_cmd_list == NULL) {
return NULL;
}
cmd_item_t *item;
SLIST_FOREACH(item, &dce->dce_cmd_list->command_list, next) {
if (strcmp(item->command, command) == 0) {
return item->function;
}
}
return NULL;
}
esp_err_t esp_modem_dce_delete_all_commands(esp_modem_dce_t *dce)
{
if (dce->dce_cmd_list) {
while (!SLIST_EMPTY(&dce->dce_cmd_list->command_list)) {
cmd_item_t *item = SLIST_FIRST(&dce->dce_cmd_list->command_list);
SLIST_REMOVE_HEAD(&dce->dce_cmd_list->command_list, next);
free(item);
}
}
return ESP_OK;
}
esp_err_t esp_modem_dce_delete_command(esp_modem_dce_t *dce, const char * command_id)
{
cmd_item_t *item;
SLIST_FOREACH(item, &dce->dce_cmd_list->command_list, next) {
if (strcmp(item->command, command_id) == 0) {
SLIST_REMOVE(&dce->dce_cmd_list->command_list, item, cmd_item_s, next);
free(item);
return ESP_OK;
}
}
return ESP_ERR_NOT_FOUND;
}
esp_err_t esp_modem_command_list_set_cmd(esp_modem_dce_t *dce, const char * command_id, dce_command_t command)
{
if (dce == NULL || dce->dce_cmd_list == NULL) {
return ESP_ERR_INVALID_ARG;
}
cmd_item_t *item;
SLIST_FOREACH(item, &dce->dce_cmd_list->command_list, next) {
if (strcmp(item->command, command_id) == 0) {
item->function = command;
return update_internal_command_refs(dce);
}
}
cmd_item_t *new_item = calloc(1, sizeof(struct cmd_item_s));
new_item->command = command_id;
new_item->function = command;
SLIST_INSERT_HEAD(&dce->dce_cmd_list->command_list, new_item, next);
return update_internal_command_refs(dce);;
}
struct esp_modem_dce_cmd_list* esp_modem_command_list_create(void)
{
return calloc(1, sizeof(struct esp_modem_dce_cmd_list));
}
esp_err_t esp_modem_command_list_deinit(esp_modem_dce_t *dce)
{
if (dce->dte) {
dce->dte->dce = NULL;
}
esp_modem_dce_delete_all_commands(dce);
free(dce->dce_cmd_list);
return ESP_OK;
}

View File

@ -0,0 +1,357 @@
// Copyright 2015-2020 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.h"
#include "esp_modem_dce_common_commands.h"
typedef struct common_string_s {
const char * command;
char * string;
size_t len;
} common_string_t;
static inline esp_err_t generic_command_default_handle(esp_modem_dce_t *dce, const char * command)
{
return esp_modem_dce_generic_command(dce, command, MODEM_COMMAND_TIMEOUT_DEFAULT, esp_modem_dce_handle_response_default, NULL);
}
static esp_err_t common_handle_string(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
} else {
common_string_t *result_str = dce->handle_line_ctx;
assert(result_str->string != NULL && result_str->len != 0);
int len = snprintf(result_str->string, result_str->len, "%s", line);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(result_str->string, len);
err = ESP_OK;
}
}
return err;
}
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t esp_modem_dce_common_handle_cbc(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
esp_modem_dce_cbc_ctx_t *cbc = dce->handle_line_ctx;
/* +CBC: <bcs>,<bcl>,<voltage> */
sscanf(line, "%*s%d,%d,%d", &cbc->bcs, &cbc->bcl, &cbc->battery_status);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from AT+CSQ
*/
static esp_err_t esp_modem_dce_common_handle_csq(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CSQ", strlen("+CSQ"))) {
/* store value of rssi and ber */
esp_modem_dce_csq_ctx_t *csq = dce->handle_line_ctx;
/* +CSQ: <rssi>,<ber> */
sscanf(line, "%*s%d,%d", &csq->rssi, &csq->ber);
err = ESP_OK;
}
return err;
}
/**
* @brief Handle response from AT+QPOWD=1
*/
static esp_err_t esp_modem_dce_handle_power_down(esp_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, "POWERED DOWN")) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
}
return err;
}
/**
* @brief Handle response from exiting the PPP mode
*/
static esp_err_t esp_modem_dce_handle_exit_data_mode(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Handle response from entry of the PPP mode
*/
static esp_err_t esp_modem_dce_handle_atd_ppp(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
static esp_err_t esp_modem_dce_handle_read_pin(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, "READY")) {
bool *ready = (bool*)dce->handle_line_ctx;
*ready = true;
err = ESP_OK;
} else if (strstr(line, "PIN") || strstr(line, "PUK")) {
bool *ready = (bool*)dce->handle_line_ctx;
*ready = false;
err = ESP_OK;
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
static esp_err_t esp_modem_dce_handle_reset(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_OK;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = ESP_OK;
} else
if (strstr(line, "PB DONE")) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
esp_err_t esp_modem_dce_sync(esp_modem_dce_t *dce, void *param, void *result)
{
return generic_command_default_handle(dce, "AT\r");
}
esp_err_t esp_modem_dce_set_echo(esp_modem_dce_t *dce, void *param, void *result)
{
bool echo_on = (bool)param;
if (echo_on) {
return generic_command_default_handle(dce, "ATE1\r");
} else {
return generic_command_default_handle(dce, "ATE0\r");
}
}
static esp_err_t common_get_operator_after_mode_format(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
} else if (!strncmp(line, "+COPS", strlen("+COPS"))) {
common_string_t *result_str = dce->handle_line_ctx;
assert(result_str->string != NULL && result_str->len != 0);
/* 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 */
char *line_copy = strdup(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(result_str->string, result_str->len, "%s", p[2]);
if (len > 2) {
/* Strip "\r\n" */
strip_cr_lf_tail(result_str->string, len);
err = ESP_OK;
}
}
free(line_copy);
}
return err;
}
static esp_err_t common_get_common_string(esp_modem_dce_t *dce, void *ctx)
{
common_string_t * param_str = ctx;
return esp_modem_dce_generic_command(dce, param_str->command, MODEM_COMMAND_TIMEOUT_DEFAULT, common_handle_string, ctx);
}
esp_err_t esp_modem_dce_get_imei_number(esp_modem_dce_t *dce, void *param, void *result)
{
common_string_t common_str = { .command = "AT+CGSN\r", .string = result, .len = (size_t)param };
return common_get_common_string(dce, &common_str);
}
esp_err_t esp_modem_dce_get_imsi_number(esp_modem_dce_t *dce, void *param, void *result)
{
common_string_t common_str = { .command = "AT+CIMI\r", .string = result, .len = (size_t)param };
return common_get_common_string(dce, &common_str);
}
esp_err_t esp_modem_dce_get_module_name(esp_modem_dce_t *dce, void *param, void *result)
{
common_string_t common_str = { .command = "AT+CGMM\r", .string = result, .len = (size_t)param };
return common_get_common_string(dce, &common_str);
}
esp_err_t esp_modem_dce_get_operator_name(esp_modem_dce_t *dce, void *param, void *result)
{
common_string_t common_str = { .command = "AT+COPS?\r", .string = result, .len = (size_t)param };
return esp_modem_dce_generic_command(dce, common_str.command, MODEM_COMMAND_TIMEOUT_OPERATOR,
common_get_operator_after_mode_format, &common_str);
}
esp_err_t esp_modem_dce_reset(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+CRESET\r", MODEM_COMMAND_TIMEOUT_RESET, esp_modem_dce_handle_reset, NULL);
}
esp_err_t esp_modem_dce_set_pin(esp_modem_dce_t *dce, void *param, void *result)
{
char command[] = "AT+CPIN=0000\r";
memcpy(command + 8, param, 4); // copy 4 bytes to the "0000" placeholder
esp_err_t err = esp_modem_dce_generic_command(dce, command, MODEM_COMMAND_TIMEOUT_DEFAULT, esp_modem_dce_handle_response_default, NULL);
return err;
}
esp_err_t esp_modem_dce_read_pin(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+CPIN?\r", MODEM_COMMAND_TIMEOUT_DEFAULT, esp_modem_dce_handle_read_pin, result);
}
esp_err_t esp_modem_dce_store_profile(esp_modem_dce_t *dce, void *param, void *result)
{
return generic_command_default_handle(dce, "AT&W\r");
}
esp_err_t esp_modem_dce_set_flow_ctrl(esp_modem_dce_t *dce, void *param, void *result)
{
esp_modem_dte_t *dte = dce->dte;
esp_modem_flow_ctrl_t flow_ctrl = (esp_modem_flow_ctrl_t)param;
char *command;
int len = asprintf(&command, "AT+IFC=%d,%d\r", dte->flow_ctrl, flow_ctrl);
if (len <= 0) {
return ESP_ERR_NO_MEM;
}
esp_err_t err = generic_command_default_handle(dce, command);
free(command);
return err;
}
esp_err_t esp_modem_dce_set_pdp_context(esp_modem_dce_t *dce, void *param, void *result)
{
esp_modem_dce_pdp_ctx_t *pdp = param;
char *command;
int len = asprintf(&command, "AT+CGDCONT=%d,\"%s\",\"%s\"\r", pdp->cid, pdp->type, pdp->apn);
if (len <= 0) {
return ESP_ERR_NO_MEM;
}
esp_err_t err = generic_command_default_handle(dce, command);
free(command);
return err;
}
#define MODEM_COMMAND_TIMEOUT_HANG_UP (90000) /*!< Timeout value for hang up */
esp_err_t esp_modem_dce_hang_up(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "ATH\r", MODEM_COMMAND_TIMEOUT_HANG_UP,
esp_modem_dce_handle_response_default, NULL);
}
esp_err_t esp_modem_dce_get_signal_quality(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+CSQ\r", MODEM_COMMAND_TIMEOUT_DEFAULT,
esp_modem_dce_common_handle_csq, result);
}
esp_err_t esp_modem_dce_get_battery_status(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+CBC\r", MODEM_COMMAND_TIMEOUT_DEFAULT,
esp_modem_dce_common_handle_cbc, result);
}
esp_err_t esp_modem_dce_set_data_mode(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "ATD*99***1#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE,
esp_modem_dce_handle_atd_ppp, NULL);
}
esp_err_t esp_modem_dce_resume_data_mode(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "ATO\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE,
esp_modem_dce_handle_atd_ppp, NULL);
}
esp_err_t esp_modem_dce_set_command_mode(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE,
esp_modem_dce_handle_exit_data_mode, NULL);
}
esp_err_t esp_modem_dce_power_down(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+QPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF,
esp_modem_dce_handle_power_down, NULL);
}
esp_err_t esp_modem_dce_set_baud_temp(esp_modem_dce_t *dce, void *param, void *result)
{
char command[] = "AT+IPR=3686400\r"; // reserve space with max baud placeholder
size_t cmd_placeholder_len = strlen(command);
strncpy(command + 7, param, cmd_placeholder_len-7); // copy param string to the param
size_t cmd_len = strlen(command);
if (cmd_len+1 >= cmd_placeholder_len) {
return ESP_FAIL;
}
command[cmd_len] = '\r';
command[cmd_len+1] = '\0';
return esp_modem_dce_generic_command(dce, command, MODEM_COMMAND_TIMEOUT_DEFAULT,
esp_modem_dce_handle_response_default, NULL);
}

View File

@ -1,126 +0,0 @@
// 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[64];
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;
}

View File

@ -0,0 +1,505 @@
// Copyright 2020 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 "esp_modem.h"
#include "esp_modem_dce.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_modem_internal.h"
#include "esp_modem_dte_internal.h"
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
#define MIN_PATTERN_INTERVAL (9)
#define MIN_POST_IDLE (0)
#define MIN_PRE_IDLE (0)
/**
* @brief Macro defined for error checking
*
*/
static const char *TAG = "esp-modem-dte";
/**
* @brief Returns true if the supplied string contains only CR or LF
*
* @param str string to check
* @param len length of string
*/
static inline bool is_only_cr_lf(const char *str, uint32_t len)
{
for (int i=0; i<len; ++i) {
if (str[i] != '\r' && str[i] != '\n') {
return false;
}
}
return true;
}
esp_err_t esp_modem_set_rx_cb(esp_modem_dte_t *dte, esp_modem_on_receive receive_cb, void *receive_cb_ctx)
{
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
esp_dte->receive_cb_ctx = receive_cb_ctx;
esp_dte->receive_cb = receive_cb;
return ESP_OK;
}
/**
* @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_internal_t *esp_dte)
{
esp_modem_dce_t *dce = esp_dte->parent.dce;
ESP_MODEM_ERR_CHECK(dce, "DTE has not yet bind with DCE", err);
const char *line = (const char *)(esp_dte->buffer);
size_t len = strlen(line);
/* Skip pure "\r\n" lines */
if (len > 2 && !is_only_cr_lf(line, len)) {
ESP_MODEM_ERR_CHECK(dce->handle_line, "no handler for line", err_handle);
ESP_LOGD(TAG, "%s: %s", __func__ , line);
ESP_MODEM_ERR_CHECK(dce->handle_line(dce, line) == ESP_OK, "handle line failed", err_handle);
}
return ESP_OK;
err_handle:
/* Send ESP_MODEM_EVENT_UNKNOWN signal to event loop */
esp_event_post_to(esp_dte->event_loop_hdl, ESP_MODEM_EVENT, ESP_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_internal_t *esp_dte)
{
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
int read_len = 0;
if (pos != -1) {
if (pos < esp_dte->line_buffer_size - 1) {
/* read one line(include '\n') */
read_len = pos + 1;
} else {
ESP_LOGW(TAG, "ESP Modem Line buffer too small");
read_len = esp_dte->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(TAG, "uart read bytes failed");
}
} else {
size_t length = 0;
uart_get_buffered_data_len(esp_dte->uart_port, &length);
ESP_LOGW(TAG, "Pattern not found in the pattern queue, uart data length = %d", length);
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem-dte: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
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_internal_t *esp_dte)
{
if (!esp_dte->parent.dce) {
// we could possibly get a data event before
// the DCE gets bound yet with the DTE, so just return
return;
}
size_t length = 0;
uart_get_buffered_data_len(esp_dte->uart_port, &length);
if (esp_dte->parent.dce->mode != ESP_MODEM_PPP_MODE) {
// Check if matches the pattern to process the data as pattern
int pos = uart_pattern_pop_pos(esp_dte->uart_port);
if (pos > -1) {
esp_handle_uart_pattern(esp_dte);
return;
}
// Read the data and process it using `handle_line` logic
length = MIN(esp_dte->line_buffer_size-1, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
ESP_LOG_BUFFER_HEXDUMP("esp-modem-dte: debug_data", esp_dte->buffer, length, ESP_LOG_DEBUG);
esp_dte->buffer[length] = '\0';
if (esp_dte->parent.dce->handle_line) {
// Send new line to handle if handler registered
esp_dte_handle_line(esp_dte);
}
return;
}
length = MIN(esp_dte->line_buffer_size, length);
length = uart_read_bytes(esp_dte->uart_port, esp_dte->buffer, length, portMAX_DELAY);
/* pass the input data to configured callback */
if (length) {
ESP_LOG_BUFFER_HEXDUMP("esp-modem-dte: ppp_input", esp_dte->buffer, length, ESP_LOG_VERBOSE);
esp_dte->receive_cb(esp_dte->buffer, length, esp_dte->receive_cb_ctx);
}
}
/**
* @brief UART Event Task Entry
*
* @param param task parameter
*/
static void uart_event_task_entry(void *param)
{
esp_modem_dte_internal_t *esp_dte = (esp_modem_dte_internal_t *)param;
uart_event_t event;
EventBits_t bits = xEventGroupWaitBits(esp_dte->process_group, (ESP_MODEM_START_BIT|ESP_MODEM_STOP_BIT), pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & ESP_MODEM_STOP_BIT) {
vTaskDelete(NULL);
}
while (xEventGroupGetBits(esp_dte->process_group) & ESP_MODEM_START_BIT) {
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(TAG, "HW FIFO Overflow");
uart_flush_input(esp_dte->uart_port);
xQueueReset(esp_dte->event_queue);
break;
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "Ring Buffer Full");
uart_flush_input(esp_dte->uart_port);
xQueueReset(esp_dte->event_queue);
break;
case UART_BREAK:
ESP_LOGW(TAG, "Rx Break");
break;
case UART_PARITY_ERR:
ESP_LOGE(TAG, "Parity Error");
break;
case UART_FRAME_ERR:
ESP_LOGE(TAG, "Frame Error");
break;
case UART_PATTERN_DET:
esp_handle_uart_pattern(esp_dte);
break;
default:
ESP_LOGW(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(0));
}
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(esp_modem_dte_t *dte, const char *command, uint32_t timeout)
{
esp_err_t ret = ESP_FAIL;
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce, "DTE has not yet bind with DCE", err);
ESP_MODEM_ERR_CHECK(command, "command is NULL", err);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
/* Calculate timeout clock tick */
/* Reset runtime information */
dce->state = ESP_MODEM_STATE_PROCESSING;
/* Send command via UART */
uart_write_bytes(esp_dte->uart_port, command, strlen(command));
/* Check timeout */
EventBits_t bits = xEventGroupWaitBits(esp_dte->process_group, (ESP_MODEM_COMMAND_BIT|ESP_MODEM_STOP_BIT), pdTRUE, pdFALSE, pdMS_TO_TICKS(timeout));
ESP_MODEM_ERR_CHECK(bits&ESP_MODEM_COMMAND_BIT, "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(esp_modem_dte_t *dte, const char *data, uint32_t length)
{
ESP_MODEM_ERR_CHECK(data, "data is NULL", err);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
if (esp_dte->parent.dce->mode == ESP_MODEM_TRANSITION_MODE) {
ESP_LOGD(TAG, "Not sending data in transition mode");
return -1;
}
ESP_LOG_BUFFER_HEXDUMP("esp-modem-dte: ppp_output", data, length, ESP_LOG_VERBOSE);
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(esp_modem_dte_t *dte, const char *data, uint32_t length,
const char *prompt, uint32_t timeout)
{
ESP_MODEM_ERR_CHECK(data, "data is NULL", err_param);
ESP_MODEM_ERR_CHECK(prompt, "prompt is NULL", err_param);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_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);
ESP_MODEM_ERR_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));
ESP_MODEM_ERR_CHECK(res >= len, "wait prompt [%s] timeout", err, prompt);
ESP_MODEM_ERR_CHECK(!strncmp(prompt, (const char *)buffer, len), "get wrong prompt: %s", err, buffer);
free(buffer);
uart_enable_pattern_det_baud_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_baud_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(esp_modem_dte_t *dte, esp_modem_mode_t new_mode)
{
esp_modem_dce_t *dce = dte->dce;
ESP_MODEM_ERR_CHECK(dce, "DTE has not yet bind with DCE", err);
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
ESP_MODEM_ERR_CHECK(dce->mode != new_mode, "already in mode: %d", err, new_mode);
esp_modem_mode_t current_mode = dce->mode;
ESP_MODEM_ERR_CHECK(current_mode != new_mode, "already in mode: %d", err, new_mode);
dce->mode = ESP_MODEM_TRANSITION_MODE; // mode switching will be finished in set_working_mode() on success
// (or restored on failure)
switch (new_mode) {
case ESP_MODEM_PPP_MODE:
ESP_MODEM_ERR_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
uart_disable_pattern_det_intr(esp_dte->uart_port);
uart_set_rx_full_threshold(esp_dte->uart_port, 64);
uart_enable_rx_intr(esp_dte->uart_port);
break;
case ESP_MODEM_COMMAND_MODE:
ESP_MODEM_ERR_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err_restore_mode, new_mode);
uart_disable_rx_intr(esp_dte->uart_port);
uart_flush(esp_dte->uart_port);
uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
uart_pattern_queue_reset(esp_dte->uart_port, esp_dte->pattern_queue_size);
break;
default:
break;
}
return ESP_OK;
err_restore_mode:
dce->mode = current_mode;
err:
return ESP_FAIL;
}
static esp_err_t esp_modem_dte_process_cmd_done(esp_modem_dte_t *dte)
{
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
EventBits_t bits = xEventGroupSetBits(esp_dte->process_group, ESP_MODEM_COMMAND_BIT);
return bits & ESP_MODEM_STOP_BIT ? ESP_FAIL : ESP_OK; // report error if the group indicated MODEM_STOP condition
}
/**
* @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(esp_modem_dte_t *dte)
{
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
/* Clear the start bit */
xEventGroupClearBits(esp_dte->process_group, ESP_MODEM_START_BIT);
/* Delete UART event task */
vTaskDelete(esp_dte->uart_event_task_hdl);
/* Delete semaphore */
vEventGroupDelete(esp_dte->process_group);
/* 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;
}
/**
* @brief Create and init Modem DTE object
*
*/
esp_modem_dte_t *esp_modem_dte_new(const esp_modem_dte_config_t *config)
{
esp_err_t res;
/* malloc memory for esp_dte object */
esp_modem_dte_internal_t *esp_dte = calloc(1, sizeof(esp_modem_dte_internal_t));
ESP_MODEM_ERR_CHECK(esp_dte, "calloc esp_dte failed", err_dte_mem);
/* malloc memory to storing lines from modem dce */
esp_dte->line_buffer_size = config->line_buffer_size;
esp_dte->buffer = calloc(1, config->line_buffer_size);
ESP_MODEM_ERR_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,
.source_clk = UART_SCLK_REF_TICK,
.flow_ctrl = (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS : UART_HW_FLOWCTRL_DISABLE
};
ESP_MODEM_ERR_CHECK(uart_param_config(esp_dte->uart_port, &uart_config) == ESP_OK, "config uart parameter failed", err_uart_config);
if (config->flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
res = uart_set_pin(esp_dte->uart_port, config->tx_io_num, config->rx_io_num,
config->rts_io_num, config->cts_io_num);
} else {
res = uart_set_pin(esp_dte->uart_port, config->tx_io_num, config->rx_io_num,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
ESP_MODEM_ERR_CHECK(res == ESP_OK, "config uart gpio failed", err_uart_config);
/* Set flow control threshold */
if (config->flow_control == ESP_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 == ESP_MODEM_FLOW_CONTROL_SW) {
res = uart_set_sw_flow_ctrl(esp_dte->uart_port, true, 8, UART_FIFO_LEN - 8);
}
ESP_MODEM_ERR_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->rx_buffer_size, config->tx_buffer_size,
config->event_queue_size, &(esp_dte->event_queue), 0);
ESP_MODEM_ERR_CHECK(res == ESP_OK, "install uart driver failed", err_uart_config);
res = uart_set_rx_timeout(esp_dte->uart_port, 1);
ESP_MODEM_ERR_CHECK(res == ESP_OK, "set rx timeout failed", err_uart_config);
/* Set pattern interrupt, used to detect the end of a line. */
res = uart_enable_pattern_det_baud_intr(esp_dte->uart_port, '\n', 1, MIN_PATTERN_INTERVAL, MIN_POST_IDLE, MIN_PRE_IDLE);
/* Set pattern queue size */
esp_dte->pattern_queue_size = config->pattern_queue_size;
res |= uart_pattern_queue_reset(esp_dte->uart_port, config->pattern_queue_size);
/* Starting in command mode -> explicitly disable RX interrupt */
uart_disable_rx_intr(esp_dte->uart_port);
uart_set_rx_full_threshold(esp_dte->uart_port, 64);
ESP_MODEM_ERR_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
};
ESP_MODEM_ERR_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_group = xEventGroupCreate();
ESP_MODEM_ERR_CHECK(esp_dte->process_group, "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->event_task_stack_size, //Task Stack Size(Bytes)
esp_dte, //Task Parameter
config->event_task_priority, //Task Priority
& (esp_dte->uart_event_task_hdl) //Task Handler
);
ESP_MODEM_ERR_CHECK(ret == pdTRUE, "create uart event task failed", err_tsk_create);
return &(esp_dte->parent);
/* Error handling */
err_tsk_create:
vEventGroupDelete(esp_dte->process_group);
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_dte_set_params(esp_modem_dte_t *dte, const esp_modem_dte_config_t *config)
{
esp_modem_dte_internal_t *esp_dte = __containerof(dte, esp_modem_dte_internal_t, parent);
return uart_set_baudrate(esp_dte->uart_port, config->baud_rate);
}

View File

@ -1,4 +1,4 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2020 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.
@ -12,30 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_modem_dte.h"
#include "esp_log.h"
#include "esp_netif_ppp.h"
static const char *TAG = "esp-modem-netif";
/**
* @brief ESP32 Modem handle to be used as netif IO object
*/
typedef struct esp_modem_netif_driver_s {
struct esp_modem_netif_driver_s {
esp_netif_driver_base_t base; /*!< base structure reserved as esp-netif driver */
modem_dte_t *dte; /*!< ptr to the esp_modem objects (DTE) */
} esp_modem_netif_driver_t;
esp_modem_dte_t *dte; /*!< ptr to the esp_modem objects (DTE) */
};
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
modem_dte_t *dte = arg;
esp_modem_dte_t *dte = arg;
if (event_id < NETIF_PP_PHASE_OFFSET) {
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
// only notify the modem on state/error events, ignoring phase transitions
esp_modem_notify_ppp_netif_closed(dte);
}
}
/**
* @brief Transmit function called from esp_netif to output network stack data
*
@ -49,7 +53,7 @@ static void on_ppp_changed(void *arg, esp_event_base_t event_base,
*/
static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len)
{
modem_dte_t *dte = h;
esp_modem_dte_t *dte = h;
if (dte->send_data(dte, (const char *)buffer, len) > 0) {
return ESP_OK;
}
@ -66,10 +70,10 @@ static esp_err_t esp_modem_dte_transmit(void *h, void *buffer, size_t len)
*
* @return ESP_OK on success, modem-start error code if starting failed
*/
static esp_err_t esp_modem_post_attach_start(esp_netif_t * esp_netif, void * args)
static esp_err_t esp_modem_post_attach_init(esp_netif_t * esp_netif, void * args)
{
esp_modem_netif_driver_t *driver = args;
modem_dte_t *dte = driver->dte;
esp_modem_dte_t *dte = driver->dte;
const esp_netif_driver_ifconfig_t driver_ifconfig = {
.driver_free_rx_buffer = NULL,
.transmit = esp_modem_dte_transmit,
@ -77,16 +81,28 @@ static esp_err_t esp_modem_post_attach_start(esp_netif_t * esp_netif, void * arg
};
driver->base.netif = esp_netif;
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
// enable both events, so we could notify the modem layer if an error occurred/state changed
esp_netif_ppp_config_t ppp_config = {
.ppp_error_event_enabled = true,
.ppp_phase_event_enabled = true
};
esp_netif_ppp_set_params(esp_netif, &ppp_config);
// check if PPP error events are enabled, if not, do enable the error occurred/state changed
// to notify the modem layer when switching modes
esp_netif_ppp_config_t ppp_config;
esp_netif_ppp_get_params(esp_netif, &ppp_config);
if (!ppp_config.ppp_error_event_enabled) {
ppp_config.ppp_error_event_enabled = true;
esp_netif_ppp_set_params(esp_netif, &ppp_config);
}
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, dte));
return esp_modem_start_ppp(dte);
return ESP_OK;
}
/**
* @brief Post attach adapter for esp-modem with autostart functionality
*
*/
static esp_err_t esp_modem_post_attach_start(esp_netif_t * esp_netif, void * args)
{
esp_modem_netif_driver_t *driver = args;
ESP_ERROR_CHECK(esp_modem_post_attach_init(esp_netif, args));
return esp_modem_start_ppp(driver->dte);
}
/**
@ -105,7 +121,17 @@ static esp_err_t modem_netif_receive_cb(void *buffer, size_t len, void *context)
return ESP_OK;
}
void *esp_modem_netif_setup(modem_dte_t *dte)
esp_modem_netif_driver_t *esp_modem_netif_new(esp_modem_dte_t *dte)
{
esp_modem_netif_driver_t *driver = esp_modem_netif_setup(dte);
if (driver) {
driver->base.post_attach = esp_modem_post_attach_init;
return driver;
}
return NULL;
}
esp_modem_netif_driver_t *esp_modem_netif_setup(esp_modem_dte_t *dte)
{
esp_modem_netif_driver_t *driver = calloc(1, sizeof(esp_modem_netif_driver_t));
if (driver == NULL) {
@ -126,13 +152,18 @@ drv_create_failed:
return NULL;
}
void esp_modem_netif_teardown(void *h)
void esp_modem_netif_destroy(esp_modem_netif_driver_t *driver)
{
esp_netif_destroy(driver->base.netif);
return esp_modem_netif_teardown(driver);
}
void esp_modem_netif_teardown(esp_modem_netif_driver_t *driver)
{
esp_modem_netif_driver_t *driver = h;
free(driver);
}
esp_err_t esp_modem_netif_clear_default_handlers(void *h)
esp_err_t esp_modem_netif_clear_default_handlers(esp_modem_netif_driver_t *h)
{
esp_modem_netif_driver_t *driver = h;
esp_err_t ret;
@ -152,7 +183,7 @@ clear_event_failed:
}
esp_err_t esp_modem_netif_set_default_handlers(void *h, esp_netif_t * esp_netif)
esp_err_t esp_modem_netif_set_default_handlers(esp_modem_netif_driver_t *h, esp_netif_t * esp_netif)
{
esp_modem_netif_driver_t *driver = h;
esp_err_t ret;

View File

@ -0,0 +1,136 @@
// Copyright 2020 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 "esp_modem_recov_helper.h"
#include <stdlib.h>
#include "driver/gpio.h"
#include "esp_modem_internal.h"
#include "esp_log.h"
static const char *TAG = "esp_modem_recov_helper";
static void pulse_destroy(esp_modem_recov_gpio_t *pin)
{
free(pin);
}
static void retry_destroy(esp_modem_recov_resend_t *retry)
{
free(retry);
}
static void pulse_special(esp_modem_recov_gpio_t * pin, int active_width_ms, int inactive_width_ms)
{
gpio_set_level(pin->gpio_num, !pin->inactive_level);
esp_modem_wait_ms(active_width_ms);
gpio_set_level(pin->gpio_num, pin->inactive_level);
esp_modem_wait_ms(inactive_width_ms);
}
static void pulse(esp_modem_recov_gpio_t * pin)
{
gpio_set_level(pin->gpio_num, !pin->inactive_level);
esp_modem_wait_ms(pin->active_width_ms);
gpio_set_level(pin->gpio_num, pin->inactive_level);
esp_modem_wait_ms(pin->inactive_width_ms);
}
static esp_err_t esp_modem_retry_run(esp_modem_recov_resend_t * retry, void *param, void *result)
{
esp_modem_dce_t *dce = retry->dce;
int errors = 0;
int timeouts = 0;
esp_err_t err = ESP_FAIL;
while (timeouts <= retry->retries_after_timeout &&
errors <= retry->retries_after_error) {
if (timeouts || errors) {
// provide recovery action based on the defined strategy
if (retry->recover(retry, err, timeouts, errors) != ESP_OK) {
// fail the retry mechanism once the recovery fails
return ESP_FAIL;
}
}
if (retry->command) {
ESP_LOGD(TAG, "%s(%d): executing:%s...", __func__, __LINE__, retry->command );
}
// Execute the command
err = retry->orig_cmd(dce, param, result);
// Check for timeout
if (err == ESP_ERR_TIMEOUT) {
if (retry->command) {
ESP_LOGW(TAG, "%s(%d): Command:%s response timeout", __func__, __LINE__, retry->command);
}
timeouts++;
continue;
// Check for errors
} else if (err != ESP_OK) {
if (retry->command) {
ESP_LOGW(TAG, "%s(%d): Command:%s failed", __func__, __LINE__, retry->command);
}
errors++;
continue;
}
// Success
if (retry->command) {
ESP_LOGD(TAG, "%s(%d): Command:%s succeeded", __func__, __LINE__, retry->command);
}
return ESP_OK;
}
return err;
}
esp_modem_recov_resend_t *esp_modem_recov_resend_new(esp_modem_dce_t *dce, dce_command_t orig_cmd, esp_modem_retry_fn_t recover, int max_timeouts, int max_errors)
{
esp_modem_recov_resend_t * retry = calloc(1, sizeof(esp_modem_recov_resend_t));
ESP_MODEM_ERR_CHECK(retry, "failed to allocate pin structure", err);
retry->retries_after_error = max_errors;
retry->retries_after_timeout = max_timeouts;
retry->orig_cmd = orig_cmd;
retry->recover = recover;
retry->destroy = retry_destroy;
retry->dce = dce;
retry->run = esp_modem_retry_run;
return retry;
err:
return NULL;
}
esp_modem_recov_gpio_t *esp_modem_recov_gpio_new(int gpio_num, int inactive_level, int active_width_ms, int inactive_width_ms)
{
gpio_config_t io_config = {
.pin_bit_mask = BIT64(gpio_num),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io_config);
gpio_set_level(gpio_num, inactive_level);
gpio_set_level(gpio_num, inactive_level);
esp_modem_recov_gpio_t * pin = calloc(1, sizeof(esp_modem_recov_gpio_t));
ESP_MODEM_ERR_CHECK(pin, "failed to allocate pin structure", err);
pin->inactive_level = inactive_level;
pin->active_width_ms = active_width_ms;
pin->inactive_width_ms = inactive_width_ms;
pin->gpio_num = gpio_num;
pin->pulse = pulse;
pin->pulse_special = pulse_special;
pin->destroy = pulse_destroy;
return pin;
err:
return NULL;
}

104
esp_modem/src/esp_sim7600.c Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2020 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_common_commands.h"
#include "esp_modem_internal.h"
/**
* @brief This module supports SIM7600 module, which has a very similar interface
* to the BG96, so it just references most of the handlers from BG96 and implements
* only those that differ.
*/
static const char *TAG = "sim7600";
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t sim7600_handle_cbc(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CBC", strlen("+CBC"))) {
esp_modem_dce_cbc_ctx_t *cbc = dce->handle_line_ctx;
int32_t volts = 0, fraction = 0;
/* +CBC: <voltage in Volts> V*/
sscanf(line, "+CBC: %d.%dV", &volts, &fraction);
/* Since the "read_battery_status()" API (besides voltage) returns also values for BCS, BCL (charge status),
* which are not applicable to this modem, we return -1 to indicate invalid value
*/
cbc->bcs = -1; // BCS
cbc->bcl = -1; // BCL
cbc->battery_status = volts*1000 + fraction;
err = ESP_OK;
}
return err;
}
/**
* @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 sim7600_get_battery_status(esp_modem_dce_t *dce, void *p, void *r)
{
return esp_modem_dce_generic_command(dce, "AT+CBC\r", 20000,
sim7600_handle_cbc, r);
}
static esp_err_t sim7600_handle_power_down(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_OK;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_NO_CARRIER)) {
err = ESP_OK;
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
static esp_err_t sim7600_power_down(esp_modem_dce_t *dce, void *p, void *r)
{
return esp_modem_dce_generic_command(dce, " AT+CPOF\r", MODEM_COMMAND_TIMEOUT_POWEROFF,
sim7600_handle_power_down, NULL);
}
esp_err_t esp_modem_sim7600_specific_init(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(dce, "failed to specific init with zero dce", err_params);
if (dce->config.populate_command_list) {
ESP_MODEM_ERR_CHECK(esp_modem_set_default_command_list(dce) == ESP_OK, "esp_modem_dce_set_default_commands failed", err);
/* Update some commands which differ from the defaults */
ESP_MODEM_ERR_CHECK(esp_modem_command_list_set_cmd(dce, "power_down", sim7600_power_down) == ESP_OK, "esp_modem_dce_set_command failed", err);
ESP_MODEM_ERR_CHECK(
esp_modem_command_list_set_cmd(dce, "get_battery_status", sim7600_get_battery_status) == ESP_OK, "esp_modem_dce_set_command failed", err);
}
return ESP_OK;
err:
return ESP_FAIL;
err_params:
return ESP_ERR_INVALID_ARG;
}

View File

@ -0,0 +1,96 @@
// Copyright 2015-2020 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_common_commands.h"
#include "esp_modem_internal.h"
static const char *TAG = "esp_sim800";
/**
* @brief @brief Response to the SIM800 specific power-down command
*/
static esp_err_t sim800_handle_power_down(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, "POWER DOWN")) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_SUCCESS);
}
return err;
}
/**
* @brief Response to the SIM800 specific data mode command
*
*/
static esp_err_t sim800_handle_atd_ppp(esp_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, ESP_MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, ESP_MODEM_STATE_FAIL);
}
return err;
}
/**
* @brief Set data mode specific to SIM800
*
*/
static esp_err_t sim800_set_data_mode(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "ATD*99##\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE,
sim800_handle_atd_ppp, NULL);
}
/**
* @brief Specific power down command to SMI800
*
*/
static esp_err_t sim800_power_down(esp_modem_dce_t *dce, void *param, void *result)
{
return esp_modem_dce_generic_command(dce, "AT+CPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF,
sim800_handle_power_down, NULL);
}
static esp_err_t sim800_start_up(esp_modem_dce_t* dce)
{
if (esp_modem_dce_default_start_up(dce) != ESP_OK) {
esp_modem_wait_ms(30000); // SIM800 wakes-up 30s after sending a command
}
return esp_modem_dce_default_start_up(dce);
}
esp_err_t esp_modem_sim800_specific_init(esp_modem_dce_t *dce)
{
ESP_MODEM_ERR_CHECK(dce, "failed to specific init with zero dce", err_params);
/* Update some commands which differ from the defaults */
if (dce->config.populate_command_list) {
ESP_MODEM_ERR_CHECK(esp_modem_command_list_set_cmd(dce, "set_data_mode", sim800_set_data_mode) == ESP_OK, "esp_modem_dce_set_command failed", err);
ESP_MODEM_ERR_CHECK(esp_modem_command_list_set_cmd(dce, "power_down", sim800_power_down) == ESP_OK, "esp_modem_dce_set_command failed", err);
} else {
dce->set_data_mode = sim800_set_data_mode;
}
dce->start_up = sim800_start_up;
return ESP_OK;
err:
return ESP_FAIL;
err_params:
return ESP_ERR_INVALID_ARG;
}

View File

@ -1,90 +0,0 @@
// Copyright 2020 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"
#include "bg96_private.h"
/**
* @brief This module supports SIM7600 module, which has a very similar interface
* to the BG96, so it just references most of the handlers from BG96 and implements
* only those that differ.
*/
static const char *DCE_TAG = "sim7600";
/**
* @brief Handle response from AT+CBC
*/
static esp_err_t sim7600_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 */
int32_t **cbc = bg96_dce->priv_resource;
int32_t volts = 0, fraction = 0;
/* +CBC: <voltage in Volts> V*/
sscanf(line, "+CBC: %d.%dV", &volts, &fraction);
/* Since the "read_battery_status()" API (besides voltage) returns also values for BCS, BCL (charge status),
* which are not applicable to this modem, we return -1 to indicate invalid value
*/
*cbc[0] = -1; // BCS
*cbc[1] = -1; // BCL
*cbc[2] = volts*1000 + fraction;
err = ESP_OK;
}
return err;
}
/**
* @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 sim7600_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 = sim7600_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 Create and initialize SIM7600 object
*
*/
modem_dce_t *sim7600_init(modem_dte_t *dte)
{
modem_dce_t *dce = bg96_init(dte);
dte->dce->get_battery_status = sim7600_get_battery_status;
return dce;
}

View File

@ -1,492 +0,0 @@
// 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;
vTaskDelay(pdMS_TO_TICKS(1000)); // spec: 1s delay for the modem to recognize the escape sequence
if (dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) != ESP_OK) {
// "+++" Could fail if we are already in the command mode.
// in that case we ignore the timout and re-sync the modem
ESP_LOGI(DCE_TAG, "Sending \"+++\" command failed");
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);
} else {
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);
if (dce->state != MODEM_STATE_SUCCESS) {
// Initiate PPP mode could fail, if we've already "dialed" the data call before.
// in that case we retry with "ATO" to just resume the data mode
ESP_LOGD(DCE_TAG, "enter ppp mode failed, retry with ATO");
dce->handle_line = sim800_handle_atd_ppp;
DCE_CHECK(dte->send_cmd(dte, "ATO\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;
}