feat(examples): Add multiple netif demo: eth+wifi+PPP

This commit is contained in:
David Cermak
2023-04-26 19:10:16 +02:00
parent 4f1769ec71
commit 00d7c40848
12 changed files with 824 additions and 0 deletions

View File

@ -0,0 +1,6 @@
# 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.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(multiple_netifs)

View File

@ -0,0 +1,25 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# Multiple Interface example
## Overview
This example demonstrates working with multiple different interfaces with different priorities. It creates these interfaces and tries to connect:
* WiFi Station
* Ethernet using ESP32 internal ethernet driver
* PPPoS over cellular modem
## How to use example
* Set the priorities and the host name for the example to ICMP ping.
* The example will initialize all interfaces
* The example will start looping and checking connectivity to the host name
* It prints the default interface and ping output
* It tries to reconfigure DNS server if host name resolution fails
* It tries to manually change the default interface if connection fails
### Hardware Required
To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html).
You would also need a modem connected to the board using UART interface.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS ethernet_netif.c multi_netif_main.c wifi_connect.c check_connection.c ppp_connect.c
INCLUDE_DIRS ".")

View File

@ -0,0 +1,42 @@
menu "Connection 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_MAXIMUM_RETRY
int "Maximum retry"
default 0
help
Set the Maximum retry to avoid station reconnecting. Set to 0 to keep retrying indefinitely.
config EXAMPLE_MODEM_PPP_APN
string "Set MODEM APN"
default "default.apn"
help
Set APN (Access Point Name), a logical name to choose data network
config EXAMPLE_MODEM_UART_TX_PIN
int "TXD Pin Number"
default 15
range 0 31
help
Pin number of UART TX.
config EXAMPLE_MODEM_UART_RX_PIN
int "RXD Pin Number"
default 14
range 0 31
help
Pin number of UART RX.
endmenu

View File

@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Checks network connectivity by pinging configured host
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 "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "ping/ping_sock.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#define SUCCESS (1)
#define FAIL (2)
static const char *TAG = "check_connection";
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, "%" PRIu32 " bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 " ms",
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", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
EventGroupHandle_t events = 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 ---", 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, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %"PRIu32"ms\n",
transmitted, received, loss, total_time_ms);
xEventGroupSetBits(events, received == 0 ? FAIL : SUCCESS);
}
esp_err_t check_connectivity(const char *host)
{
EventGroupHandle_t events = xEventGroupCreate();
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(host, NULL, &hint, &res) != 0) {
ESP_LOGE(TAG, "ping: unknown host %s\n", host);
return ESP_ERR_NOT_FOUND;
}
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);
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
config.target_addr = target_addr;
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 = events
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
vTaskDelay(pdMS_TO_TICKS(config.count * config.interval_ms));
EventBits_t bits = xEventGroupWaitBits(events, FAIL | SUCCESS, pdFALSE, pdFALSE, portMAX_DELAY);
vEventGroupDelete(events);
esp_ping_delete_session(ping);
return bits == SUCCESS ? ESP_OK : ESP_FAIL;
}

View File

