pppos_client: Add a test application for pppos_client

Adding testing facilities for pppos-client including esp-modem component
and PPP client in lwip. Testing is provided with PPP server running on
rpi.
This commit is contained in:
David Cermak 2020-02-25 21:48:06 +01:00
parent 745f062e07
commit 636621d1c5
15 changed files with 657 additions and 93 deletions

View file

@ -7,4 +7,4 @@ set(srcs "src/esp_modem.c"
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include
REQUIRES driver)
REQUIRES driver)

View file

@ -0,0 +1,82 @@
menu "ESP-MODEM"
config MODEM_APN
string "Set Access Point Name (APN)"
default "CMNET"
help
Logical name which is used to select the GGSN or the external packet data network.
menu "UART Configuration"
config MODEM_UART_MODEM_TX_PIN
int "TXD Pin Number"
default 25
range 0 31
help
Pin number of UART TX.
config MODEM_UART_MODEM_RX_PIN
int "RXD Pin Number"
default 26
range 0 31
help
Pin number of UART RX.
config MODEM_UART_MODEM_RTS_PIN
int "RTS Pin Number"
default 27
range 0 31
help
Pin number of UART RTS.
config MODEM_UART_MODEM_CTS_PIN
int "CTS Pin Number"
default 23
range 0 31
help
Pin number of UART CTS.
config 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 MODEM_UART_EVENT_TASK_PRIORITY
int "UART Event Task Priority"
range 3 22
default 5
help
Priority of UART event task.
config MODEM_UART_EVENT_QUEUE_SIZE
int "UART Event Queue Size"
range 10 40
default 30
help
Length of UART event queue.
config MODEM_UART_PATTERN_QUEUE_SIZE
int "UART Pattern Queue Size"
range 10 40
default 20
help
Length of UART pattern queue.
config MODEM_UART_TX_BUFFER_SIZE
int "UART TX Buffer Size"
range 256 2048
default 512
help
Buffer size of UART TX buffer.
config 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

