feat(examples): Move the slip example to esp-netif folder

And update the slip_modem_get_ipv6_address() to return the address by
copy instead of reference (to employ return value optimizaiton where
possible)
This commit is contained in:
David Cermak
2022-09-14 15:19:27 +02:00
parent 52d7458b99
commit fcd6f0bb14
11 changed files with 16 additions and 18 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(slip_client)

View File

@ -0,0 +1,88 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
# SLIP device client
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This provides SLIP support for connection to Contiki gateway devices, allowing the ESP platform board to be used to bridge between low-power networks and IP (Wifi / Ethernet).
This example also demonstrates creating custom network interfaces, including UART drivers and lwIP netif layers, and attaching them to the standard `esp_netif` component, so the generic system interfaces can still use the common approach of listing all interfaces, updating states, posting events and handling routing priorities. Please refer to the implementation of [slip_modem](components/slip_modem) component for more details.
## How to use example
### Hardware Required
To run this example, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC).
For test purpose, you also need a SLIP capable gateway device, such as anything running [Contiki](https://github.com/contiki-os/contiki) gateway firmware.
You can also try other modules as long as they implement the SLIP protocol (e.g. linux device with slip module loaded)
#### Setup a test SLIP device
It is possible to configure any device with linux and a serial interface
(e.g. raspberry PI or a PC with USB to serial bridge) to enable SLIP interface.
To test this example with such device, please follow these steps:
- Configure IPv4 mode in the example configuration menu
- Setup SLIP interface
```
slattach -v -L -s 115200 -p slip /dev/ttyAMA0
```
where the `/dev/ttyAMA0` is the device's serial port
- Configure IP addresses
```
ifconfig sl0 10.0.0.1 dstaddr 10.0.0.2
```
where the `10.0.0.2` is IPv4 address of the ESP platform board
- Send and receive back UDP packets, as the example implements UDP echo server
```
nc -u 10.0.0.2 5678
```
#### Pin Assignment
**Note:** The following pin assignments are used by default which can be changed in menuconfig.
| ESP32 | Gateway |
| ------ | -------------- |
| GPIO25 | RX |
| GPIO26 | TX |
| GND | GND |
| 3v3 | VCC |
### Configure the project
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
- Choose the RX and TX pins
- Choose port number and IP protocol for socket udp server
For use in external projects `SLIP support` must be enabled under the `components/lwip` menu.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project..
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting
1. Invalid slip packets
Many slip devices use additional messages for things like ipv6 prefix configuration (or sending log messages over the SLIP serial port). This is supported in the driver through the use of an `rx_filter` function that is called on receipt of all packets and can be used to filter packets prior to passing them to the stack.
2. No packets received
The first layer to check is the serial port, you can enable debugging of the SLIP component by setting the global log level to `DEBUG`, or changing the slip component log levbel with `esp_log_level_set("esp-netif_lwip-slip", ESP_LOG_DEBUG);`
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -0,0 +1,8 @@
# SLIP Modem Component
idf_component_register(
SRCS "library/slip_modem.c" "library/slip_modem_netif.c"
INCLUDE_DIRS "include"
REQUIRES esp_netif driver
)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,95 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "esp_netif.h"
#include "driver/uart.h"
/** @brief Configuration of SLIP network interface
*
*/
#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \
{ \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \
.get_ip_event = 0, \
.lost_ip_event = 0, \
.if_key = "SLP_DEF", \
.if_desc = "slip", \
.route_prio = 16, \
.bridge_info = NULL \
};
extern esp_netif_netstack_config_t *netstack_default_slip;
typedef struct slip_modem slip_modem_t;
// Filter callbacks for handling application specific slip messages
typedef bool slip_rx_filter_cb_t(slip_modem_t *slip, uint8_t *data, uint32_t len);
/** @brief Configuration structure for SLIP modem interface
*
*/
typedef struct {
uart_port_t uart_dev; /* UART device for reading and writing SLIP information, this must be initialised externally */
int uart_tx_pin; /* UART TX pin number */
int uart_rx_pin; /* UART TX pin number */
uint32_t uart_baud; /* UART baud rate */
uint32_t rx_buffer_len; /* Length of buffer for RX messages */
slip_rx_filter_cb_t *rx_filter; /* Filter for parsing out non-SLIP messages from incoming SLIP stream */
esp_ip6_addr_t *ipv6_addr;
} slip_modem_config_t;
/** @brief Create a slip modem
*
* @param[in] slip configured esp netif
* @param[in] configuration for the slip modem
*
* @returns
* - slip modem driver glue object
*/
slip_modem_t *slip_modem_create(esp_netif_t *slip_netif, const slip_modem_config_t *modem_config);
/** @brief Destroy a slip modem
*
* @param[in] slip modem object for destruction
*
* @return
* - ESP_OK on success
*/
esp_err_t slip_modem_destroy(slip_modem_t *slip);
/**
* @brief Getter for the internally configured IPv6 address
*
* @param[in] slip modem object
*
* @returns
* - ipv6 address returned by copy
*/
esp_ip6_addr_t slip_modem_get_ipv6_address(slip_modem_t *slip);
/**
* @brief Data path API that forward the supplied data to the attached network interface
*
* @param[in] slip modem object
* @param[in] buffer pointer to the outgoing data
* @param[in] len length of the data
*
*/
void slip_modem_raw_write(slip_modem_t *slip, void *buffer, size_t len);

View File

@ -0,0 +1,246 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "slip_modem.h"
#include "esp_netif.h"
#include "slip_modem_netif.h"
#include "esp_event.h"
#include "esp_log.h"
#define SLIP_RX_TASK_PRIORITY 10
#define SLIP_RX_TASK_STACK_SIZE (4 * 1024)
static const char *TAG = "slip-modem";
// UART container object
typedef struct {
// UART device number for SIO use
uart_port_t uart_dev;
// UART baud rate for configuration
uint32_t uart_baud;
// UART TX pin for configuration
int uart_tx_pin;
// UART RX pin for configuration
int uart_rx_pin;
// QueueHandle for uart driver
QueueHandle_t uart_queue;
// TaskHandle for receive task
TaskHandle_t uart_rx_task;
} esp_slip_uart_t;
// Modem object, implements glue logic for slip_driver and esp_netif
struct slip_modem {
// ESP base netif driver
esp_netif_driver_base_t base;
// Uart for use with slip
esp_slip_uart_t uart;
// Buffer for incoming messages
uint8_t *buffer;
uint32_t buffer_len;
// Filter callbacks for application-specific slip message handling
slip_rx_filter_cb_t *rx_filter;
// Running flag
bool running;
// esp_netif related: SLIP interface IP6 address
esp_ip6_addr_t addr;
};
static void slip_modem_uart_rx_task(void *arg);
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args);
// Create a new slip modem
slip_modem_t *slip_modem_create(esp_netif_t *slip_netif, const slip_modem_config_t *modem_config)
{
ESP_LOGI(TAG, "%s: Creating slip modem (netif: %p)", __func__, slip_netif);
ESP_LOGD(TAG, "%s (netif: %p)", __func__, slip_netif);
slip_modem_t *slip_modem = calloc(1, sizeof(slip_modem_t));
if (!slip_modem) {
ESP_LOGE(TAG, "create netif glue failed");
return NULL;
}
// Attach driver and post_attach callbacks
slip_modem->base.post_attach = slip_modem_post_attach;
slip_modem->base.netif = slip_netif;
// Attach config
slip_modem->buffer_len = modem_config->rx_buffer_len;
slip_modem->rx_filter = modem_config->rx_filter;
slip_modem->uart.uart_dev = modem_config->uart_dev;
slip_modem->uart.uart_baud = modem_config->uart_baud;
slip_modem->uart.uart_rx_pin = modem_config->uart_rx_pin;
slip_modem->uart.uart_tx_pin = modem_config->uart_tx_pin;
memcpy(&slip_modem->addr, modem_config->ipv6_addr, sizeof(esp_ip6_addr_t));
// Return the new modem
return slip_modem;
}
// Internal handler called on driver start
static esp_err_t esp_slip_driver_start(slip_modem_t *slip_modem)
{
ESP_LOGD(TAG, "%s: Starting SLIP modem (modem %p)", __func__, slip_modem);
// Allocate RX buffer if one does not exist
if (slip_modem->buffer == NULL) {
slip_modem->buffer = malloc(slip_modem->buffer_len);
}
if (slip_modem->buffer == NULL) {
ESP_LOGE(TAG, "error allocating rx buffer");
return ESP_ERR_NO_MEM;
}
// Build configuration
uart_config_t uart_config = {
.baud_rate = slip_modem->uart.uart_baud,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
};
// Initialise uart
ESP_ERROR_CHECK(uart_param_config(slip_modem->uart.uart_dev, &uart_config));
// Set UART pins
ESP_ERROR_CHECK(uart_set_pin(slip_modem->uart.uart_dev, slip_modem->uart.uart_tx_pin, slip_modem->uart.uart_rx_pin, 0, 0));
// Install UART driver
ESP_ERROR_CHECK(uart_driver_install(slip_modem->uart.uart_dev, slip_modem->buffer_len, slip_modem->buffer_len, 10, &slip_modem->uart.uart_queue, 0));
// Start slip RX task
slip_modem->running = true;
xTaskCreate(slip_modem_uart_rx_task, "slip_modem_uart_rx_task", SLIP_RX_TASK_STACK_SIZE, slip_modem, SLIP_RX_TASK_PRIORITY, &slip_modem->uart.uart_rx_task);
// Finally, initialise slip network interface
esp_netif_action_start(slip_modem->base.netif, 0, 0, 0);
ESP_ERROR_CHECK(slip_modem_netif_start(slip_modem->base.netif, &slip_modem->addr));
return ESP_OK;
}
esp_err_t slip_modem_destroy(slip_modem_t *slip)
{
// Stop slip driver
esp_netif_action_stop(slip->base.netif, 0, 0, 0);
ESP_ERROR_CHECK(slip_modem_netif_stop(slip->base.netif));
// Stop uart rx task
vTaskDelete(slip->uart.uart_rx_task);
// Delete driver
uart_driver_delete(slip->uart.uart_dev);
// Free slip interface
free(slip);
return ESP_OK;
}
// Modem transmit for glue logic
static esp_err_t slip_modem_transmit(void *slip_driver, void *buffer, size_t len)
{
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
slip_modem_t *slip_modem = (slip_modem_t *) slip_driver;
int32_t res = uart_write_bytes(slip_modem->uart.uart_dev, (char *)buffer, len);
if (res < 0) {
// Handle errors
ESP_LOGE(TAG, "%s: uart_write_bytes error %i", __func__, res);
return ESP_FAIL;
}
return ESP_OK;
}
// Post-attach handler for netif
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args)
{
slip_modem_t *slip_modem = (slip_modem_t *) args;
ESP_LOGD(TAG, "%s (netif: %p args: %p)", __func__, esp_netif, args);
const esp_netif_driver_ifconfig_t driver_ifconfig = {
.driver_free_rx_buffer = NULL,
.transmit = slip_modem_transmit,
.handle = slip_modem,
};
slip_modem->base.netif = esp_netif;
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
esp_slip_driver_start(slip_modem);
return ESP_OK;
}
static void slip_modem_uart_rx_task(void *arg)
{
slip_modem_t *slip_modem = (slip_modem_t *) arg;
ESP_LOGD(TAG, "Start SLIP modem RX task (slip_modem %p filter: %p)", slip_modem, slip_modem->rx_filter);
ESP_LOGD(TAG, "Uart: %d, buffer: %p (%d bytes)", slip_modem->uart.uart_dev, slip_modem->buffer, slip_modem->buffer_len);
while (slip_modem->running == true) {
// Read data from the UART
int len = uart_read_bytes(slip_modem->uart.uart_dev, slip_modem->buffer, slip_modem->buffer_len, 1 / portTICK_PERIOD_MS);
if (len > 0) {
// Log slip RX data
ESP_LOGD(TAG, "rx %d bytes", len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, slip_modem->buffer, len, ESP_LOG_DEBUG);
// Ensure null termination
slip_modem->buffer[len] = '\0';
// Filter if provided
if ((slip_modem->rx_filter != NULL) && slip_modem->rx_filter(slip_modem, slip_modem->buffer, len)) {
continue;
}
// Pass received bytes in to slip interface
ESP_LOGI(TAG, "esp_netif %p", slip_modem->base.netif);
esp_netif_receive(slip_modem->base.netif, slip_modem->buffer, len, NULL);
}
// Yield to allow other tasks to progress
vTaskDelay(1 * portTICK_PERIOD_MS);
}
}
/**
* @brief Gets the internally configured ipv6 address
*/
esp_ip6_addr_t slip_modem_get_ipv6_address(slip_modem_t *slip)
{
return slip->addr;
}
void slip_modem_raw_write(slip_modem_t *slip, void *buffer, size_t len)
{
slip_modem_netif_raw_write(slip->base.netif, buffer, len);
}