@ -0,0 +1,137 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Ethernet Basic Initialization
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_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "iface_info.h"
static const char *TAG = "ethernet_connect";
struct eth_info_t {
iface_info_t parent;
esp_eth_handle_t eth_handle;
esp_eth_netif_glue_handle_t glue;
esp_eth_mac_t *mac;
esp_eth_phy_t *phy;
};
static void eth_event_handler(void *args, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
uint8_t mac_addr[6] = {0};
/* we can get the ethernet driver handle from event data */
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
struct eth_info_t *eth_info = args;
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet Link Up");
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
eth_info->parent.connected = true;
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
eth_info->parent.connected = false;
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
static void got_ip_event_handler(void *args, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
struct eth_info_t *eth_info = args;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "IP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "MASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "GW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
for (int i = 0; i < 2; ++i) {
esp_netif_get_dns_info(eth_info->parent.netif, i, &eth_info->parent.dns[i]);
ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(&eth_info->parent.dns[i].ip.u_addr.ip4));
}
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
static void teardown_eth(iface_info_t *info)
{
struct eth_info_t *eth_info = __containerof(info, struct eth_info_t, parent);
esp_eth_stop(eth_info->eth_handle);
esp_eth_del_netif_glue(eth_info->glue);
esp_eth_driver_uninstall(eth_info->eth_handle);
eth_info->phy->del(eth_info->phy);
eth_info->mac->del(eth_info->mac);
esp_netif_destroy(eth_info->parent.netif);
free(eth_info);
}
iface_info_t *setup_eth(int prio)
{
struct eth_info_t *eth_info = malloc(sizeof(struct eth_info_t));
assert(eth_info);
eth_info->parent.teardown = teardown_eth;
eth_info->parent.name = "Ethernet";
// Init common MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
// Use internal ESP32's ethernet
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
eth_info->mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
eth_info->phy = esp_eth_phy_new_ip101(&phy_config);
// Init Ethernet driver to default and install it
esp_eth_config_t config = ETH_DEFAULT_CONFIG(eth_info->mac, eth_info->phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_info->eth_handle));
// Create an instance of esp-netif for Ethernet
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_ETH();
base_netif_cfg.route_prio = prio;
esp_netif_config_t cfg = {
.base = &base_netif_cfg,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH,
};
eth_info->parent.netif = esp_netif_new(&cfg);
eth_info->glue = esp_eth_new_netif_glue(eth_info->eth_handle);
// Attach Ethernet driver to TCP/IP stack
ESP_ERROR_CHECK(esp_netif_attach(eth_info->parent.netif, eth_info->glue ));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_info));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_info));
// Start Ethernet driver state machine
ESP_ERROR_CHECK(esp_eth_start(eth_info->eth_handle));
return &eth_info->parent;
}

View File

@ -0,0 +1,4 @@
dependencies:
espressif/esp_modem:
version: "^1.0.0"
override_path: "../../../../components/esp_modem"

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Common interface info
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.
*/
#pragma once
#include "esp_netif.h"
struct iface_info_t {
esp_netif_t *netif;
esp_netif_dns_info_t dns[2];
void (*teardown)(struct iface_info_t *);
const char *name;
bool connected;
};
typedef struct iface_info_t iface_info_t;

View File

@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Multiple Network Interface 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 <lwip/dns.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "nvs_flash.h"
#include "iface_info.h"
iface_info_t *setup_eth(int prio);
iface_info_t *setup_wifi(int prio);
iface_info_t *setup_ppp(int prio);
esp_err_t check_connectivity(const char *host);
#define HOST "www.espressif.com"
#define ETH_PRIO 200
#define WIFI_PRIO 100
#define PPP_PRIO 50
static const char *TAG = "app_main";
static ssize_t get_default(iface_info_t *list[], size_t num)
{
esp_netif_t *default_netif = esp_netif_get_default_netif();
if (default_netif == NULL) {
ESP_LOGE(TAG, "default netif is NULL!");
return -1;
}
ESP_LOGI(TAG, "Default netif: %s", esp_netif_get_desc(default_netif));
for (int i = 0; i < num; ++i) {
if (list[i] && list[i]->netif == default_netif) {
ESP_LOGI(TAG, "Default interface: %s", list[i]->name);
return i;
}
}
// not found
return -2;
}
void app_main(void)
{
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());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
// all interfaces
iface_info_t *ifaces[] = {
setup_eth(ETH_PRIO),
setup_wifi(WIFI_PRIO),
setup_ppp(PPP_PRIO),
};
size_t num_of_ifaces = sizeof(ifaces) / sizeof(ifaces[0]);
while (true) {
dns_clear_cache();
vTaskDelay(pdMS_TO_TICKS(2000));
ssize_t i = get_default(ifaces, num_of_ifaces);
if (i == -1) { // default netif is NULL, probably all interfaces are down -> retry
continue;
} else if (i < 0) {
break; // some other error, exit
}
esp_err_t connect_status = check_connectivity(HOST);
if (connect_status == ESP_OK) {
// connectivity ok
continue;
}
if (connect_status == ESP_ERR_NOT_FOUND) {
// set the default DNS info to global DNS server list
for (int j = 0; j < 2; ++j) {
esp_netif_dns_info_t dns_info;
esp_netif_get_dns_info(ifaces[i]->netif, j, &dns_info);
if (memcmp(&dns_info.ip, &ifaces[i]->dns[j].ip, sizeof(esp_ip_addr_t)) == 0) {
connect_status = ESP_FAIL;
} else {
esp_netif_set_dns_info(ifaces[i]->netif, j, &ifaces[i]->dns[j]);
ESP_LOGI(TAG, "Reconfigured DNS%i=" IPSTR, j, IP2STR(&ifaces[i]->dns[j].ip.u_addr.ip4));
}
}
}
if (connect_status == ESP_FAIL) {
ESP_LOGE(TAG, "No connection via the default netif!");
// try to switch interfaces manually
// WARNING: Once we set_default_netif() manually, we disable the automatic prio-routing
int next = (i + 1) % num_of_ifaces;
while (ifaces[i] != ifaces[next]) {
if (ifaces[next]->connected) {
ESP_LOGE(TAG, "Trying another interface: %s", ifaces[next]->name);
esp_netif_set_default_netif(ifaces[next]->netif);
break;
}
++next;
next = next % num_of_ifaces;
}
}
}
ESP_LOGI(TAG, "Stop and cleanup all interfaces");
for (int i = 0; i < num_of_ifaces; ++i) {
if (ifaces[i]) {
ifaces[i]->teardown(ifaces[i]);
}
}
}

