feat(example): Add socket ipv4 and ipv6 example

Commit ID: b650d19c
This commit is contained in:
Dong Heng
2018-10-30 14:26:34 +08:00
parent 37f6a717d3
commit 8832cb2ad0
40 changed files with 2402 additions and 0 deletions

View File

@ -0,0 +1,102 @@
# BSD Socket API Examples
This directory contains simple examples demonstrating BSD Socket API.
Each example, contains README.md file with mode detailed informations about that particular example.
For more general informations about all examples, see the README.md file in the upper level 'examples' directory.
Examples:
* UDP Client - The application creates UDP socket and sends message to the predefined port and IP address. After the server's reply, the application prints received reply as ASCII text, waits for 2 seconds and sends another message.
* UDP Server - The application creates UDP socket with the specified port number and waits for the data to be received. Received data are printed as ASCII text and retransmitted back to the client.
* TCP Client - The application creates a TCP socket and tries to connect to the server with predefined IP address and port number. When a connection is successfully established, the application sends message and waits for the answer. After the server's reply, application prints received reply as ASCII text, waits for 2 seconds and sends another message.
* TCP Server - The application creates a TCP socket with the specified port number and waits for a connection request from the client. After accepting a request from the client, connection between server and client is established and the application waits for some data to be received from the client. Received data are printed as ASCII text and retransmitted back to the client.
* UDP Multicast - The application shows how to use the IPV4 & IPV6 UDP multicast features via the BSD-style sockets interface.
Standard BSD API documentation:
http://pubs.opengroup.org/onlinepubs/007908799/xnsix.html
Other references:
https://csperkins.org/teaching/2007-2008/networked-systems/lecture04.pdf
http://wiki.treck.com/Introduction_to_BSD_Sockets
## Host tools
There are many host-side tools which can be used to interact with the UDP/TCP server/client example.
One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following commands.
In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
### Send UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### Receive UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### UDP client using netcat
```
nc -u 192.168.0.167 3333
```
### UDP server using netcat
```
nc -u -l 192.168.0.167 -p 3333
```
### TCP client using netcat
```
nc 192.168.0.167 3333
```
### TCP server using netcat
```
nc -l 192.168.0.167 -p 3333
```
### Python scripts
Each script contains port number, IP version (IPv4 or IPv6) and IP address (only clients) that has to be altered to match the values used by the application. Example:
```
PORT = 3333;
IP_VERSION = 'IPv4'
IPV4 = '192.168.0.167'
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
```
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
* Specific configuration for each example can be found in its README.md file.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

View File

@ -0,0 +1,51 @@
# 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.
# -*- coding: utf-8 -*-
from builtins import input
import socket
import sys
# ----------- Config ----------
PORT = 3333;
IP_VERSION = 'IPv4'
IPV4 = '192.168.0.167'
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
# -------------------------------
if IP_VERSION == 'IPv4':
family_addr = socket.AF_INET
host = IPV4
elif IP_VERSION == 'IPv6':
family_addr = socket.AF_INET6
host = IPV6
else:
print('IP_VERSION must be IPv4 or IPv6')
sys.exit(1)
try:
sock = socket.socket(family_addr, socket.SOCK_STREAM)
except socket.error as msg:
print('Could not create socket: ' + str(msg[0]) + ': ' + msg[1])
sys.exit(1);
try:
sock.connect((host, PORT))
except socket.error as msg:
print('Could not open socket: ', msg)
sock.close()
sys.exit(1);
while True:
msg = input('Enter message to send: ')
assert isinstance(msg, str)
msg = msg.encode()
sock.sendall(msg)
data = sock.recv(1024)
if not data: break;
print( 'Reply: ' + data.decode())
sock.close()

View File

@ -0,0 +1,53 @@
# 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.
# -*- coding: utf-8 -*-
import socket
import sys
# ----------- Config ----------
IP_VERSION = 'IPv4'
PORT = 3333;
# -------------------------------
if IP_VERSION == 'IPv4':
family_addr = socket.AF_INET
elif IP_VERSION == 'IPv6':
family_addr = socket.AF_INET6
else:
print('IP_VERSION must be IPv4 or IPv6')
sys.exit(1)
try:
sock = socket.socket(family_addr, socket.SOCK_STREAM)
except socket.error as msg:
print('Error: ' + str(msg[0]) + ': ' + msg[1])
sys.exit(1)
print('Socket created')
try:
sock.bind(('', PORT))
print('Socket binded')
sock.listen(1)
print('Socket listening')
conn, addr = sock.accept()
print('Connected by', addr)
except socket.error as msg:
print('Error: ' + str(msg[0]) + ': ' + msg[1])
sock.close()
sys.exit(1)
while True:
data = conn.recv(128)
if not data: break
data = data.decode()
print('Received data: ' + data)
reply = 'OK: ' + data
conn.send(reply.encode())
conn.close()

