esp_modem: Refactor the modem to a standalone managed component

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,108 @@
/* Modem console example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
#include "esp_http_client.h"
static const char *TAG = "modem_console_httpget";
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if ((bool)evt->user_data &&
!esp_http_client_is_chunked_response(evt->client)) {
ESP_LOG_BUFFER_HEXDUMP(TAG, evt->data, evt->data_len, ESP_LOG_INFO);
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static struct {
struct arg_str *host;
struct arg_lit *hex;
struct arg_end *end;
} http_args;
static int do_http_client(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&http_args);
if (nerrors != 0) {
arg_print_errors(stderr, http_args.end, argv[0]);
return 1;
}
esp_http_client_config_t config = {
.event_handler = http_event_handler,
};
if (http_args.host->count > 0) {
config.url = http_args.host->sval[0];
} else {
config.url = "http://httpbin.org/get";
}
if (http_args.hex->count > 0) {
// show hex data from http-get
config.user_data = (void*)true;
}
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
return 0;
}
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
return 1;
}
void modem_console_register_http(void)
{
http_args.host = arg_str0(NULL, NULL, "<host>", "address or host-name to send GET request (defaults to http://httpbin.org/get)");
http_args.hex = arg_litn("p", "print-hex", 0, 1, "print hex output"),
http_args.end = arg_end(1);
const esp_console_cmd_t http_cmd = {
.command = "httpget",
.help = "http get command to test data mode",
.hint = NULL,
.func = &do_http_client,
.argtable = &http_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&http_cmd));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,422 @@
/* PPPoS Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "mqtt_client.h"
#include "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_log.h"
#if defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
#include "sim800.h"
#include "bg96.h"
#include "sim7600.h"
#endif
#define BROKER_URL "mqtt://mqtt.eclipse.org"
static const char *TAG = "pppos_example";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int STOP_BIT = BIT1;
static const int GOT_DATA_BIT = BIT2;
#if CONFIG_EXAMPLE_SEND_MSG
/**
* @brief This example will also show how to send short message using the infrastructure provided by esp modem library.
* @note Not all modem support SMG.
*
*/
static esp_err_t example_default_handle(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
}
return err;
}
static esp_err_t example_handle_cmgs(esp_modem_dce_t *dce, const char *line)
{
esp_err_t err = ESP_FAIL;
if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
} else if (strstr(line, MODEM_RESULT_CODE_ERROR)) {
err = esp_modem_process_command_done(dce, MODEM_STATE_FAIL);
} else if (!strncmp(line, "+CMGS", strlen("+CMGS"))) {
err = ESP_OK;
}
return err;
}
#define MODEM_SMS_MAX_LENGTH (128)
#define MODEM_COMMAND_TIMEOUT_SMS_MS (120000)
#define MODEM_PROMPT_TIMEOUT_MS (10)
static esp_err_t example_send_message_text(modem_dce_t *user_dce, const char *phone_num, const char *text)
{
esp_modem_dce_t *dce = &user_dce->parent;
modem_dte_t *dte = dce->dte;
dce->handle_line = example_default_handle;
/* Set text mode */
if (dte->send_cmd(dte, "AT+CMGF=1\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "set message format failed");
goto err;
}
ESP_LOGD(TAG, "set message format ok");
/* Specify character set */
dce->handle_line = example_default_handle;
if (dte->send_cmd(dte, "AT+CSCS=\"GSM\"\r", MODEM_COMMAND_TIMEOUT_DEFAULT) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "set character set failed");
goto err;
}
ESP_LOGD(TAG, "set character set ok");
/* send message */
char command[MODEM_SMS_MAX_LENGTH] = {0};
int length = snprintf(command, MODEM_SMS_MAX_LENGTH, "AT+CMGS=\"%s\"\r", phone_num);
/* set phone number and wait for "> " */
dte->send_wait(dte, command, length, "\r\n> ", MODEM_PROMPT_TIMEOUT_MS);
/* end with CTRL+Z */
snprintf(command, MODEM_SMS_MAX_LENGTH, "%s\x1A", text);
dce->handle_line = example_handle_cmgs;
if (dte->send_cmd(dte, command, MODEM_COMMAND_TIMEOUT_SMS_MS) != ESP_OK) {
ESP_LOGE(TAG, "send command failed");
goto err;
}
if (dce->state != MODEM_STATE_SUCCESS) {
ESP_LOGE(TAG, "send message failed");
goto err;
}
ESP_LOGD(TAG, "send message ok");
return ESP_OK;
err:
return ESP_FAIL;
}
#endif
static void modem_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
switch (event_id) {
case ESP_MODEM_EVENT_PPP_START:
ESP_LOGI(TAG, "Modem PPP Started");
break;
case ESP_MODEM_EVENT_PPP_STOP:
ESP_LOGI(TAG, "Modem PPP Stopped");
xEventGroupSetBits(event_group, STOP_BIT);
break;
case ESP_MODEM_EVENT_UNKNOWN:
ESP_LOGW(TAG, "Unknow line received: %s", (char *)event_data);
break;
default:
break;
}
}
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, "/topic/esp-pppos", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/esp-pppos", "esp32-pppos", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
xEventGroupSetBits(event_group, GOT_DATA_BIT);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
break;
}
return ESP_OK;
}
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "PPP state changed event %d", event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
/* User interrupted event from esp-netif */
esp_netif_t *netif = event_data;
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
}
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "IP event! %d", event_id);
if (event_id == IP_EVENT_PPP_GOT_IP) {
esp_netif_dns_info_t dns_info;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
esp_netif_t *netif = event->esp_netif;
ESP_LOGI(TAG, "Modem Connect to PPP Server");
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
esp_netif_get_dns_info(netif, 0, &dns_info);
ESP_LOGI(TAG, "Name Server1: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
esp_netif_get_dns_info(netif, 1, &dns_info);
ESP_LOGI(TAG, "Name Server2: " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
} else if (event_id == IP_EVENT_GOT_IP6) {
ESP_LOGI(TAG, "GOT IPv6 event!");
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip));
}
}
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config);
void app_main(void)
{
/* Init and register system/core components */
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
event_group = xEventGroupCreate();
/* Configure the DTE */
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
/* setup UART specific configuration based on kconfig options */
dte_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
dte_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
dte_config.rts_io_num = CONFIG_EXAMPLE_MODEM_UART_RTS_PIN;
dte_config.cts_io_num = CONFIG_EXAMPLE_MODEM_UART_CTS_PIN;
dte_config.rx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE;
dte_config.tx_buffer_size = CONFIG_EXAMPLE_MODEM_UART_TX_BUFFER_SIZE;
dte_config.pattern_queue_size = CONFIG_EXAMPLE_MODEM_UART_PATTERN_QUEUE_SIZE;
dte_config.event_queue_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_QUEUE_SIZE;
dte_config.event_task_stack_size = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_STACK_SIZE;
dte_config.event_task_priority = CONFIG_EXAMPLE_MODEM_UART_EVENT_TASK_PRIORITY;
dte_config.line_buffer_size = CONFIG_EXAMPLE_MODEM_UART_RX_BUFFER_SIZE / 2;
/* Configure the DCE */
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
/* Configure the PPP netif */
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
/* Run the modem demo app */
return modem_test_app(&dte_config, &dce_config,&netif_ppp_config);
}
#if !defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config)
{
/* create dte object */
esp_modem_dte_t *dte = esp_modem_dte_new(dte_config);
assert(dte != NULL);
/* create dce object */
#if CONFIG_EXAMPLE_MODEM_DEVICE_SIM800
dce_config->device = ESP_MODEM_DEVICE_SIM800;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96
dce_config->device = ESP_MODEM_DEVICE_BG96;
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600
dce_config->device = ESP_MODEM_DEVICE_SIM7600;
#else
#error "Unsupported DCE"
#endif
esp_modem_dce_t *dce = esp_modem_dce_new(dce_config);
assert(dce != NULL);
/* create netif object */
esp_netif_t *esp_netif = esp_netif_new(ppp_config);
assert(esp_netif);
#if !defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_NONE) && (defined(CONFIG_LWIP_PPP_PAP_SUPPORT) || defined(CONFIG_LWIP_PPP_CHAP_SUPPORT))
#if CONFIG_LWIP_PPP_PAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#else
#error "Unsupported AUTH Negotiation"
#endif
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
/* attach the DCE, DTE, netif to initialize the modem */
ESP_ERROR_CHECK(esp_modem_default_attach(dte, dce, esp_netif));
while (1) {
ESP_ERROR_CHECK(esp_modem_default_start(dte));
/* Start PPP mode */
ESP_ERROR_CHECK(esp_modem_start_ppp(dte));
/* Wait for IP address */
xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
/* Config MQTT */
esp_mqtt_client_config_t mqtt_config = {
.uri = BROKER_URL,
.event_handle = mqtt_event_handler,
};
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_start(mqtt_client);
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
esp_mqtt_client_destroy(mqtt_client);
/* Exit PPP mode */
ESP_ERROR_CHECK(esp_modem_stop_ppp(dte));
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "Restart after 60 seconds");
vTaskDelay(pdMS_TO_TICKS(60000));
}
/* Default destroy all modem sub-units attached to it (DTE, DCE, netif) */
ESP_ERROR_CHECK(esp_modem_default_destroy(dte));
}
#else // defined(CONFIG_EXAMPLE_MODEM_LEGACY_API)
static void modem_test_app(esp_modem_dte_config_t *dte_config, esp_modem_dce_config_t *dce_config, esp_netif_config_t *ppp_config)
{
/* create dte object */
modem_dte_t *dte = esp_modem_dte_init(dte_config);
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
// Init netif object
esp_netif_t *esp_netif = esp_netif_new(ppp_config);
assert(esp_netif);
void *modem_netif_adapter = esp_modem_netif_setup(dte);
esp_modem_netif_set_default_handlers(modem_netif_adapter, esp_netif);
while (1) {
modem_dce_t *dce = NULL;
/* create dce object */
#if CONFIG_EXAMPLE_MODEM_DEVICE_SIM800
dce = sim800_init(dte);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_BG96
dce = bg96_init(dte);
#elif CONFIG_EXAMPLE_MODEM_DEVICE_SIM7600
dce = sim7600_init(dte);
#else
#error "Unsupported DCE"
#endif
assert(dce != NULL);
ESP_ERROR_CHECK(dce->set_flow_ctrl(dce, ESP_MODEM_FLOW_CONTROL_NONE));
ESP_ERROR_CHECK(dce->store_profile(dce));
/* Print Module ID, Operator, IMEI, IMSI */
ESP_LOGI(TAG, "Module: %s", dce->name);
ESP_LOGI(TAG, "Operator: %s", dce->oper);
ESP_LOGI(TAG, "IMEI: %s", dce->imei);
ESP_LOGI(TAG, "IMSI: %s", dce->imsi);
/* Get signal quality */
uint32_t rssi = 0, ber = 0;
ESP_ERROR_CHECK(dce->get_signal_quality(dce, &rssi, &ber));
ESP_LOGI(TAG, "rssi: %d, ber: %d", rssi, ber);
/* Get battery voltage */
uint32_t voltage = 0, bcs = 0, bcl = 0;
ESP_ERROR_CHECK(dce->get_battery_status(dce, &bcs, &bcl, &voltage));
ESP_LOGI(TAG, "Battery voltage: %d mV", voltage);
/* setup PPPoS network parameters */
#if !defined(CONFIG_EXAMPLE_MODEM_PPP_AUTH_NONE) && (defined(CONFIG_LWIP_PPP_PAP_SUPPORT) || defined(CONFIG_LWIP_PPP_CHAP_SUPPORT))
#if CONFIG_LWIP_PPP_PAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_PAP;
#elif CONFIG_LWIP_PPP_CHAP_SUPPORT
esp_netif_auth_type_t auth_type = NETIF_PPP_AUTHTYPE_CHAP;
#else
#error "Unsupported AUTH Negotiation"
#endif
esp_netif_ppp_set_auth(esp_netif, auth_type, CONFIG_EXAMPLE_MODEM_PPP_AUTH_USERNAME, CONFIG_EXAMPLE_MODEM_PPP_AUTH_PASSWORD);
#endif
/* attach the modem to the network interface */
esp_netif_attach(esp_netif, modem_netif_adapter);
/* Wait for IP address */
xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
/* Config MQTT */
esp_mqtt_client_config_t mqtt_config = {
.uri = BROKER_URL,
.event_handle = mqtt_event_handler,
};
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
esp_mqtt_client_start(mqtt_client);
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
esp_mqtt_client_destroy(mqtt_client);
/* Exit PPP mode */
ESP_ERROR_CHECK(esp_modem_stop_ppp(dte));
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
#if CONFIG_EXAMPLE_SEND_MSG
const char *message = "Welcome to ESP32!";
ESP_ERROR_CHECK(example_send_message_text(dce, CONFIG_EXAMPLE_SEND_MSG_PEER_PHONE_NUMBER, message));
ESP_LOGI(TAG, "Send send message [%s] ok", message);
#endif
/* Power down module */
ESP_ERROR_CHECK(dce->power_down(dce));
ESP_LOGI(TAG, "Power down");
ESP_ERROR_CHECK(dce->deinit(dce));
ESP_LOGI(TAG, "Restart after 60 seconds");
vTaskDelay(pdMS_TO_TICKS(60000));
}
/* Unregister events, destroy the netif adapter and destroy its esp-netif instance */
esp_modem_netif_clear_default_handlers(modem_netif_adapter);
esp_modem_netif_teardown(modem_netif_adapter);
esp_netif_destroy(esp_netif);
ESP_ERROR_CHECK(dte->deinit(dte));
}
#endif // CONFIG_EXAMPLE_MODEM_LEGACY_API

View File

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