View File

@ -0,0 +1,210 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* PPPoS Initialization
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_api.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "iface_info.h"
struct ppp_info_t {
iface_info_t parent;
esp_modem_dce_t *dce;
bool stop_task;
};
static const int CONNECT_BIT = BIT0;
static const char *TAG = "pppos_connect";
static EventGroupHandle_t event_group = NULL;
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 %" PRIu32, event_id);
if (event_id == NETIF_PPP_ERRORUSER) {
struct ppp_info_t *ppp_info = arg;
esp_netif_t *netif = event_data;
ESP_LOGI(TAG, "User interrupted event from netif:%p", netif);
ppp_info->parent.connected = false;
}
}
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! %" PRIu32, event_id);
struct ppp_info_t *ppp_info = arg;
if (event_id == IP_EVENT_PPP_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
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));
for (int i = 0; i < 2; ++i) {
esp_netif_get_dns_info(ppp_info->parent.netif, i, &ppp_info->parent.dns[i]);
ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(&ppp_info->parent.dns[i].ip.u_addr.ip4));
}
ESP_LOGI(TAG, "~~~~~~~~~~~~~~");
xEventGroupSetBits(event_group, CONNECT_BIT);
ppp_info->parent.connected = true;
ESP_LOGI(TAG, "GOT ip event!!!");
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Modem Disconnect from PPP Server");
ppp_info->parent.connected = false;
} 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 teardown_ppp(iface_info_t *info)
{
struct ppp_info_t *ppp_info = __containerof(info, struct ppp_info_t, parent);
esp_netif_action_disconnected(ppp_info->parent.netif, 0, 0, 0);
esp_netif_action_stop(ppp_info->parent.netif, 0, 0, 0);
esp_err_t err = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_COMMAND);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);
return;
}
esp_modem_destroy(ppp_info->dce);
vEventGroupDelete(event_group);
ppp_info->stop_task = true;
free(info);
}
static void ppp_task(void *args)
{
struct ppp_info_t *ppp_info = args;
int backoff_time = 15000;
const int max_backoff = 60000;
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN;
dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN;
ppp_info->dce = esp_modem_new(&dte_config, &dce_config, ppp_info->parent.netif);
int rssi, ber;
esp_err_t ret = esp_modem_get_signal_quality(ppp_info->dce, &rssi, &ber);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d %s", ret, esp_err_to_name(ret));
goto failed;
}
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
ret = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_DATA);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DATA) failed with %d", ret);
goto failed;
}
failed:
#define CONTINUE_LATER() backoff_time *= 2; \
if (backoff_time > max_backoff) { backoff_time = max_backoff; } \
continue;
// now let's keep retrying
while (!ppp_info->stop_task) {
vTaskDelay(pdMS_TO_TICKS(backoff_time));
if (ppp_info->parent.connected) {
backoff_time = 5000;
continue;
}
// try if the modem got stuck in data mode
ESP_LOGI(TAG, "Trying to Sync with modem");
ret = esp_modem_sync(ppp_info->dce);
if (ret != ESP_OK) {
ESP_LOGI(TAG, "Switching to command mode");
esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_COMMAND);
ESP_LOGI(TAG, "Retry sync 3 times");
for (int i = 0; i < 3; ++i) {
ret = esp_modem_sync(ppp_info->dce);
if (ret == ESP_OK) {
break;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
if (ret != ESP_OK) {
CONTINUE_LATER();
}
}
ESP_LOGI(TAG, "Manual hang-up before reconnecting");
ret = esp_modem_at(ppp_info->dce, "ATH", NULL, 2000);
if (ret != ESP_OK) {
CONTINUE_LATER();
}
ret = esp_modem_get_signal_quality(ppp_info->dce, &rssi, &ber);
if (ret != ESP_OK) {
CONTINUE_LATER();
}
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
ret = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_DATA);
if (ret != ESP_OK) {
CONTINUE_LATER();
}
}
#undef CONTINUE_LATER
vTaskDelete(NULL);
}
iface_info_t *setup_ppp(int prio)
{
struct ppp_info_t *ppp_info = calloc(1, sizeof(struct ppp_info_t));
assert(ppp_info);
ppp_info->parent.teardown = teardown_ppp;
ppp_info->parent.name = "Modem";
event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, ppp_info));
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, ppp_info));
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
base_netif_cfg.route_prio = prio;
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
};
ppp_info->parent.netif = esp_netif_new(&netif_ppp_config);
if (ppp_info->parent.netif == NULL) {
goto err;
}
if (xTaskCreate(ppp_task, "ppp_retry_task", 4096, ppp_info, 5, NULL) != pdTRUE) {
goto err;
}
ESP_LOGI(TAG, "Waiting for IP address");
xEventGroupWaitBits(event_group, CONNECT_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(10000));
return &ppp_info->parent;
err:
teardown_ppp(&ppp_info->parent);
return NULL;
}