View File

@ -0,0 +1,192 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_netif.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "lwip/esp_netif_net_stack.h"
#include "lwip/dns.h"
#include "lwip/ip6_addr.h"
#include "lwip/netif.h"
#include "netif/slipif.h"
#include "lwip/sio.h"
static const char *TAG = "slip-modem-netif";
/**
* @brief Stops the SLIP interface
*/
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif)
{
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
ESP_LOGI(TAG, "%s: Stopped SLIP connection: lwip netif:%p", __func__, netif);
// Stop interface
netif_set_link_down(netif);
return ESP_OK;
}
/**
* @brief Starts the SLIP interface
*/
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr)
{
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
ESP_LOGI(TAG, "%s: Starting SLIP interface: lwip netif:%p", __func__, netif);
// Set the netif up
netif_set_up(netif);
netif_set_link_up(netif);
#if CONFIG_LWIP_IPV6
int8_t addr_index = 0;
netif_ip6_addr_set(netif, addr_index, (ip6_addr_t *)addr);
netif_ip6_addr_set_state(netif, addr_index, IP6_ADDR_VALID);
#endif
return ESP_OK;
}
/**
* @brief Write incoming serial data to the SLIP interface
*/
void esp_netif_lwip_slip_input(void *h, void *buffer, unsigned int len, void *eb)
{
struct netif *netif = h;
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
// Update slip netif with data
const int max_batch = 255;
int sent = 0;
while(sent < len) {
int batch = (len - sent) > max_batch ? max_batch : (len - sent);
slipif_received_bytes(netif, buffer+sent, batch);
sent += batch;
}
// Process incoming bytes
for (int i = 0; i < len; i++) {
slipif_process_rxqueue(netif);
}
}
/**
* @brief Write raw data out the SLIP interface
*/
void slip_modem_netif_raw_write(esp_netif_t *netif, void *buffer, size_t len)
{
struct netif *lwip_netif = esp_netif_get_netif_impl(netif);
ESP_LOGD(TAG, "%s", __func__);
struct pbuf p = {
.next = NULL,
.payload = buffer,
.tot_len = len,
.len = len,
};
// Call slip if output function to feed data out slip interface
#if CONFIG_LWIP_IPV6
lwip_netif->output_ip6(lwip_netif, &p, NULL);
#else
lwip_netif->output(lwip_netif, &p, NULL);
#endif
}
/** @brief Get esp-netif object corresponding to registration index
*/
static esp_netif_t * get_netif_with_esp_index(int index)
{
esp_netif_t *netif = NULL;
int counter = 0;
while ((netif = esp_netif_next(netif)) != NULL) {
if (counter == index) {
return netif;
}
counter++;
}
return NULL;
}
/** @brief Return list registration index of the supplied netif ptr
*/
static int get_esp_netif_index(esp_netif_t * esp_netif)
{
esp_netif_t *netif = NULL;
int counter = 0;
while ((netif = esp_netif_next(netif)) != NULL) {
if (esp_netif == netif) {
return counter;
}
counter++;
}
return -1;
}
err_t esp_slipif_init(struct netif *netif)
{
esp_netif_t *esp_netif = netif->state;
int esp_index = get_esp_netif_index(esp_netif);
if (esp_index < 0) {
return ERR_IF;
}
// Store netif index in net interface for SIO open command to abstract the dev
netif->state = (void *)esp_index;
return slipif_init(netif);
}
const struct esp_netif_netstack_config s_netif_config_slip = {
.lwip = {
.init_fn = esp_slipif_init,
.input_fn = esp_netif_lwip_slip_input,
}
};
const esp_netif_netstack_config_t *netstack_default_slip = &s_netif_config_slip;
/***
* @brief Open a serial device for communication
*/
sio_fd_t sio_open(uint8_t devnum)
{
ESP_LOGD(TAG, "Opening device: %d\r\n", devnum);
esp_netif_t *esp_netif = get_netif_with_esp_index(devnum);
if (!esp_netif) {
ESP_LOGE(TAG, "didn't find esp-netif with index=%d\n", devnum);
return NULL;
}
// Return SIO handle
return esp_netif;
}
/***
* @brief Send a single character to the serial device (blocking)
*/
void sio_send(uint8_t c, sio_fd_t fd)
{
esp_netif_t *esp_netif = fd;
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &c, 1, ESP_LOG_DEBUG);
esp_err_t ret = esp_netif_transmit(esp_netif, &c, 1);
if (ret != ESP_OK) {
// Handle errors
ESP_LOGD(TAG, "%s: uart_write_bytes error %i", __func__, ret);
}
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @brief Stop the esp slip netif
*
* @param[in] esp_netif handle to slip esp-netif instance
*
* @return
* - ESP_OK on success
*/
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif);
/**
* @brief Start the esp slip netif
*
* @param[in] esp_netif handle to slip esp-netif instance
* @param[in] addr IPv6 address associated with this SLIP interface
*
* @return
* - ESP_OK on success
*/
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr);
/**
* @brief Data path API to write raw packet ous the SLIP interface
*
* This API is typically used when implementing user defined methods
*
* @param[in] esp_netif handle to slip esp-netif instance
* @param[in] buffer pointer to the outgoing data
* @param[in] len length of the data
*
* @return
* - ESP_OK on success
*/
void slip_modem_netif_raw_write(esp_netif_t *netif, void *buffer, size_t len);

