From c8e89098bdcd392633429b69b2a2c8571a7fd90a Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 18 Nov 2020 21:20:35 +0100 Subject: [PATCH] esp_modem: Refactor the modem to a standalone managed component --- esp_modem/.gitignore | 90 +++ esp_modem/CMakeLists.txt | 22 +- esp_modem/Kconfig | 17 +- esp_modem/README.md | 74 ++ esp_modem/examples/ap_to_pppos/CMakeLists.txt | 8 + esp_modem/examples/ap_to_pppos/Makefile | 9 + esp_modem/examples/ap_to_pppos/README.md | 11 + .../examples/ap_to_pppos/main/CMakeLists.txt | 3 + .../ap_to_pppos/main/Kconfig.projbuild | 26 + .../ap_to_pppos/main/ap2pppos_example_main.c | 193 ++++++ .../examples/ap_to_pppos/main/component.mk | 8 + .../ap_to_pppos/main/idf_component.yml | 7 + .../examples/ap_to_pppos/main/modem_board.c | 162 +++++ .../examples/ap_to_pppos/sdkconfig.defaults | 12 + .../examples/modem_console/CMakeLists.txt | 8 + esp_modem/examples/modem_console/Makefile | 11 + esp_modem/examples/modem_console/README.md | 10 + .../modem_console/main/CMakeLists.txt | 4 + .../examples/modem_console/main/component.mk | 4 + .../modem_console/main/httpget_handle.c | 108 +++ .../modem_console/main/modem_console_main.c | 307 +++++++++ .../examples/modem_console/main/ping_handle.c | 141 ++++ .../examples/modem_console/sdkconfig.defaults | 8 + .../examples/pppos_client/CMakeLists.txt | 10 + esp_modem/examples/pppos_client/Makefile | 9 + esp_modem/examples/pppos_client/README.md | 10 + .../examples/pppos_client/main/CMakeLists.txt | 2 + .../pppos_client/main/Kconfig.projbuild | 143 ++++ .../examples/pppos_client/main/component.mk | 4 + .../pppos_client/main/pppos_client_main.c | 422 ++++++++++++ .../examples/pppos_client/sdkconfig.defaults | 7 + esp_modem/idf_component.yml | 3 + esp_modem/include/esp_modem.h | 220 +++++- esp_modem/include/esp_modem_compat.h | 62 -- esp_modem/include/esp_modem_dce.h | 253 +++++-- .../include/esp_modem_dce_common_commands.h | 359 ++++++++++ esp_modem/include/esp_modem_dce_service.h | 131 ---- esp_modem/include/esp_modem_dte.h | 89 ++- esp_modem/include/esp_modem_netif.h | 62 +- esp_modem/include/esp_modem_recov_helper.h | 89 +++ esp_modem/{include => include_compat}/bg96.h | 21 +- esp_modem/include_compat/esp_modem_compat.h | 71 ++ .../{include => include_compat}/sim7600.h | 21 +- .../{include => include_compat}/sim800.h | 21 +- .../esp_modem_dce_command_lib.h | 62 ++ .../esp_modem_device_specific_dce.h | 51 ++ .../private_include/esp_modem_dte_internal.h | 57 ++ .../{bg96_private.h => esp_modem_internal.h} | 37 +- esp_modem/src/bg96.c | 472 ------------- esp_modem/src/esp_bg96.c | 25 + esp_modem/src/esp_modem.c | 643 ++++-------------- esp_modem/src/esp_modem_compat.c | 287 ++++++-- esp_modem/src/esp_modem_dce.c | 190 ++++++ esp_modem/src/esp_modem_dce_command_lib.c | 204 ++++++ esp_modem/src/esp_modem_dce_common_commands.c | 357 ++++++++++ esp_modem/src/esp_modem_dce_service.c | 126 ---- esp_modem/src/esp_modem_dte.c | 505 ++++++++++++++ esp_modem/src/esp_modem_netif.c | 75 +- esp_modem/src/esp_modem_recov_helper.c | 136 ++++ esp_modem/src/esp_sim7600.c | 104 +++ esp_modem/src/esp_sim800.c | 96 +++ esp_modem/src/sim7600.c | 90 --- esp_modem/src/sim800.c | 492 -------------- 63 files changed, 5081 insertions(+), 2180 deletions(-) create mode 100644 esp_modem/.gitignore create mode 100644 esp_modem/README.md create mode 100644 esp_modem/examples/ap_to_pppos/CMakeLists.txt create mode 100644 esp_modem/examples/ap_to_pppos/Makefile create mode 100644 esp_modem/examples/ap_to_pppos/README.md create mode 100644 esp_modem/examples/ap_to_pppos/main/CMakeLists.txt create mode 100644 esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild create mode 100644 esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c create mode 100644 esp_modem/examples/ap_to_pppos/main/component.mk create mode 100644 esp_modem/examples/ap_to_pppos/main/idf_component.yml create mode 100644 esp_modem/examples/ap_to_pppos/main/modem_board.c create mode 100644 esp_modem/examples/ap_to_pppos/sdkconfig.defaults create mode 100644 esp_modem/examples/modem_console/CMakeLists.txt create mode 100644 esp_modem/examples/modem_console/Makefile create mode 100644 esp_modem/examples/modem_console/README.md create mode 100644 esp_modem/examples/modem_console/main/CMakeLists.txt create mode 100644 esp_modem/examples/modem_console/main/component.mk create mode 100644 esp_modem/examples/modem_console/main/httpget_handle.c create mode 100644 esp_modem/examples/modem_console/main/modem_console_main.c create mode 100644 esp_modem/examples/modem_console/main/ping_handle.c create mode 100644 esp_modem/examples/modem_console/sdkconfig.defaults create mode 100644 esp_modem/examples/pppos_client/CMakeLists.txt create mode 100644 esp_modem/examples/pppos_client/Makefile create mode 100644 esp_modem/examples/pppos_client/README.md create mode 100644 esp_modem/examples/pppos_client/main/CMakeLists.txt create mode 100644 esp_modem/examples/pppos_client/main/Kconfig.projbuild create mode 100644 esp_modem/examples/pppos_client/main/component.mk create mode 100644 esp_modem/examples/pppos_client/main/pppos_client_main.c create mode 100644 esp_modem/examples/pppos_client/sdkconfig.defaults create mode 100644 esp_modem/idf_component.yml delete mode 100644 esp_modem/include/esp_modem_compat.h create mode 100644 esp_modem/include/esp_modem_dce_common_commands.h delete mode 100644 esp_modem/include/esp_modem_dce_service.h create mode 100644 esp_modem/include/esp_modem_recov_helper.h rename esp_modem/{include => include_compat}/bg96.h (61%) create mode 100644 esp_modem/include_compat/esp_modem_compat.h rename esp_modem/{include => include_compat}/sim7600.h (61%) rename esp_modem/{include => include_compat}/sim800.h (61%) create mode 100644 esp_modem/private_include/esp_modem_dce_command_lib.h create mode 100644 esp_modem/private_include/esp_modem_device_specific_dce.h create mode 100644 esp_modem/private_include/esp_modem_dte_internal.h rename esp_modem/private_include/{bg96_private.h => esp_modem_internal.h} (69%) delete mode 100644 esp_modem/src/bg96.c create mode 100644 esp_modem/src/esp_bg96.c create mode 100644 esp_modem/src/esp_modem_dce.c create mode 100644 esp_modem/src/esp_modem_dce_command_lib.c create mode 100644 esp_modem/src/esp_modem_dce_common_commands.c delete mode 100644 esp_modem/src/esp_modem_dce_service.c create mode 100644 esp_modem/src/esp_modem_dte.c create mode 100644 esp_modem/src/esp_modem_recov_helper.c create mode 100644 esp_modem/src/esp_sim7600.c create mode 100644 esp_modem/src/esp_sim800.c delete mode 100644 esp_modem/src/sim7600.c delete mode 100644 esp_modem/src/sim800.c diff --git a/esp_modem/.gitignore b/esp_modem/.gitignore new file mode 100644 index 000000000..61e02dae7 --- /dev/null +++ b/esp_modem/.gitignore @@ -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 diff --git a/esp_modem/CMakeLists.txt b/esp_modem/CMakeLists.txt index 50fa1a4b7..48235631f 100644 --- a/esp_modem/CMakeLists.txt +++ b/esp_modem/CMakeLists.txt @@ -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) diff --git a/esp_modem/Kconfig b/esp_modem/Kconfig index 932c1c091..e441e4cf5 100644 --- a/esp_modem/Kconfig +++ b/esp_modem/Kconfig @@ -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 diff --git a/esp_modem/README.md b/esp_modem/README.md new file mode 100644 index 000000000..e8aeef334 --- /dev/null +++ b/esp_modem/README.md @@ -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. + diff --git a/esp_modem/examples/ap_to_pppos/CMakeLists.txt b/esp_modem/examples/ap_to_pppos/CMakeLists.txt new file mode 100644 index 000000000..c7d69911f --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/CMakeLists.txt @@ -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) diff --git a/esp_modem/examples/ap_to_pppos/Makefile b/esp_modem/examples/ap_to_pppos/Makefile new file mode 100644 index 000000000..06822ceac --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/Makefile @@ -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 + diff --git a/esp_modem/examples/ap_to_pppos/README.md b/esp_modem/examples/ap_to_pppos/README.md new file mode 100644 index 000000000..04f8b2dcd --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/README.md @@ -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. diff --git a/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt b/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt new file mode 100644 index 000000000..a18b3c44a --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "ap2pppos_example_main.c" + "modem_board.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild b/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild new file mode 100644 index 000000000..19e9f86e0 --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/Kconfig.projbuild @@ -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 diff --git a/esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c b/esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c new file mode 100644 index 000000000..7830c8f60 --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/ap2pppos_example_main.c @@ -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 +#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)); + } + +} diff --git a/esp_modem/examples/ap_to_pppos/main/component.mk b/esp_modem/examples/ap_to_pppos/main/component.mk new file mode 100644 index 000000000..61f8990c3 --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/component.mk @@ -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. +# diff --git a/esp_modem/examples/ap_to_pppos/main/idf_component.yml b/esp_modem/examples/ap_to_pppos/main/idf_component.yml new file mode 100644 index 000000000..12c900f0b --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/idf_component.yml @@ -0,0 +1,7 @@ +version: "0.0.2" +targets: + - esp32 +description: pppos_client to AP +dependencies: + espressif/esp_modem: + version: "~0.0.2" diff --git a/esp_modem/examples/ap_to_pppos/main/modem_board.c b/esp_modem/examples/ap_to_pppos/main/modem_board.c new file mode 100644 index 000000000..d52987086 --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/main/modem_board.c @@ -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 +#include +#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; +} \ No newline at end of file diff --git a/esp_modem/examples/ap_to_pppos/sdkconfig.defaults b/esp_modem/examples/ap_to_pppos/sdkconfig.defaults new file mode 100644 index 000000000..03925c80d --- /dev/null +++ b/esp_modem/examples/ap_to_pppos/sdkconfig.defaults @@ -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 + diff --git a/esp_modem/examples/modem_console/CMakeLists.txt b/esp_modem/examples/modem_console/CMakeLists.txt new file mode 100644 index 000000000..8df0278b8 --- /dev/null +++ b/esp_modem/examples/modem_console/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "../..") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(modem-console) diff --git a/esp_modem/examples/modem_console/Makefile b/esp_modem/examples/modem_console/Makefile new file mode 100644 index 000000000..1adbf2ba1 --- /dev/null +++ b/esp_modem/examples/modem_console/Makefile @@ -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 + diff --git a/esp_modem/examples/modem_console/README.md b/esp_modem/examples/modem_console/README.md new file mode 100644 index 000000000..1219bce0e --- /dev/null +++ b/esp_modem/examples/modem_console/README.md @@ -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. diff --git a/esp_modem/examples/modem_console/main/CMakeLists.txt b/esp_modem/examples/modem_console/main/CMakeLists.txt new file mode 100644 index 000000000..435f5ea3a --- /dev/null +++ b/esp_modem/examples/modem_console/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "modem_console_main.c" + "httpget_handle.c" + "ping_handle.c" + INCLUDE_DIRS ".") diff --git a/esp_modem/examples/modem_console/main/component.mk b/esp_modem/examples/modem_console/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/esp_modem/examples/modem_console/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/esp_modem/examples/modem_console/main/httpget_handle.c b/esp_modem/examples/modem_console/main/httpget_handle.c new file mode 100644 index 000000000..da15b75d9 --- /dev/null +++ b/esp_modem/examples/modem_console/main/httpget_handle.c @@ -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 +#include +#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, "", "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)); +} \ No newline at end of file diff --git a/esp_modem/examples/modem_console/main/modem_console_main.c b/esp_modem/examples/modem_console/main/modem_console_main.c new file mode 100644 index 000000000..6a2fe5610 --- /dev/null +++ b/esp_modem/examples/modem_console/main/modem_console_main.c @@ -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 +#include +#include +#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, "", "Symbolic name of DCE command"); + at_args.param_int = arg_int0("i", "int", "", "Input parameter of integer type"); + at_args.param_str = arg_str0("s", "str", "", "Input parameter of string type"); + at_args.param_pdp = arg_str0("p", "pdp", "", "Comma separated string with PDP context"); + at_args.param_bool = arg_str0("b", "bool", "", "Input parameter of bool type"); + at_args.param = arg_str0(NULL, NULL, "", "Default input argument treated as string"); + at_args.result = arg_str0("o", "out", "", "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, "", "AT command to send to the modem"); + generic_at_args.timeout = arg_int0("t", "timeout", "", "command timeout"); + generic_at_args.pattern = arg_str0("p", "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, "", "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)); +} diff --git a/esp_modem/examples/modem_console/main/ping_handle.c b/esp_modem/examples/modem_console/main/ping_handle.c new file mode 100644 index 000000000..cc01cdfb3 --- /dev/null +++ b/esp_modem/examples/modem_console/main/ping_handle.c @@ -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 +#include +#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", "", "Time to wait for a response, in seconds"); + ping_args.count = arg_int0("c", "count", "", "Stop after sending count packets"); + ping_args.host = arg_str1(NULL, NULL, "", "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)); +} \ No newline at end of file diff --git a/esp_modem/examples/modem_console/sdkconfig.defaults b/esp_modem/examples/modem_console/sdkconfig.defaults new file mode 100644 index 000000000..893755f68 --- /dev/null +++ b/esp_modem/examples/modem_console/sdkconfig.defaults @@ -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 diff --git a/esp_modem/examples/pppos_client/CMakeLists.txt b/esp_modem/examples/pppos_client/CMakeLists.txt new file mode 100644 index 000000000..43372227e --- /dev/null +++ b/esp_modem/examples/pppos_client/CMakeLists.txt @@ -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) + + diff --git a/esp_modem/examples/pppos_client/Makefile b/esp_modem/examples/pppos_client/Makefile new file mode 100644 index 000000000..25571d087 --- /dev/null +++ b/esp_modem/examples/pppos_client/Makefile @@ -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 + diff --git a/esp_modem/examples/pppos_client/README.md b/esp_modem/examples/pppos_client/README.md new file mode 100644 index 000000000..376f37eb8 --- /dev/null +++ b/esp_modem/examples/pppos_client/README.md @@ -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. diff --git a/esp_modem/examples/pppos_client/main/CMakeLists.txt b/esp_modem/examples/pppos_client/main/CMakeLists.txt new file mode 100644 index 000000000..679e282a0 --- /dev/null +++ b/esp_modem/examples/pppos_client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "pppos_client_main.c" + INCLUDE_DIRS ".") diff --git a/esp_modem/examples/pppos_client/main/Kconfig.projbuild b/esp_modem/examples/pppos_client/main/Kconfig.projbuild new file mode 100644 index 000000000..68b4e2048 --- /dev/null +++ b/esp_modem/examples/pppos_client/main/Kconfig.projbuild @@ -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 diff --git a/esp_modem/examples/pppos_client/main/component.mk b/esp_modem/examples/pppos_client/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/esp_modem/examples/pppos_client/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/esp_modem/examples/pppos_client/main/pppos_client_main.c b/esp_modem/examples/pppos_client/main/pppos_client_main.c new file mode 100644 index 000000000..455a77f9c --- /dev/null +++ b/esp_modem/examples/pppos_client/main/pppos_client_main.c @@ -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 +#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 diff --git a/esp_modem/examples/pppos_client/sdkconfig.defaults b/esp_modem/examples/pppos_client/sdkconfig.defaults new file mode 100644 index 000000000..19f2ce6cd --- /dev/null +++ b/esp_modem/examples/pppos_client/sdkconfig.defaults @@ -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 diff --git a/esp_modem/idf_component.yml b/esp_modem/idf_component.yml new file mode 100644 index 000000000..943252501 --- /dev/null +++ b/esp_modem/idf_component.yml @@ -0,0 +1,3 @@ +version: "0.0.6" +name: esp_modem +description: esp modem diff --git a/esp_modem/include/esp_modem.h b/esp_modem/include/esp_modem.h index 4440b7a5a..75e278d4d 100644 --- a/esp_modem/include/esp_modem.h +++ b/esp_modem/include/esp_modem.h @@ -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 } diff --git a/esp_modem/include/esp_modem_compat.h b/esp_modem/include/esp_modem_compat.h deleted file mode 100644 index 592fd12e6..000000000 --- a/esp_modem/include/esp_modem_compat.h +++ /dev/null @@ -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 diff --git a/esp_modem/include/esp_modem_dce.h b/esp_modem/include/esp_modem_dce.h index 7c180b421..f260d042b 100644 --- a/esp_modem/include/esp_modem_dce.h +++ b/esp_modem/include/esp_modem_dce.h @@ -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 diff --git a/esp_modem/include/esp_modem_dce_common_commands.h b/esp_modem/include/esp_modem_dce_common_commands.h new file mode 100644 index 000000000..8d8f15b7b --- /dev/null +++ b/esp_modem/include/esp_modem_dce_common_commands.h @@ -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); diff --git a/esp_modem/include/esp_modem_dce_service.h b/esp_modem/include/esp_modem_dce_service.h deleted file mode 100644 index 9b9a3a6c4..000000000 --- a/esp_modem/include/esp_modem_dce_service.h +++ /dev/null @@ -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 diff --git a/esp_modem/include/esp_modem_dte.h b/esp_modem/include/esp_modem_dte.h index ba7380c05..67ada81dd 100644 --- a/esp_modem/include/esp_modem_dte.h +++ b/esp_modem/include/esp_modem_dte.h @@ -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 diff --git a/esp_modem/include/esp_modem_netif.h b/esp_modem/include/esp_modem_netif.h index 1eac1b497..1455a2a60 100644 --- a/esp_modem/include/esp_modem_netif.h +++ b/esp_modem/include/esp_modem_netif.h @@ -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 } diff --git a/esp_modem/include/esp_modem_recov_helper.h b/esp_modem/include/esp_modem_recov_helper.h new file mode 100644 index 000000000..ec8c95388 --- /dev/null +++ b/esp_modem/include/esp_modem_recov_helper.h @@ -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); + + diff --git a/esp_modem/include/bg96.h b/esp_modem/include_compat/bg96.h similarity index 61% rename from esp_modem/include/bg96.h rename to esp_modem/include_compat/bg96.h index f31d0a25c..02cb12f31 100644 --- a/esp_modem/include/bg96.h +++ b/esp_modem/include_compat/bg96.h @@ -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" diff --git a/esp_modem/include_compat/esp_modem_compat.h b/esp_modem/include_compat/esp_modem_compat.h new file mode 100644 index 000000000..b692ca083 --- /dev/null +++ b/esp_modem/include_compat/esp_modem_compat.h @@ -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 \ No newline at end of file diff --git a/esp_modem/include/sim7600.h b/esp_modem/include_compat/sim7600.h similarity index 61% rename from esp_modem/include/sim7600.h rename to esp_modem/include_compat/sim7600.h index 4fbb9a1b1..02cb12f31 100644 --- a/esp_modem/include/sim7600.h +++ b/esp_modem/include_compat/sim7600.h @@ -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" diff --git a/esp_modem/include/sim800.h b/esp_modem/include_compat/sim800.h similarity index 61% rename from esp_modem/include/sim800.h rename to esp_modem/include_compat/sim800.h index f0455c7f8..02cb12f31 100644 --- a/esp_modem/include/sim800.h +++ b/esp_modem/include_compat/sim800.h @@ -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" diff --git a/esp_modem/private_include/esp_modem_dce_command_lib.h b/esp_modem/private_include/esp_modem_dce_command_lib.h new file mode 100644 index 000000000..b29ac7b8f --- /dev/null +++ b/esp_modem/private_include/esp_modem_dce_command_lib.h @@ -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 diff --git a/esp_modem/private_include/esp_modem_device_specific_dce.h b/esp_modem/private_include/esp_modem_device_specific_dce.h new file mode 100644 index 000000000..57b321b49 --- /dev/null +++ b/esp_modem/private_include/esp_modem_device_specific_dce.h @@ -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 \ No newline at end of file diff --git a/esp_modem/private_include/esp_modem_dte_internal.h b/esp_modem/private_include/esp_modem_dte_internal.h new file mode 100644 index 000000000..288ac17cc --- /dev/null +++ b/esp_modem/private_include/esp_modem_dte_internal.h @@ -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 diff --git a/esp_modem/private_include/bg96_private.h b/esp_modem/private_include/esp_modem_internal.h similarity index 69% rename from esp_modem/private_include/bg96_private.h rename to esp_modem/private_include/esp_modem_internal.h index 6b245fc8f..fd5d1312f 100644 --- a/esp_modem/private_include/bg96_private.h +++ b/esp_modem/private_include/esp_modem_internal.h @@ -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 diff --git a/esp_modem/src/bg96.c b/esp_modem/src/bg96.c deleted file mode 100644 index 892d13c0a..000000000 --- a/esp_modem/src/bg96.c +++ /dev/null @@ -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 -#include -#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: , */ - 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: ,, */ - 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: [, [, ]] */ - 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; -} diff --git a/esp_modem/src/esp_bg96.c b/esp_modem/src/esp_bg96.c new file mode 100644 index 000000000..cad5484d8 --- /dev/null +++ b/esp_modem/src/esp_bg96.c @@ -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; +} diff --git a/esp_modem/src/esp_modem.c b/esp_modem/src/esp_modem.c index f49f0015b..160ba349b 100644 --- a/esp_modem/src/esp_modem.c +++ b/esp_modem/src/esp_modem.c @@ -12,524 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. #include -#include -#include #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; ireceive_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; -} \ No newline at end of file + 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; +} diff --git a/esp_modem/src/esp_modem_compat.c b/esp_modem/src/esp_modem_compat.c index 297acc248..dd2ef42be 100644 --- a/esp_modem/src/esp_modem_compat.c +++ b/esp_modem/src/esp_modem_compat.c @@ -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 -#include "esp_netif.h" -#include "esp_netif_ppp.h" -#include "esp_modem.h" -#include "esp_modem_netif.h" +#include #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); } diff --git a/esp_modem/src/esp_modem_dce.c b/esp_modem/src/esp_modem_dce.c new file mode 100644 index 000000000..1d1e27821 --- /dev/null +++ b/esp_modem/src/esp_modem_dce.c @@ -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 + +#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; +} \ No newline at end of file diff --git a/esp_modem/src/esp_modem_dce_command_lib.c b/esp_modem/src/esp_modem_dce_command_lib.c new file mode 100644 index 000000000..c811124b8 --- /dev/null +++ b/esp_modem/src/esp_modem_dce_command_lib.c @@ -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 +#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; +} + + diff --git a/esp_modem/src/esp_modem_dce_common_commands.c b/esp_modem/src/esp_modem_dce_common_commands.c new file mode 100644 index 000000000..fa862d9bc --- /dev/null +++ b/esp_modem/src/esp_modem_dce_common_commands.c @@ -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 +#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: ,, */ + 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: , */ + 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: [, [, ]] */ + 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); +} \ No newline at end of file diff --git a/esp_modem/src/esp_modem_dce_service.c b/esp_modem/src/esp_modem_dce_service.c deleted file mode 100644 index 4b73534cf..000000000 --- a/esp_modem/src/esp_modem_dce_service.c +++ /dev/null @@ -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 -#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; -} diff --git a/esp_modem/src/esp_modem_dte.c b/esp_modem/src/esp_modem_dte.c new file mode 100644 index 000000000..e61ea7175 --- /dev/null +++ b/esp_modem/src/esp_modem_dte.c @@ -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 +#include +#include +#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; ireceive_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); +} \ No newline at end of file diff --git a/esp_modem/src/esp_modem_netif.c b/esp_modem/src/esp_modem_netif.c index 1c6f4bcf1..35c5b03a4 100644 --- a/esp_modem/src/esp_modem_netif.c +++ b/esp_modem/src/esp_modem_netif.c @@ -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; diff --git a/esp_modem/src/esp_modem_recov_helper.c b/esp_modem/src/esp_modem_recov_helper.c new file mode 100644 index 000000000..16bc66fb0 --- /dev/null +++ b/esp_modem/src/esp_modem_recov_helper.c @@ -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 +#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; +} diff --git a/esp_modem/src/esp_sim7600.c b/esp_modem/src/esp_sim7600.c new file mode 100644 index 000000000..652b105d4 --- /dev/null +++ b/esp_modem/src/esp_sim7600.c @@ -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 +#include +#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: 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; +} diff --git a/esp_modem/src/esp_sim800.c b/esp_modem/src/esp_sim800.c new file mode 100644 index 000000000..850e569b4 --- /dev/null +++ b/esp_modem/src/esp_sim800.c @@ -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 +#include +#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; +} diff --git a/esp_modem/src/sim7600.c b/esp_modem/src/sim7600.c deleted file mode 100644 index 3cf8907de..000000000 --- a/esp_modem/src/sim7600.c +++ /dev/null @@ -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 -#include -#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: 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; -} diff --git a/esp_modem/src/sim800.c b/esp_modem/src/sim800.c deleted file mode 100644 index 7bc1ece73..000000000 --- a/esp_modem/src/sim800.c +++ /dev/null @@ -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 -#include -#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: , */ - 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: ,, */ - 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: [, [, ]] */ - 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; -}