View File

@ -0,0 +1,46 @@
# 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.
# -*- coding: utf-8 -*-
from builtins import input
import socket
import sys
# ----------- Config ----------
PORT = 3333
IP_VERSION = 'IPv4'
IPV4 = '192.168.0.167'
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
# -------------------------------
if IP_VERSION == 'IPv4':
host = IPV4
family_addr = socket.AF_INET
elif IP_VERSION == 'IPv6':
host = IPV6
family_addr = socket.AF_INET6
else:
print('IP_VERSION must be IPv4 or IPv6')
sys.exit(1)
try:
sock = socket.socket(family_addr, socket.SOCK_DGRAM)
except socket.error as msg:
print('Failed to create socket')
sys.exit()
while True:
msg = input('Enter message to send : ')
try:
sock.sendto(msg.encode(), (host, PORT))
reply, addr = sock.recvfrom(128)
if not reply: break
print('Reply[' + addr[0] + ':' + str(addr[1]) + '] - ' + str(reply))
except socket.error as msg:
print('Error Code : ' + str(msg[0]) + ' Message: ' + msg[1])
sys.exit()

View File

@ -0,0 +1,51 @@
# 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.
# -*- coding: utf-8 -*-
import socket
import sys
# ----------- Config ----------
IP_VERSION = 'IPv4'
PORT = 3333;
# -------------------------------
if IP_VERSION == 'IPv4':
family_addr = socket.AF_INET
elif IP_VERSION == 'IPv6':
family_addr = socket.AF_INET6
else:
print('IP_VERSION must be IPv4 or IPv6')
sys.exit(1)
try :
sock = socket.socket(family_addr, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error as msg :
print('Failed to create socket. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
sys.exit()
try:
sock.bind(('', PORT))
except socket.error as msg:
print('Bind failed. Error: ' + str(msg[0]) + ': ' + msg[1])
sys.exit()
while True:
try :
print('Waiting for data...')
data, addr = sock.recvfrom(1024)
if not data: break
data = data.decode()
print('Reply[' + addr[0] + ':' + str(addr[1]) + '] - ' + data)
reply = 'OK ' + data
sock.sendto(reply.encode(), addr)
except socket.error as msg:
print('Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
sock.close()

View File

@ -0,0 +1,6 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_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 := tcp_client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,74 @@
# TCP Client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The application creates a TCP socket and tries to connect to the server with predefined IP address and port number. When a connection is successfully established, the application sends message and waits for the answer. After the server's reply, application prints received reply as ASCII text, waits for 2 seconds and sends another message.
## How to use example
In order to create TCP server that communicates with TCP Client example, choose one of the following options.
There are many host-side tools which can be used to interact with the UDP/TCP server/client.
One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command.
In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
### TCP server using netcat
```
nc -l 192.168.0.167 -p 3333
```
### Python scripts
Script tcpserver.py contains configuration for port number and IP version (IPv4 or IPv6) that has to be altered to match the values used by the application. Example:
```
IP_VERSION = 'IPv4'
PORT = 3333;
```
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
make menuconfig
```
Set following parameter under Serial Flasher Options:
* Set `Default serial port`.
Set following parameters under Example Configuration Options:
* Set `WiFi SSID` of the Router (Access-Point).
* Set `WiFi Password` of the Router (Access-Point).
* Set `IP version` of example to be IPV4 or IPV6.
* Set `IPV4 Address` in case your chose IP version IPV4 above.
* Set `IPV6 Address` in case your chose IP version IPV6 above.
* Set `Port` number that represents remote port the example will connect to.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting
Start server first, to receive data sent from the client (application).

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "tcp_client.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,50 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_IP_MODE
prompt "IP Version"
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
endchoice
config EXAMPLE_IPV4_ADDR
string "IPV4 Address"
default "192.168.0.165"
depends on EXAMPLE_IPV4
help
The example will connect to this IPV4 address.
config EXAMPLE_IPV6_ADDR
string "IPV6 Address"
default "FE80::30AD:E57B:C212:68AD"
depends on EXAMPLE_IPV6
help
The example will connect to this IPV6 address.
config EXAMPLE_PORT
int "Port"
range 0 65535
default 3333
help
The remote port to which the client example will connect to.
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,192 @@
/* BSD Socket API 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/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#ifdef CONFIG_EXAMPLE_IPV4
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#else
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#endif
#define PORT CONFIG_EXAMPLE_PORT
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
const int IPV4_GOTIP_BIT = BIT0;
const int IPV6_GOTIP_BIT = BIT1;
static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
break;
case SYSTEM_EVENT_STA_CONNECTED:
/* enable ipv6 */
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP6");
char *ip6 = ip6addr_ntoa(&event->event_info.got_ip6.ip6_info.ip);
ESP_LOGI(TAG, "IPv6: %s", ip6);
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void wait_for_ip()
{
uint32_t bits = IPV4_GOTIP_BIT | IPV6_GOTIP_BIT ;
ESP_LOGI(TAG, "Waiting for AP connection...");
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
}
static void tcp_client_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 destAddr;
inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr);
destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created");
int err = connect(sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
}
ESP_LOGI(TAG, "Successfully connected");
while (1) {
int err = send(sock, payload, strlen(payload), 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
break;
}
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occured during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Data received
else {
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
wait_for_ip();
xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
}

View File

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

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 := tcp_server
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,72 @@
# TCP Server example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The application creates a TCP socket with the specified port number and waits for a connection request from the client. After accepting a request from the client, connection between server and client is established and the application waits for some data to be received from the client. Received data are printed as ASCII text and retransmitted back to the client.
## How to use example
In order to create TCP client that communicates with TCP server example, choose one of the following options.
There are many host-side tools which can be used to interact with the UDP/TCP server/client.
One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command.
In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
### TCP client using netcat
```
nc 192.168.0.167 3333
```
### Python scripts
Script tcpclient.py contains configuration for port number, IP version (IPv4 or IPv6) and IP address that has to be altered to match the values used by the application. Example:
```
PORT = 3333;
IP_VERSION = 'IPv4'
IPV4 = '192.168.0.167'
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
```
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
make menuconfig
```
Set following parameter under Serial Flasher Options:
* Set `Default serial port`.
Set following parameters under Example Configuration Options:
* Set `WiFi SSID` of the Router (Access-Point).
* Set `WiFi Password` of the Router (Access-Point).
* Set `IP version` of the example to be IPV4 or IPV6.
* Set `Port` number of the socket, that server example will create.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting
Start server first, to receive data sent from the client (application).

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "tcp_server.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,36 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_IP_MODE
prompt "IP Version"
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
endchoice
config EXAMPLE_PORT
int "Port"
range 0 65535
default 3333
help
Local port the example server will listen on.
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,212 @@
/* BSD Socket API 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/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#define PORT CONFIG_EXAMPLE_PORT
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
const int IPV4_GOTIP_BIT = BIT0;
const int IPV6_GOTIP_BIT = BIT1;
static const char *TAG = "example";
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
break;
case SYSTEM_EVENT_STA_CONNECTED:
/* enable ipv6 */
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP6");
char *ip6 = ip6addr_ntoa(&event->event_info.got_ip6.ip6_info.ip);
ESP_LOGI(TAG, "IPv6: %s", ip6);
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void wait_for_ip()
{
uint32_t bits = IPV4_GOTIP_BIT | IPV6_GOTIP_BIT ;
ESP_LOGI(TAG, "Waiting for AP connection...");
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
}
static void tcp_server_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 destAddr;
bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));
destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket binded");
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occured during listen: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_in6 sourceAddr; // Large enough for both IPv4 or IPv6
uint addrLen = sizeof(sourceAddr);
int sock = accept(listen_sock, (struct sockaddr *)&sourceAddr, &addrLen);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket accepted");
while (1) {
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occured during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Connection closed
else if (len == 0) {
ESP_LOGI(TAG, "Connection closed");
break;
}
// Data received
else {
// Get the sender's ip address as string
if (sourceAddr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (sourceAddr.sin6_family == PF_INET6) {
inet6_ntoa_r(sourceAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
int err = send(sock, rx_buffer, len, 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
break;
}
}
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
wait_for_ip();
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
}

View File

@ -0,0 +1,6 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(udp_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 := udp_client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,84 @@
# UDP Client example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The application creates UDP socket and sends message to the predefined port and IP address. After the server's reply, the application prints received reply as ASCII text, waits for 2 seconds and sends another message.
## How to use example
In order to create UDP server that communicates with UDP Client example, choose one of the following options.
There are many host-side tools which can be used to interact with the UDP/TCP server/client.
One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following commands.
In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
### Send UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### Receive UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### UDP server using netcat
```
nc -u -l 192.168.0.167 -p 3333
```
### Python scripts
Script udpserver.py contains configuration for port number and IP version (IPv4 or IPv6) that has to be altered to match the values used by the application. Example:
```
IP_VERSION = 'IPv4'
PORT = 3333;
```
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
make menuconfig
```
Set following parameter under Serial Flasher Options:
* Set `Default serial port`.
Set following parameters under Example Configuration Options:
* Set `WiFi SSID` of the Router (Access-Point).
* Set `WiFi Password` of the Router (Access-Point).
* Set `IP version` of example to be IPV4 or IPV6.
* Set `IPV4 Address` in case your chose IP version IPV4 above.
* Set `IPV6 Address` in case your chose IP version IPV6 above.
* Set `Port` number that represents remote port the example will send data and receive data from.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting
Start server first, to receive data sent from the client (application).

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "udp_client.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,50 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_IP_MODE
prompt "IP Version"
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
endchoice
config EXAMPLE_IPV4_ADDR
string "IPV4 Address"
default "192.168.0.165"
depends on EXAMPLE_IPV4
help
IPV4 address to which the client example will send data.
config EXAMPLE_IPV6_ADDR
string "IPV6 Address"
default "FE80::30AD:E57B:C212:68AD"
depends on EXAMPLE_IPV6
help
IPV6 address to which the client example will send data.
config EXAMPLE_PORT
int "Port"
range 0 65535
default 3333
help
The remote port to which the client example will send data.
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,191 @@
/* BSD Socket API 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/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#ifdef CONFIG_EXAMPLE_IPV4
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#else
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#endif
#define PORT CONFIG_EXAMPLE_PORT
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
const int IPV4_GOTIP_BIT = BIT0;
const int IPV6_GOTIP_BIT = BIT1;
static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
break;
case SYSTEM_EVENT_STA_CONNECTED:
/* enable ipv6 */
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP6");
char *ip6 = ip6addr_ntoa(&event->event_info.got_ip6.ip6_info.ip);
ESP_LOGI(TAG, "IPv6: %s", ip6);
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void wait_for_ip()
{
uint32_t bits = IPV4_GOTIP_BIT | IPV6_GOTIP_BIT ;
ESP_LOGI(TAG, "Waiting for AP connection...");
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
}
static void udp_client_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 destAddr;
inet6_aton(HOST_IP_ADDR, &destAddr.sin6_addr);
destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created");
while (1) {
int err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err < 0) {
ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Message sent");
struct sockaddr_in sourceAddr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(sourceAddr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&sourceAddr, &socklen);
// Error occured during receiving
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
break;
}
// Data received
else {
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
wait_for_ip();
xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}

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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(udp-multicast)

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 := udp-multicast
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,66 @@
# UDP Multicast Example
This example shows how to use the IPV4 & IPV6 UDP multicast features via the BSD-style sockets interface.
## Behaviour
The behaviour of the example is:
* Listens to specified multicast addresses (one IPV4 and/or one IPV6).
* Print any UDP packets received as ASCII text.
* If no packets are received it will periodicially (after 2.5 seconds) send its own plaintext packet(s) to the multicast address(es).
## Configuration
The "Example Configuration" menu "make menuconfig" allows you to configure the details of the example:
* WiFi SSD & Password
* IP Mode: IPV4 & IPV6 dual, IPV4 only, or IPv6 only.
* Multicast addresses for IPV4 and/or IPV6.
* Enable multicast socket loopback (ie should the socket receive its own multicast transmissions.)
* Change the interface to add the multicast group on (default interface, or WiFi STA interface.) Both methods are valid.
## Implementation Details
In IPV4 & IPV6 dual mode, an IPV6 socket is created and the "dual mode" options described in [RFC4038](https://tools.ietf.org/html/rfc4038) are used to bind it to the default address for both IPV4 & IPV6 and join both the configured IPV4 & IPV6 multicast groups. Otherwise, a single socket of the appropriate type is created.
The socket is always bound to the default address, so it will also receive unicast packets. If you only want to receive multicast packets for a particular address, `bind()` to that multicast address instead.
## Host Tools
There are many host-side tools which can be used to interact with the UDP multicast example. One command line tool is [socat](http://www.dest-unreach.org/socat/) which can send and receive many kinds of packets.
### Send IPV4 multicast via socat
```
echo "Hi there, IPv4!" | socat STDIO UDP4-DATAGRAM:232.10.11.12:3333,ip-multicast-if=(host_ip_addr)
```
Replace `232.10.11.12:3333` with the IPV4 multicast address and port, and `(host_ip_addr)` with the host's IP address (used to find the interface to send the multicast packet on.)
### Receive IPV4 multicast via socat
```
socat STDIO UDP4-RECVFROM:3333,ip-add-membership=232.10.11.12:(host_ip_addr)
```
Replace `:3333` and `232.10.11.12` with the port and IPV4 multicast address, respectively. Replace `(host_ip_addr)` with the host IP address, used to find the interface to listen on.
(The `,ip-add-membership=...` clause may not be necessary, depending on your network configuration.)
### Send IPV6 multicast via socat
```
echo "Hi there, IPV6!" | socat STDIO UDP6-DATAGRAM:[ff02::fc]:3333
```
Replace `[ff02::fc]:3333` with the IPV6 multicast address and port, respectively.
### Receive IPV6 multicast via socat
At time of writing this is not possible without patching socat. Use a different tool or programming language to receive IPV6 multicast packets.
## About Examples
See the README.md file in the upper level 'examples' directory for general information about examples.

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "udp_multicast_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,91 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_IP_MODE
prompt "Multicast IP type"
help
Example can multicast IPV4, IPV6, or both.
config EXAMPLE_IPV4_V6
bool "IPV4 & IPV6"
select EXAMPLE_IPV4
select EXAMPLE_IPV6
config EXAMPLE_IPV4_ONLY
bool "IPV4"
select EXAMPLE_IPV4
config EXAMPLE_IPV6_ONLY
bool "IPV6"
select EXAMPLE_IPV6
endchoice
config EXAMPLE_IPV4
bool
config EXAMPLE_IPV6
bool
config EXAMPLE_MULTICAST_IPV4_ADDR
string "Multicast IPV4 Address (send & receive)"
default "232.10.11.12"
depends on EXAMPLE_IPV4
help
IPV4 multicast address. Example will both send to and listen to this address.
config EXAMPLE_MULTICAST_IPV6_ADDR
string "Multicast IPV6 Address (send & receive)"
default "FF02::FC"
depends on EXAMPLE_IPV6
help
IPV6 multicast address. Example will both send to and listen to this address.
The default FF02::FC address is a link-local multicast address. Consult IPV6 specifications or documentation for information about meaning of different IPV6 multicast ranges.
config EXAMPLE_PORT
int "Multicast port (send & receive)"
range 0 65535
default 3333
help
Multicast port the example will both send & receive UDP packets on.
config EXAMPLE_LOOPBACK
bool "Multicast loopback"
help
Enables IP_MULTICAST_LOOP/IPV6_MULTICAST_LOOP options, meaning
that packets transmitted from the device are also received by the
device itself.
config EXAMPLE_MULTICAST_TTL
int "Multicast packet TTL"
range 1 255
help
Sets TTL field of multicast packets. Separate from uni- & broadcast TTL.
choice EXAMPLE_MULTICAST_IF
prompt "Multicast Interface"
help
Multicast socket can bind to default interface, or all interfaces.
config EXAMPLE_MULTICAST_LISTEN_DEFAULT_IF
bool "Default interface"
config EXAMPLE_MULTICAST_LISTEN_STA_IF
bool "WiFi STA interface"
endchoice
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,553 @@
/* UDP MultiCast Send/Receive 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/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#define UDP_PORT CONFIG_EXAMPLE_PORT
#define MULTICAST_LOOPBACK CONFIG_EXAMPLE_LOOPBACK
#define MULTICAST_TTL CONFIG_EXAMPLE_MULTICAST_TTL
#define MULTICAST_IPV4_ADDR CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR
#define MULTICAST_IPV6_ADDR CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR
#define LISTEN_DEFAULT_IF CONFIG_EXAMPLE_LISTEN_DEFAULT_IF
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
we use two - one for IPv4 "got ip", and
one for IPv6 "got ip". */
const int IPV4_GOTIP_BIT = BIT0;
const int IPV6_GOTIP_BIT = BIT1;
static const char *TAG = "multicast";
#ifdef CONFIG_EXAMPLE_IPV4
static const char *V4TAG = "mcast-ipv4";
#endif
#ifdef CONFIG_EXAMPLE_IPV6
static const char *V6TAG = "mcast-ipv6";
#endif
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_CONNECTED:
/* enable ipv6 */
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
#ifdef CONFIG_EXAMPLE_IPV4
/* Add a socket, either IPV4-only or IPV6 dual mode, to the IPV4
multicast group */
static int socket_add_ipv4_multicast_group(int sock, bool assign_source_if)
{
struct ip_mreq imreq = { 0 };
struct in_addr iaddr = { 0 };
int err = 0;
// Configure source interface
#if USE_DEFAULT_IF
imreq.imr_interface.s_addr = IPADDR_ANY;
#else
tcpip_adapter_ip_info_t ip_info = { 0 };
err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
if (err != ESP_OK) {
ESP_LOGE(V4TAG, "Failed to get IP address info. Error 0x%x", err);
goto err;
}
inet_addr_from_ip4addr(&iaddr, &ip_info.ip);
#endif
// Configure multicast address to listen to
err = inet_aton(MULTICAST_IPV4_ADDR, &imreq.imr_multiaddr.s_addr);
if (err != 1) {
ESP_LOGE(V4TAG, "Configured IPV4 multicast address '%s' is invalid.", MULTICAST_IPV4_ADDR);
goto err;
}
ESP_LOGI(TAG, "Configured IPV4 Multicast address %s", inet_ntoa(imreq.imr_multiaddr.s_addr));
if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) {
ESP_LOGW(V4TAG, "Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV4_ADDR);
}
if (assign_source_if) {
// Assign the IPv4 multicast source interface, via its IP
// (only necessary if this socket is IPV4 only)
err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr,
sizeof(struct in_addr));
if (err < 0) {
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_IF. Error %d", errno);
goto err;
}
}
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&imreq, sizeof(struct ip_mreq));
if (err < 0) {
ESP_LOGE(V4TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
goto err;
}
err:
return err;
}
#endif /* CONFIG_EXAMPLE_IPV4 */
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
static int create_multicast_ipv4_socket()
{
struct sockaddr_in saddr = { 0 };
int sock = -1;
int err = 0;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(V4TAG, "Failed to create socket. Error %d", errno);
return -1;
}
// Bind the socket to any address
saddr.sin_family = PF_INET;
saddr.sin_port = htons(UDP_PORT);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (err < 0) {
ESP_LOGE(V4TAG, "Failed to bind socket. Error %d", errno);
goto err;
}
// Assign multicast TTL (set separately from normal interface TTL)
uint8_t ttl = MULTICAST_TTL;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
if (err < 0) {
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_TTL. Error %d", errno);
goto err;
}
#if MULTICAST_LOOPBACK
// select whether multicast traffic should be received by this device, too
// (if setsockopt() is not called, the default is no)
uint8_t loopback_val = MULTICAST_LOOPBACK;
err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
&loopback_val, sizeof(uint8_t));
if (err < 0) {
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_LOOP. Error %d", errno);
goto err;
}
#endif
// this is also a listening socket, so add it to the multicast
// group for listening...
err = socket_add_ipv4_multicast_group(sock, true);
if (err < 0) {
goto err;
}
// All set, socket is configured for sending and receiving
return sock;
err:
close(sock);
return -1;
}
#endif /* CONFIG_EXAMPLE_IPV4_ONLY */
#ifdef CONFIG_EXAMPLE_IPV6
static int create_multicast_ipv6_socket()
{
struct sockaddr_in6 saddr = { 0 };
struct in6_addr if_inaddr = { 0 };
struct ip6_addr if_ipaddr = { 0 };
struct ip6_mreq v6imreq = { 0 };
int sock = -1;
int err = 0;
sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
if (sock < 0) {
ESP_LOGE(V6TAG, "Failed to create socket. Error %d", errno);
return -1;
}
// Bind the socket to any address
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(UDP_PORT);
bzero(&saddr.sin6_addr.un, sizeof(saddr.sin6_addr.un));
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to bind socket. Error %d", errno);
goto err;
}
// Selct the interface to use as multicast source for this socket.
#if USE_DEFAULT_IF
bzero(&if_inaddr.un, sizeof(if_inaddr.un));
#else
// Read interface adapter link-local address and use it
// to bind the multicast IF to this socket.
//
// (Note the interface may have other non-LL IPV6 addresses as well,
// but it doesn't matter in this context as the address is only
// used to identify the interface.)
err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &if_ipaddr);
inet6_addr_from_ip6addr(&if_inaddr, &if_ipaddr);
if (err != ESP_OK) {
ESP_LOGE(V6TAG, "Failed to get IPV6 LL address. Error 0x%x", err);
goto err;
}
#endif
// Assign the multicast source interface, via its IP
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_inaddr,
sizeof(struct in6_addr));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
goto err;
}
// Assign multicast TTL (set separately from normal interface TTL)
uint8_t ttl = MULTICAST_TTL;
setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(uint8_t));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_HOPS. Error %d", errno);
goto err;
}
#if MULTICAST_LOOPBACK
// select whether multicast traffic should be received by this device, too
// (if setsockopt() is not called, the default is no)
uint8_t loopback_val = MULTICAST_LOOPBACK;
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loopback_val, sizeof(uint8_t));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_LOOP. Error %d", errno);
goto err;
}
#endif
// this is also a listening socket, so add it to the multicast
// group for listening...
// Configure source interface
#if USE_DEFAULT_IF
v6imreq.imr_interface.s_addr = IPADDR_ANY;
#else
inet6_addr_from_ip6addr(&v6imreq.ipv6mr_interface, &if_ipaddr);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
// Configure multicast address to listen to
err = inet6_aton(MULTICAST_IPV6_ADDR, &v6imreq.ipv6mr_multiaddr);
if (err != 1) {
ESP_LOGE(V6TAG, "Configured IPV6 multicast address '%s' is invalid.", MULTICAST_IPV6_ADDR);
goto err;
}
ESP_LOGI(TAG, "Configured IPV6 Multicast address %s", inet6_ntoa(v6imreq.ipv6mr_multiaddr));
ip6_addr_t multi_addr;
inet6_addr_to_ip6addr(&multi_addr, &v6imreq.ipv6mr_multiaddr);
if (!ip6_addr_ismulticast(&multi_addr)) {
ESP_LOGW(V6TAG, "Configured IPV6 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV6_ADDR);
}
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
&v6imreq, sizeof(struct ip6_mreq));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
goto err;
}
#endif
#if CONFIG_EXAMPLE_IPV4_V6
// Add the common IPV4 config options
err = socket_add_ipv4_multicast_group(sock, false);
if (err < 0) {
goto err;
}
#endif
#if CONFIG_EXAMPLE_IPV4_V6
int only = 0;
#else
int only = 1; /* IPV6-only socket */
#endif
err = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &only, sizeof(int));
if (err < 0) {
ESP_LOGE(V6TAG, "Failed to set IPV6_V6ONLY. Error %d", errno);
goto err;
}
ESP_LOGI(TAG, "Socket set IPV6-only");
// All set, socket is configured for sending and receiving
return sock;
err:
close(sock);
return -1;
}
#endif
static void mcast_example_task(void *pvParameters)
{
while (1) {
/* Wait for all the IPs we care about to be set
*/
uint32_t bits = 0;
#ifdef CONFIG_EXAMPLE_IPV4
bits |= IPV4_GOTIP_BIT;
#endif
#ifdef CONFIG_EXAMPLE_IPV6
bits |= IPV6_GOTIP_BIT;
#endif
ESP_LOGI(TAG, "Waiting for AP connection...");
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
int sock;
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
sock = create_multicast_ipv4_socket();
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create IPv4 multicast socket");
}
#else
sock = create_multicast_ipv6_socket();
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create IPv6 multicast socket");
}
#endif
if (sock < 0) {
// Nothing to do!
vTaskDelay(5 / portTICK_PERIOD_MS);
continue;
}
#ifdef CONFIG_EXAMPLE_IPV4
// set destination multicast addresses for sending from these sockets
struct sockaddr_in sdestv4 = {
.sin_family = PF_INET,
.sin_port = htons(UDP_PORT),
};
// We know this inet_aton will pass because we did it above already
inet_aton(MULTICAST_IPV4_ADDR, &sdestv4.sin_addr.s_addr);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
struct sockaddr_in6 sdestv6 = {
.sin6_family = PF_INET6,
.sin6_port = htons(UDP_PORT),
};
// We know this inet_aton will pass because we did it above already
inet6_aton(MULTICAST_IPV6_ADDR, &sdestv6.sin6_addr);
#endif
// Loop waiting for UDP received, and sending UDP packets if we don't
// see any.
int err = 1;
while (err > 0) {
struct timeval tv = {
.tv_sec = 2,
.tv_usec = 0,
};
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
int s = select(sock + 1, &rfds, NULL, NULL, &tv);
if (s < 0) {
ESP_LOGE(TAG, "Select failed: errno %d", errno);
err = -1;
break;
}
else if (s > 0) {
if (FD_ISSET(sock, &rfds)) {
// Incoming datagram received
char recvbuf[48];
char raddr_name[32] = { 0 };
struct sockaddr_in6 raddr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(raddr);
int len = recvfrom(sock, recvbuf, sizeof(recvbuf)-1, 0,
(struct sockaddr *)&raddr, &socklen);
if (len < 0) {
ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
err = -1;
break;
}
// Get the sender's address as a string
#ifdef CONFIG_EXAMPLE_IPV4
if (raddr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&raddr)->sin_addr.s_addr,
raddr_name, sizeof(raddr_name)-1);
}
#endif
#ifdef CONFIG_EXAMPLE_IPV6
if (raddr.sin6_family == PF_INET6) {
inet6_ntoa_r(raddr.sin6_addr, raddr_name, sizeof(raddr_name)-1);
}
#endif
ESP_LOGI(TAG, "received %d bytes from %s:", len, raddr_name);
recvbuf[len] = 0; // Null-terminate whatever we received and treat like a string...
ESP_LOGI(TAG, "%s", recvbuf);
}
}
else { // s == 0
// Timeout passed with no incoming data, so send something!
static int send_count;
const char sendfmt[] = "Multicast #%d sent by ESP32\n";
char sendbuf[48];
char addrbuf[32] = { 0 };
int len = snprintf(sendbuf, sizeof(sendbuf), sendfmt, send_count++);
if (len > sizeof(sendbuf)) {
ESP_LOGE(TAG, "Overflowed multicast sendfmt buffer!!");
send_count = 0;
err = -1;
break;
}
struct addrinfo hints = {
.ai_flags = AI_PASSIVE,
.ai_socktype = SOCK_DGRAM,
};
struct addrinfo *res;
#ifdef CONFIG_EXAMPLE_IPV4 // Send an IPv4 multicast packet
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
hints.ai_family = AF_INET; // For an IPv4 socket
#else
hints.ai_family = AF_INET6; // For an IPv4 socket with V4 mapped addresses
hints.ai_flags |= AI_V4MAPPED;
#endif
int err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR,
NULL,
&hints,
&res);
if (err < 0) {
ESP_LOGE(TAG, "getaddrinfo() failed for IPV4 destination address. error: %d", err);
break;
}
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
((struct sockaddr_in *)res->ai_addr)->sin_port = htons(UDP_PORT);
inet_ntoa_r(((struct sockaddr_in *)res->ai_addr)->sin_addr, addrbuf, sizeof(addrbuf)-1);
ESP_LOGI(TAG, "Sending to IPV4 multicast address %s:%d...", addrbuf, UDP_PORT);
#else
((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(UDP_PORT);
inet6_ntoa_r(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrbuf, sizeof(addrbuf)-1);
ESP_LOGI(TAG, "Sending to IPV6 (V4 mapped) multicast address %s port %d (%s)...", addrbuf, UDP_PORT, CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR);
#endif
err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if (err < 0) {
ESP_LOGE(TAG, "IPV4 sendto failed. errno: %d", errno);
break;
}
#endif
#ifdef CONFIG_EXAMPLE_IPV6
hints.ai_family = AF_INET6;
hints.ai_protocol = 0;
err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR,
NULL,
&hints,
&res);
if (err < 0) {
ESP_LOGE(TAG, "getaddrinfo() failed for IPV6 destination address. error: %d", err);
break;
}
struct sockaddr_in6 *s6addr = (struct sockaddr_in6 *)res->ai_addr;
s6addr->sin6_port = htons(UDP_PORT);
inet6_ntoa_r(s6addr->sin6_addr, addrbuf, sizeof(addrbuf)-1);
ESP_LOGI(TAG, "Sending to IPV6 multicast address %s port %d...", addrbuf, UDP_PORT);
err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if (err < 0) {
ESP_LOGE(TAG, "IPV6 sendto failed. errno: %d", errno);
break;
}
#endif
}
}
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
xTaskCreate(&mcast_example_task, "mcast_task", 4096, NULL, 5, NULL);
}

View File

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

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 := udp_server
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,82 @@
# UDP Server example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The application creates UDP socket with the specified port number and waits for the data to be received. Received data are printed as ASCII text and retransmitted back to the client.
## How to use example
In order to create UDP client that communicates with UDP server example, choose one of the following options.
There are many host-side tools which can be used to interact with the UDP/TCP server/client.
One command line tool is [netcat](http://netcat.sourceforge.net) which can send and receive many kinds of packets.
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following commands.
In addition to those tools, simple Python scripts can be found under sockets/scripts directory. Every script is designed to interact with one of the examples.
### Send UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### Receive UDP packet via netcat
```
echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
```
### UDP client using netcat
```
nc -u 192.168.0.167 3333
```
### Python scripts
Script udpclient.py contains configuration for port number, IP version (IPv4 or IPv6) and IP address that has to be altered to match the values used by the application. Example:
```
PORT = 3333;
IP_VERSION = 'IPv4'
IPV4 = '192.168.0.167'
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
```
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
make menuconfig
```
Set following parameter under Serial Flasher Options:
* Set `Default serial port`.
Set following parameters under Example Configuration Options:
* Set `WiFi SSID` of the Router (Access-Point).
* Set `WiFi Password` of the Router (Access-Point).
* Set `IP version` of the example to be IPV4 or IPV6.
* Set `Port` number that represents remote port the example will create.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Troubleshooting
Start server first, to receive data sent from the client (application).

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "udp_server.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,36 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
choice EXAMPLE_IP_MODE
prompt "IP Version"
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
endchoice
config EXAMPLE_PORT
int "Port"
range 0 65535
default 3333
help
Local port the example server will listen on.
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,195 @@
/* BSD Socket API 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/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
#define PORT CONFIG_EXAMPLE_PORT
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
const int IPV4_GOTIP_BIT = BIT0;
const int IPV6_GOTIP_BIT = BIT1;
static const char *TAG = "example";
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
break;
case SYSTEM_EVENT_STA_CONNECTED:
/* enable ipv6 */
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
break;
case SYSTEM_EVENT_AP_STA_GOT_IP6:
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP6");
char *ip6 = ip6addr_ntoa(&event->event_info.got_ip6.ip6_info.ip);
ESP_LOGI(TAG, "IPv6: %s", ip6);
default:
break;
}
return ESP_OK;
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void wait_for_ip()
{
uint32_t bits = IPV4_GOTIP_BIT | IPV6_GOTIP_BIT ;
ESP_LOGI(TAG, "Waiting for AP connection...");
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
}
static void udp_server_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 destAddr;
bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));
destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created");
int err = bind(sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err < 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
}
ESP_LOGI(TAG, "Socket binded");
while (1) {
ESP_LOGI(TAG, "Waiting for data");
struct sockaddr_in6 sourceAddr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(sourceAddr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&sourceAddr, &socklen);
// Error occured during receiving
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
break;
}
// Data received
else {
// Get the sender's ip address as string
if (sourceAddr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (sourceAddr.sin6_family == PF_INET6) {
inet6_ntoa_r(sourceAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *)&sourceAddr, sizeof(sourceAddr));
if (err < 0) {
ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
break;
}
}
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
void app_main()
{
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
wait_for_ip();
xTaskCreate(udp_server_task, "udp_server", 4096, NULL, 5, NULL);
}