@ -21,7 +21,7 @@
#include "esp_log.h"
#include "sdkconfig.h"
#define ESP_MODEM_LINE_BUFFER_SIZE (CONFIG_EXAMPLE_UART_RX_BUFFER_SIZE / 2)
#define ESP_MODEM_LINE_BUFFER_SIZE (CONFIG_MODEM_UART_RX_BUFFER_SIZE / 2)
#define ESP_MODEM_EVENT_QUEUE_SIZE (16)
#define MIN_PATTERN_INTERVAL (9)
@ -314,7 +314,7 @@ static esp_err_t esp_modem_dte_change_mode(modem_dte_t *dte, modem_mode_t new_mo
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, CONFIG_EXAMPLE_UART_PATTERN_QUEUE_SIZE);
uart_pattern_queue_reset(esp_dte->uart_port, CONFIG_MODEM_UART_PATTERN_QUEUE_SIZE);
MODEM_CHECK(dce->set_working_mode(dce, new_mode) == ESP_OK, "set new working mode:%d failed", err, new_mode);
break;
default:
@ -391,16 +391,16 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
.flow_ctrl = (config->flow_control == MODEM_FLOW_CONTROL_HW) ? UART_HW_FLOWCTRL_CTS_RTS : UART_HW_FLOWCTRL_DISABLE
};
/* Install UART driver and get event queue used inside driver */
res = uart_driver_install(esp_dte->uart_port, CONFIG_EXAMPLE_UART_RX_BUFFER_SIZE, CONFIG_EXAMPLE_UART_TX_BUFFER_SIZE,
CONFIG_EXAMPLE_UART_EVENT_QUEUE_SIZE, &(esp_dte->event_queue), 0);
res = uart_driver_install(esp_dte->uart_port, CONFIG_MODEM_UART_RX_BUFFER_SIZE, CONFIG_MODEM_UART_TX_BUFFER_SIZE,
CONFIG_MODEM_UART_EVENT_QUEUE_SIZE, &(esp_dte->event_queue), 0);
MODEM_CHECK(res == ESP_OK, "install uart driver failed", err_uart_config);
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_EXAMPLE_UART_MODEM_TX_PIN, CONFIG_EXAMPLE_UART_MODEM_RX_PIN,
CONFIG_EXAMPLE_UART_MODEM_RTS_PIN, CONFIG_EXAMPLE_UART_MODEM_CTS_PIN);
res = uart_set_pin(esp_dte->uart_port, CONFIG_MODEM_UART_MODEM_TX_PIN, CONFIG_MODEM_UART_MODEM_RX_PIN,
CONFIG_MODEM_UART_MODEM_RTS_PIN, CONFIG_MODEM_UART_MODEM_CTS_PIN);
} else {
res = uart_set_pin(esp_dte->uart_port, CONFIG_EXAMPLE_UART_MODEM_TX_PIN, CONFIG_EXAMPLE_UART_MODEM_RX_PIN,
res = uart_set_pin(esp_dte->uart_port, CONFIG_MODEM_UART_MODEM_TX_PIN, CONFIG_MODEM_UART_MODEM_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
MODEM_CHECK(res == ESP_OK, "config uart gpio failed", err_uart_config);
@ -414,7 +414,7 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *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 */
res |= uart_pattern_queue_reset(esp_dte->uart_port, CONFIG_EXAMPLE_UART_PATTERN_QUEUE_SIZE);
res |= uart_pattern_queue_reset(esp_dte->uart_port, CONFIG_MODEM_UART_PATTERN_QUEUE_SIZE);
/* Starting in command mode -> explicitly disable RX interrupt */
uart_disable_rx_intr(esp_dte->uart_port);
@ -431,9 +431,9 @@ modem_dte_t *esp_modem_dte_init(const esp_modem_dte_config_t *config)
/* Create UART Event task */
BaseType_t ret = xTaskCreate(uart_event_task_entry, //Task Entry
"uart_event", //Task Name
CONFIG_EXAMPLE_UART_EVENT_TASK_STACK_SIZE, //Task Stack Size(Bytes)
CONFIG_MODEM_UART_EVENT_TASK_STACK_SIZE, //Task Stack Size(Bytes)
esp_dte, //Task Parameter
CONFIG_EXAMPLE_UART_EVENT_TASK_PRIORITY, //Task Priority
CONFIG_MODEM_UART_EVENT_TASK_PRIORITY, //Task Priority
& (esp_dte->uart_event_task_hdl) //Task Handler
);
MODEM_CHECK(ret == pdTRUE, "create uart event task failed", err_tsk_create);
@ -473,7 +473,7 @@ esp_err_t esp_modem_start_ppp(modem_dte_t *dte)
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_MODEM_APN) == ESP_OK, "set MODEM APN failed", err);
MODEM_CHECK(dce->define_pdp_context(dce, 1, "IP", CONFIG_MODEM_APN) == ESP_OK, "set MODEM APN failed", err);
/* Enter PPP mode */
MODEM_CHECK(dte->change_mode(dte, MODEM_PPP_MODE) == ESP_OK, "enter ppp mode failed", err);

View file

@ -73,12 +73,12 @@ esp_err_t esp_modem_add_event_handler(modem_dte_t *dte, esp_event_handler_t hand
esp_err_t esp_modem_setup_ppp(modem_dte_t *dte)
{
#if CONFIG_LWIP_PPP_PAP_SUPPORT
#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
#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;
#else
#error "Unsupported AUTH Negotiation"
#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();
@ -88,7 +88,9 @@ esp_err_t esp_modem_setup_ppp(modem_dte_t *dte)
// 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 */

View file

@ -16,12 +16,6 @@ menu "Example Configuration"
Quectel BG96 is a series of LTE Cat M1/Cat NB1/EGPRS module.
endchoice
config EXAMPLE_MODEM_APN
string "Set Access Point Name (APN)"
default "CMNET"
help
Logical name which is used to select the GGSN or the external packet data network.
config EXAMPLE_MODEM_PPP_AUTH_USERNAME
string "Set username for authentication"
default "espressif"
@ -48,76 +42,5 @@ menu "Example Configuration"
Enter the peer phone number that you want to send message to.
endif
menu "UART Configuration"
config EXAMPLE_UART_MODEM_TX_PIN
int "TXD Pin Number"
default 25
range 0 31
help
Pin number of UART TX.
config EXAMPLE_UART_MODEM_RX_PIN
int "RXD Pin Number"
default 26
range 0 31
help
Pin number of UART RX.
config EXAMPLE_UART_MODEM_RTS_PIN
int "RTS Pin Number"
default 27
range 0 31
help
Pin number of UART RTS.
config EXAMPLE_UART_MODEM_CTS_PIN
int "CTS Pin Number"
default 23
range 0 31
help
Pin number of UART CTS.
config EXAMPLE_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_UART_EVENT_TASK_PRIORITY
int "UART Event Task Priority"
range 3 22
default 5
help
Priority of UART event task.
config EXAMPLE_UART_EVENT_QUEUE_SIZE
int "UART Event Queue Size"
range 10 40
default 30
help
Length of UART event queue.
config EXAMPLE_UART_PATTERN_QUEUE_SIZE
int "UART Pattern Queue Size"
range 10 40
default 20
help
Length of UART pattern queue.
config EXAMPLE_UART_TX_BUFFER_SIZE
int "UART TX Buffer Size"
range 256 2048
default 512
help
Buffer size of UART TX buffer.
config EXAMPLE_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

@ -313,6 +313,12 @@ test_app_test_002:
- ESP32
- Example_WIFI
test_app_test_003:
extends: .test_app_template
tags:
- ESP32
- Example_PPP
example_test_011:
extends: .example_debug_template
tags:

View file

@ -0,0 +1,7 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/pppos_client/components)
project(pppos_client)

View file

@ -0,0 +1,54 @@
# PPP over Serial test
Testing connection of PPP client (ESP) to PPP server (raspberry pi)
## Pin Assignment
| ESP | rpi |
| ------ | -------------- |
| GPIO25 | UART-RX |
| GPIO26 | UART-TX |
| GND | GND |
## Test workflow
* Starts PPP server on a linux host
* Starts PPPoS client on ESP board
* Connects with both IPv4 and IPv6
* Test IPv6 connection
- Start server on ESP
- Have linux host to connect and exchange some data
* Test IPv4 connection
- Start server on linux host
- Connect with ESP to the server and exchange some data
* Pass/fail
## Manual test
### Server side
```
sudo pppd /dev/ttyAMA0 115200 CONFIG_TEST_APP_PPP_SERVER_IP:TEST_APP_PPP_CLIENT_IP modem local noauth debug nodetach dump nocrtscts
```
or with `+ipv6` to support ipv6 assignment
### IPv6 test
Connect to the ESP local link address and send data, e.g.
```
nc fe80::74d7:edc5:9907:5457 2222
```
### IPv4 tst
Server side expects to run tcp server on port 2222
```
nc -l 2222
```
## Test environment
configuration `Example_PPP` see wikis/test-docs/example-test-env#example_ppp

View file

@ -0,0 +1,107 @@
from __future__ import print_function
from __future__ import unicode_literals
import re
import socket
import subprocess
import ttfw_idf
import time
from threading import Thread, Event
def run_server(server_stop, port, server_ip, client_ip):
print("Starting PPP server on port: {}".format(port))
try:
arg_list = ['pppd', port, '115200', '{}:{}'.format(server_ip, client_ip), 'modem', 'local', 'noauth', 'debug', 'nocrtscts', 'nodetach', '+ipv6']
p = subprocess.Popen(arg_list, stdout=subprocess.PIPE, bufsize=1)
while not server_stop.is_set():
if p.poll() is not None:
raise ValueError('ENV_TEST_FAILURE: PPP terminated unexpectedly with {}'.format(p.poll()))
line = p.stdout.readline()
if line:
print("[PPPD:]{}".format(line.rstrip()))
time.sleep(0.1)
except Exception as e:
print(e)
raise ValueError('ENV_TEST_FAILURE: Error running PPP server')
finally:
p.terminate()
print("PPP server stopped")
@ttfw_idf.idf_custom_test(env_tag="Example_PPP", group="test-apps")
def test_examples_protocol_pppos_connect(env, extra_data):
"""
steps:
1. starts PPP server
2. get DUT as PPP client to connect to the server
3. check TCP client-server connection between client-server
"""
dut1 = env.get_dut("pppos_connect_test", "tools/test_apps/protocols/pppos", dut_class=ttfw_idf.ESP32DUT)
# Look for test case symbolic names
try:
server_ip = dut1.app.get_sdkconfig()["CONFIG_TEST_APP_PPP_SERVER_IP"].replace('"','')
client_ip = dut1.app.get_sdkconfig()["CONFIG_TEST_APP_PPP_CLIENT_IP"].replace('"','')
port_nr = dut1.app.get_sdkconfig()["CONFIG_TEST_APP_TCP_PORT"]
except Exception:
print('ENV_TEST_FAILURE: Some mandatory configuration not found in sdkconfig')
raise
print("Starting the test on {}".format(dut1))
dut1.start_app()
# the PPP test env uses two ttyUSB's: one for ESP32 board, another one for ppp server
# use the other port for PPP server than the DUT/ESP
port = '/dev/ttyUSB0' if dut1.port == '/dev/ttyUSB1' else '/dev/ttyUSB1'
# Start the PPP server
server_stop = Event()
t = Thread(target=run_server, args=(server_stop, port, server_ip, client_ip))
t.start()
try:
ip6_addr = dut1.expect(re.compile(r"Got IPv6 address ([0-9a-f\:]+)"), timeout=30)[0]
print("IPv6 address of ESP: {}".format(ip6_addr))
dut1.expect(re.compile(r"Socket listening"))
print("Starting the IPv6 test...")
# Connect to TCP server on ESP using IPv6 address
for res in socket.getaddrinfo(ip6_addr + "%ppp0", int(port_nr), socket.AF_INET6,
socket.SOCK_STREAM, socket.SOL_TCP):
af, socktype, proto, canonname, addr = res
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.connect(addr)
sock.sendall("Espressif")
sock.close()
dut1.expect(re.compile(r"IPv6 test passed"))
print("IPv6 test passed!")
print("Starting the IPv4 test...")
# Start the TCP server and wait for the ESP to connect with IPv4 address
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', int(port_nr)))
sock.listen(1)
conn, addr = sock.accept()
except socket.error as msg:
print('Socket error: ' + str(msg[0]) + ': ' + msg[1])
raise
timeout = time.time() + 60
while time.time() < timeout:
data = conn.recv(128)
if not data:
break
data = data.decode()
print('Received data: ' + data)
if data.startswith('Espressif'):
conn.send(data.encode())
break
conn.close()
dut1.expect(re.compile(r"IPv4 test passed"))
print("IPv4 test passed!")
finally:
server_stop.set()
t.join()
if __name__ == '__main__':
test_examples_protocol_pppos_connect()

View file

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

View file

@ -0,0 +1,24 @@
menu "Test App Configuration"
config TEST_APP_PPP_SERVER_IP
string "IP address of PPP server"
default "10.0.0.1"
help
IP address of PPP server. Note: this is also the address
where the TCP server is started to test the connection
config TEST_APP_PPP_CLIENT_IP
string "IP address of PPP client"
default "10.0.0.2"
help
IP address that PPP server assigns to PPP client.
config TEST_APP_TCP_PORT
int "Port of test"
range 0 65535
default 2222
help
The remote port to which the client will connects to
once the PPP connection established
endmenu

View file

@ -0,0 +1,71 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"
#include "esp_modem.h"
/**
* @brief Dummy DCE to facilitate testing lwip ppp client with ppp server
*
*/
typedef struct {
modem_dce_t parent; /*!< DCE parent class */
} null_modem_dce_t;
static esp_err_t null_dce_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
{
dce->mode = mode;
return ESP_OK;
}
static esp_err_t null_dce_define_pdp_context(modem_dce_t *dce, uint32_t cid, const char *type, const char *apn)
{
return ESP_OK;
}
esp_err_t null_dce_dce_hang_up(modem_dce_t *dce)
{
return ESP_OK;
}
static esp_err_t null_dce_deinit(modem_dce_t *dce)
{
null_modem_dce_t *bg96_dce = __containerof(dce, null_modem_dce_t, parent);
if (dce->dte) {
dce->dte->dce = NULL;
}
free(bg96_dce);
return ESP_OK;
}
modem_dce_t *null_dce_init(modem_dte_t *dte)
{
if (!dte) return NULL;
/* malloc memory for bg96_dce object */
null_modem_dce_t *null_dce = calloc(1, sizeof(null_modem_dce_t));
if (!null_dce) return NULL;
/* Bind DTE with DCE */
null_dce->parent.dte = dte;
dte->dce = &(null_dce->parent);
/* Bind methods */
null_dce->parent.handle_line = NULL;
null_dce->parent.define_pdp_context = null_dce_define_pdp_context;
null_dce->parent.set_working_mode = null_dce_set_working_mode;
null_dce->parent.deinit = null_dce_deinit;
null_dce->parent.hang_up = null_dce_dce_hang_up;
/* Sync between DTE and DCE */
return &(null_dce->parent);
}

View file

@ -0,0 +1,23 @@
// 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
/**
* @brief Dummy DCE to facilitate testing of lwip ppp client connecting to ppp server
* It creates a modem instance that supports no command interface
*
* @param dte Modem DTE object
* @return modem_dce_t* Modem DCE object
*/
modem_dce_t *null_dce_init(modem_dte_t *dte);

View file

@ -0,0 +1,257 @@
/* 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 "esp_modem.h"
#include "esp_modem_netif.h"
#include "esp_log.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "null_dce.h"
#define HOST_IP_ADDR CONFIG_TEST_APP_PPP_SERVER_IP
#define PORT CONFIG_TEST_APP_TCP_PORT
static const char *TAG = "pppos_test_app";
static EventGroupHandle_t event_group = NULL;
static const int CONNECT_BIT = BIT0;
static const int STOP_BIT = BIT1;
static const int TCP_SERVER_DONE = BIT3;
static const int TCP_SERVER_FAILED = BIT4;
static char addr_str[128];
static char rx_buffer[128];
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 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);
}
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_id == IP_EVENT_PPP_GOT_IP) {
ESP_LOGI(TAG, "GOT ip event!!!");
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "PPP client connected to PPP Server");
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));
xEventGroupSetBits(event_group, CONNECT_BIT);
} 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 esp_err_t test_tcp_client_ipv4(void)
{
struct sockaddr_in dest_addr;
int addr_family;
int ip_protocol;
const char *payload = "Espressif";
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
goto fail;
}
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", HOST_IP_ADDR, PORT);
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
goto fail;
}
ESP_LOGI(TAG, "Successfully connected");
err = send(sock, payload, strlen(payload), 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
goto fail;
}
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
goto fail;
}
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received from socket:%s", rx_buffer);
return ESP_OK;
fail:
ESP_LOGE(TAG, "Test TCP connection failed!");
return ESP_FAIL;
}
static void test_tcp_server_ipv6(void *pvParameters)
{
int addr_family;
int ip_protocol;
struct sockaddr_in6 dest_addr;
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
goto failed;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
goto failed;
}
while (1) {
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_in6 source_addr;
size_t addr_len = sizeof(source_addr);
int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0) {
ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed");
} else {
rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
goto passed;
}
shutdown(sock, 0);
close(sock);
}
failed:
xEventGroupSetBits(event_group, TCP_SERVER_FAILED);
passed:
xEventGroupSetBits(event_group, TCP_SERVER_DONE);
close(listen_sock);
vTaskDelete(NULL);
}
void app_main(void)
{
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();
// Init netif object
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_PPP();
esp_netif_t *esp_netif = esp_netif_new(&cfg);
assert(esp_netif);
/* create dte object */
esp_modem_dte_config_t config = ESP_MODEM_DTE_DEFAULT_CONFIG();
config.rx_io_num = 26;
config.tx_io_num = 25;
modem_dte_t *dte = esp_modem_dte_init(&config);
/* Register event handler */
ESP_ERROR_CHECK(esp_modem_set_event_handler(dte, modem_event_handler, ESP_EVENT_ANY_ID, NULL));
/* create dce object */
modem_dce_t *dce = null_dce_init(dte);
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 */
esp_netif_attach(esp_netif, modem_netif_adapter);
/* Wait for IP address */
xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
ESP_LOGI(TAG, "start IPv6 test");
xTaskCreate(test_tcp_server_ipv6, "tcp_server_ipv6", 4096, NULL, 5, NULL);
xEventGroupWaitBits(event_group, TCP_SERVER_DONE, pdTRUE, pdTRUE, portMAX_DELAY);
if (TCP_SERVER_FAILED & xEventGroupGetBits(event_group)) {
ESP_LOGE(TAG, "IPv6 test failed!");
} else {
ESP_LOGI(TAG, "IPv6 test passed");
}
/* Wait until the TCP server starts on the host */
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "start IPv4 test");
if (test_tcp_client_ipv4() == ESP_OK) {
ESP_LOGI(TAG, "IPv4 test passed");
} else {
ESP_LOGE(TAG, "IPv6 test failed!");
}
ESP_ERROR_CHECK(esp_modem_stop_ppp(dte));
/* Destroy the netif adapter withe events, which internally frees also the esp-netif instance */
esp_modem_netif_clear_default_handlers(modem_netif_adapter);
esp_modem_netif_teardown(modem_netif_adapter);
xEventGroupWaitBits(event_group, STOP_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
ESP_ERROR_CHECK(dce->deinit(dce));
ESP_ERROR_CHECK(dte->deinit(dte));
}

View file

@ -0,0 +1,6 @@
# 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
CONFIG_LWIP_PPP_ENABLE_IPV6=y