View File

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* WiFi connection
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_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "iface_info.h"
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "wifi_connect";
static int s_retry_num = 0;
static EventGroupHandle_t s_wifi_event_group;
static void event_handler(void *args, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
struct iface_info_t *wifi_info = args;
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_info->connected = false;
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY || CONFIG_ESP_MAXIMUM_RETRY == 0) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG, "connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "WiFi station Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "IP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "MASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "GW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
for (int i = 0; i < 2; ++i) {
esp_netif_get_dns_info(wifi_info->netif, i, &wifi_info->dns[i]);
ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(&wifi_info->dns[i].ip.u_addr.ip4));
}
ESP_LOGI(TAG, "~~~~~~~~~~~");
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
wifi_info->connected = true;
}
}
static void teardown_wifi(iface_info_t *info)
{
esp_netif_action_disconnected(info->netif, 0, 0, 0);
esp_netif_action_stop(info->netif, 0, 0, 0);
esp_wifi_stop();
esp_wifi_deinit();
free(info);
}
iface_info_t *setup_wifi(int prio)
{
struct iface_info_t *wifi_info = malloc(sizeof(iface_info_t));
assert(wifi_info);
wifi_info->teardown = teardown_wifi;
wifi_info->name = "WiFi station";
s_wifi_event_group = xEventGroupCreate();
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
esp_netif_config.route_prio = prio;
wifi_info->netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config);
esp_wifi_set_default_wifi_sta_handlers();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, wifi_info));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, wifi_info));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection or a failure (connection failed or a timeout) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, (WIFI_CONNECTED_BIT | WIFI_FAIL_BIT), pdFALSE, pdFALSE, pdMS_TO_TICKS(5000));
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
teardown_wifi(wifi_info);
wifi_info = NULL;
} else if (CONFIG_ESP_MAXIMUM_RETRY == 0) {
ESP_LOGI(TAG, "No connection at the moment, will keep retrying...");
} else {
ESP_LOGE(TAG, "Failed to connect withing specified timeout");
teardown_wifi(wifi_info);
wifi_info = NULL;
}
return wifi_info;
}

View File

@ -0,0 +1,2 @@
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y