View File

@ -0,0 +1,8 @@
# Slip client example
idf_component_register(
SRCS "slip_client_main.c"
INCLUDE_DIRS "."
REQUIRES esp_netif slip_modem driver
)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,38 @@
menu "Example Configuration"
menu "UART Configuration"
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
default 25
range 0 36
help
Pin number of UART TX.
config EXAMPLE_UART_RX_PIN
int "RXD Pin Number"
default 26
range 0 36
help
Pin number of UART RX.
config EXAMPLE_UART_BAUD
int "UART baud rate"
default 115200
help
Baud rate for UART communication
endmenu
config EXAMPLE_UDP_PORT
int "Port for UDP echo server"
default 5678
help
Port for UDP echo server in example
config EXAMPLE_IPV4
bool "Test with IPv4 address"
default n
help
Test interface using IPv4
endmenu

View File

@ -0,0 +1,236 @@
/* SLIP 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 <sys/socket.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "slip_modem.h"
static const char *TAG = "slip-example";
#define STACK_SIZE (10 * 1024)
#define PRIORITY 10
static void udp_rx_tx_task(void *arg)
{
char addr_str[128];
uint8_t rx_buff[1024];
int sock = (int)arg;
struct sockaddr_storage source_addr;
socklen_t socklen = sizeof(source_addr);
ESP_LOGI(TAG, "Starting node manager UDP task");
while (1) {
// Receive data
int len = recvfrom(sock, rx_buff, sizeof(rx_buff) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
break;
}
// Parse out address to string
if (source_addr.ss_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (source_addr.ss_family == PF_INET6) {
inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
}
// Force null termination of received data and print
rx_buff[len] = 0;
ESP_LOGI(TAG, "Received '%s' from '%s'", rx_buff, addr_str);
// Send data back
int err = sendto(sock, rx_buff, len, 0, (struct sockaddr *)&source_addr, socklen);
if (err < 0) {
ESP_LOGE(TAG, "sendto failed: errno %d", errno);
break;
}
}
vTaskDelete(NULL);
}
esp_err_t udp_rx_tx_init(void)
{
// Setup bind address
struct sockaddr_in6 dest_addr;
#if CONFIG_EXAMPLE_IPV4
sa_family_t family = AF_INET;
int ip_protocol = IPPROTO_IP;
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr_ip4->sin_family = AF_INET;
dest_addr_ip4->sin_port = htons(CONFIG_EXAMPLE_UDP_PORT);
ip_protocol = IPPROTO_IP;
#else
sa_family_t family = AF_INET6;
int ip_protocol = IPPROTO_IPV6;
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = family;
dest_addr.sin6_port = htons(CONFIG_EXAMPLE_UDP_PORT);
#endif
// Create socket
int sock = socket(family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return ESP_FAIL;
}
// Disable IPv4 and reuse address
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#if !CONFIG_EXAMPLE_IPV4
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif
// Bind socket
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Socket bound, port %d", CONFIG_EXAMPLE_UDP_PORT);
// Start UDP rx thread
xTaskCreate(udp_rx_tx_task, "udp_rx_tx", STACK_SIZE, (void *)sock, PRIORITY, NULL);
return ESP_OK;
}
// Write a prefix to the contiki slip device
static void slip_set_prefix(slip_modem_t *slip)
{
uint8_t buff[10] = {0};
const esp_ip6_addr_t addr = slip_modem_get_ipv6_address(slip);
ESP_LOGI(TAG, "%s: prefix set (%08x:%08x)", __func__,
lwip_ntohl(addr.addr[0]), lwip_ntohl(addr.addr[1]));
// Build slip set message
buff[0] = '!';
buff[1] = 'P';
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
buff[2 + i * 4 + j] = addr.addr[i] >> (j * 8);
}
}
// Write raw data out the slip interface
slip_modem_raw_write(slip, buff, 2 + 8);
}
// slip_rx_filter filters incoming commands from the slip interface
// this implementation is designed for use with contiki slip devices
static bool slip_rx_filter(slip_modem_t *slip, uint8_t *data, uint32_t len)
{
if (data[1] == '?') {
switch (data[2]) {
case 'P':
ESP_LOGI(TAG, "Prefix request");
slip_set_prefix(slip);
return true;
default:
ESP_LOGI(TAG, "Unhandled request '%c'", data[2]);
break;
}
return true;
} else if (data[1] == '!') {
switch (data[2]) {
default:
ESP_LOGI(TAG, "Unhandled command '%c'", data[2]);
break;
}
}
return false;
}
#if CONFIG_EXAMPLE_IPV4
static const esp_netif_ip_info_t s_slip_ip4 = {
.ip = { .addr = ESP_IP4TOADDR( 10, 0, 0, 2) },
};
#endif
// Initialise the SLIP interface
esp_netif_t *slip_if_init(void)
{
ESP_LOGI(TAG, "Initialising SLIP interface");
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_SLIP()
#if CONFIG_EXAMPLE_IPV4
base_cfg.ip_info = &s_slip_ip4;
#endif
esp_netif_config_t cfg = { .base = &base_cfg,
.driver = NULL,
.stack = netstack_default_slip };
esp_netif_t *slip_netif = esp_netif_new(&cfg);
esp_ip6_addr_t local_addr; /* Local IP6 address */
IP6_ADDR(&local_addr,
lwip_htonl(0xfd0000),
lwip_htonl(0x00000000),
lwip_htonl(0x00000000),
lwip_htonl(0x00000001)
);
ESP_LOGI(TAG, "Initialising SLIP modem");
slip_modem_config_t modem_cfg = {
.uart_dev = UART_NUM_1,
.uart_tx_pin = CONFIG_EXAMPLE_UART_TX_PIN,
.uart_rx_pin = CONFIG_EXAMPLE_UART_RX_PIN,
.uart_baud = CONFIG_EXAMPLE_UART_BAUD,
.rx_buffer_len = 1024,
.rx_filter = slip_rx_filter,
.ipv6_addr = &local_addr
};
void *slip_modem = slip_modem_create(slip_netif, &modem_cfg);
assert(slip_modem);
ESP_ERROR_CHECK(esp_netif_attach(slip_netif, slip_modem));
ESP_LOGI(TAG, "SLIP init complete");
return slip_netif;
}
void app_main(void)
{
// Setup networking
esp_netif_init();
esp_log_level_set("*", ESP_LOG_DEBUG);
// Create event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Setup slip interface
slip_if_init();
// Setup UDP loopback service
udp_rx_tx_init();
}

View File

@ -0,0 +1,2 @@
# Override some defaults to enable SLIP
CONFIG_LWIP_SLIP_SUPPORT=y