添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
# 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 in the application directory could be used to exercise the socket communication.
Command line arguments such as IP version (IPv4 or IPv6) and IP address and payload data (only clients) shall be supplied.
In addition to that, port number and interface id are hardcoded in the scripts and might need to be altered to match the values used by the application. Example:
```
PORT = 3333
INTERFACE = 'en0'
```
### Note about IPv6 addresses
Examples are configured to obtain multiple IPv6 addresses. The actual behavior may differ depending on the local network, typically the ESP gets assigned these two addresses
* Local Link address
* Unique Local address
The value and type of the IPv6 address is displayed in the terminal, for example:
Please make sure that when using the Local Link address, an interface id is included in the configuration:
* In the embedded code
```
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(esp_netif_instance);
```
* On the host
- Interface name suffix is present when passing the address as a string, for example `fe80::260a:XXX:XXX:XXX%en0`
- The interface id is present when passing the endpoint as tupple, for example `socket.connect(('fd00::260a:XXXX:XXXX:XXXX', 3333, 0, 3))`
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
idf.py menuconfig
```
* 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:
```
idf.py -p PORT 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,10 @@
# 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)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_client)

View File

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

View File

@@ -0,0 +1,69 @@
# 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 3333
```
### Python scripts
Script example_test.py could be used as a counter part to the tcp-client project, ip protocol name (IPv4 or IPv6) shall be stated as argument. Example:
```
python example_test.py IPv4
```
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported;
please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`.
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
idf.py menuconfig
```
Set following parameters under Example Configuration Options:
* 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.
Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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,125 @@
# 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 __future__ import print_function
from __future__ import unicode_literals
from builtins import input
import os
import re
import sys
import netifaces
import socket
from threading import Thread, Event
import ttfw_idf
# ----------- Config ----------
PORT = 3333
INTERFACE = 'eth0'
# -------------------------------
def get_my_ip(type):
for i in netifaces.ifaddresses(INTERFACE)[type]:
return i['addr'].replace("%{}".format(INTERFACE), "")
class TcpServer:
def __init__(self, port, family_addr, persist=False):
self.port = port
self.socket = socket.socket(family_addr, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.settimeout(10.0)
self.shutdown = Event()
self.persist = persist
self.family_addr = family_addr
def __enter__(self):
try:
self.socket.bind(('', self.port))
except socket.error as e:
print("Bind failed:{}".format(e))
raise
self.socket.listen(1)
self.server_thread = Thread(target=self.run_server)
self.server_thread.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.persist:
sock = socket.socket(self.family_addr, socket.SOCK_STREAM)
sock.connect(('localhost', self.port))
sock.sendall(b'Stop', )
sock.close()
self.shutdown.set()
self.shutdown.set()
self.server_thread.join()
self.socket.close()
def run_server(self):
while not self.shutdown.is_set():
try:
conn, address = self.socket.accept() # accept new connection
print("Connection from: {}".format(address))
conn.setblocking(1)
data = conn.recv(1024)
if not data:
return
data = data.decode()
print('Received data: ' + data)
reply = 'OK: ' + data
conn.send(reply.encode())
conn.close()
except socket.error as e:
print("Running server failed:{}".format(e))
raise
if not self.persist:
break
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_socket(env, extra_data):
"""
steps:
1. join AP
2. have the board connect to the server
3. send and receive data
"""
dut1 = env.get_dut("tcp_client", "examples/protocols/sockets/tcp_client", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "tcp_client.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("tcp_client_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("tcp_client_bin_size", bin_size // 1024, dut1.TARGET)
# start test
dut1.start_app()
data = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)
print("Connected with IPv4: {}".format(data[0]))
# test IPv4
with TcpServer(PORT, socket.AF_INET):
dut1.write(get_my_ip(netifaces.AF_INET))
dut1.expect(re.compile(r"OK: Message from ESP32"))
# test IPv6
with TcpServer(PORT, socket.AF_INET6):
dut1.write(get_my_ip(netifaces.AF_INET6))
dut1.expect(re.compile(r"OK: Message from ESP32"))
if __name__ == '__main__':
if sys.argv[1:] and sys.argv[1].startswith("IPv"): # if additional arguments provided:
# Usage: example_test.py <IPv4|IPv6>
family_addr = socket.AF_INET6 if sys.argv[1] == "IPv6" else socket.AF_INET
with TcpServer(PORT, family_addr, persist=True) as s:
print(input("Press Enter stop the server..."))
else:
test_examples_protocol_socket()

View File

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

View File

@@ -0,0 +1,51 @@
menu "Example Configuration"
choice EXAMPLE_IP_MODE
prompt "IP Version"
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
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.
choice EXAMPLE_SOCKET_IP_INPUT
prompt "Socket example source"
default EXAMPLE_SOCKET_IP_INPUT_STRING
help
Selects the input source of the IP used in the example.
config EXAMPLE_SOCKET_IP_INPUT_STRING
bool "From string"
config EXAMPLE_SOCKET_IP_INPUT_STDIN
bool "From stdin"
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,125 @@
/* 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.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif
#define PORT CONFIG_EXAMPLE_PORT
static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";
static void tcp_client_task(void *pvParameters)
{
char rx_buffer[128];
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
struct sockaddr_in6 dest_addr = { 0 };
inet6_aton(host_ip, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
struct sockaddr_in6 dest_addr = { 0 };
ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#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, connecting to %s:%d", host_ip, PORT);
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Successfully connected");
while (1) {
int err = send(sock, payload, strlen(payload), 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occurred 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, host_ip);
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(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1 @@
CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y

View File

@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_client_multiple)

View File

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

View File

@@ -0,0 +1,133 @@
# Multiple Ethernet Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of Ethernet interface and WiFi station together. The workflow of the example could be as follow:
1. Connects to both WiFi and Ethernet using common-connect component
2. Starts two tasks, one for each interface to resolve configured host name and connect to it periodically
3. Connection to host endpoint is handled by:
- creating a socket as TCP client
- binding it to the related interface (Ethernet of WiFi)
- send and receive a trivial HTTP request and response
If you have a new multiple interface application to go (for example, connect to IoT cloud via Ethernet and WiFi), try this as a basic template, then add your own code.
## How to use example
### Hardware Required
To run this example, you need to have one ESP32 development board integrated with an Ethernet interface, for example, ESP32-Ethernet-Kit, or just connect your ESP32-DevkitC board to a breakout board which features RMII Ethernet PHY.
### Configure the project
Enter project configuration by `idf.py menuconfig` (or `make menuconfig` if using legacy GNU Make build system) and navigate into:
* `Example Connection Configuration` menu to choose the connection details:
- Enter SSID and password for WiFi connection
- Set Ethernet type and configuration for Ethernet connection
- Note that the project is preconfigured to have both WiFi and Ethernet interface enabled by default
- See the [README.md](../../README.md) for more details about common example connection component
* `Example Configuration` menu:
- Set host name and port for the tcp_client to connect to
### 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.
## Example Output
```bash
I (695) example_connect: Connecting to DavidsAP...
I (795) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
I (795) wifi:mode : sta (30:ae:a4:c6:b4:f8)
W (795) event: handler already registered, overwriting
I (815) esp_eth.netif.glue: 30:ae:a4:c6:b4:fb
I (815) esp_eth.netif.glue: ethernet attached to netif
I (825) example_connect: Waiting for IP(s)
I (1525) wifi:new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
I (2295) wifi:state: init -> auth (b0)
I (2295) wifi:state: auth -> assoc (0)
I (2305) wifi:state: assoc -> run (10)
I (2315) wifi:connected with DavidsAP, aid = 2, channel 6, BW20, bssid = 16:f7:28:37:58:36
I (2315) wifi:security type: 3, phy: bgn, rssi: -35
I (2315) wifi:pm start, type: 1
I (2325) wifi:AP's beacon interval = 102400 us, DTIM period = 3
I (3125) esp_netif_handlers: example_connect: sta ip: 192.168.2.15, mask: 255.255.255.0, gw: 192.168.2.1
I (3125) example_connect: Interface desciption example_connect: sta
I (3135) example_connect: Interface "example_connect: sta" got IPv4 address: 192.168.2.15
I (3625) example_connect: Interface "example_connect: sta" got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4f8, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (4825) example_connect: Ethernet Link Up
I (5625) esp_netif_handlers: example_connect: eth ip: 192.168.32.148, mask: 255.255.252.0, gw: 192.168.32.3
I (5625) example_connect: Interface desciption example_connect: eth
I (5635) example_connect: Interface "example_connect: eth" got IPv4 address: 192.168.32.148
I (6625) example_connect: Interface "example_connect: eth" got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4fb, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6625) example_connect: Connected to example_connect: eth
I (6635) example_connect: - IPv4 address: 192.168.32.148
I (6635) example_connect: - IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4fbtype: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6645) example_connect: Connected to example_connect: sta
I (6655) example_connect: - IPv4 address: 192.168.2.15
I (6655) example_connect: - IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4f8type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6675) example_connect: Connected to Ethernet
I (6675) tcp_client_multiple: netif described as "sta" corresponds to esp-netif ptr:0x3ffba3ac
I (6675) tcp_client_multiple: netif described as "eth" corresponds to esp-netif ptr:0x3ffc608c
I (6895) tcp_client_multiple: "example_connect: eth" Socket created
I (6895) tcp_client_multiple: "example_connect: sta" Socket created
I (6895) tcp_client_multiple: "example_connect: eth" Successfully connected
I (6905) tcp_client_multiple: "example_connect: sta" Successfully connected
I (6965) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (6965) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:58 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (6965) tcp_client_multiple: "example_connect: sta" Received Data 127 bytes
I (6985) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:58 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (7675) tcp_client_multiple: "example_connect: eth" Socket created
I (7675) tcp_client_multiple: "example_connect: eth" Successfully connected
I (7695) tcp_client_multiple: "example_connect: sta" Socket created
I (7705) tcp_client_multiple: "example_connect: sta" Successfully connected
I (7735) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (7735) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:59 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (7955) tcp_client_multiple: "example_connect: sta" Received Data 127 bytes
I (7955) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:59 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (8445) tcp_client_multiple: "example_connect: eth" Socket created
I (8445) tcp_client_multiple: "example_connect: eth" Successfully connected
I (8505) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (8505) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:03:00 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (8675) tcp_client_multiple: "example_connect: sta" Socket created
```
## Troubleshooting
* When connecting using Ethernet, please consult troubleshooting described in [Ethernet common readme](../../../ethernet/README.md)
or [Ethernet documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html).
If using Ethernet for the first time, it is recommended to start with the [Ethernet example readme](../../../ethernet/basic/README.md), which contains instructions for connecting and configuring the PHY.
Once Ethernet example obtains IP address successfully, proceed to this example.
* When connecting using Wi-Fi, please refer to the WiFi examples in [examples/wifi/getting_started/](../../../wifi/getting_started).

View File

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

View File

@@ -0,0 +1,24 @@
menu "Example Configuration"
config EXAMPLE_HOST_NAME
string "Host Name"
default "baidu.com"
help
host name
config EXAMPLE_HOST_PORT
int "Host Port"
default 80
range 0 65535
help
host port
config EXAMPLE_BIND_SOCKET_TO_NETIF_NAME
bool "Bind to interface by its name"
default y
help
By default example uses setsockopt() to bind the tcp-client's socket
to specific interface. Setting this option to true demonstrates binding
the socket to the local ip address of the network interface.
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,157 @@
/* multiple network interface Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include <sys/socket.h>
#include <netdb.h>
#include "protocol_examples_common.h"
static const char *TAG = "tcp_client_multiple";
#define HOST_NAME CONFIG_EXAMPLE_HOST_NAME
#define HOST_IP_PORT CONFIG_EXAMPLE_HOST_PORT
static const char *payload = "GET / HTTP/1.1\r\n\r\n";
static void app_multiple_handle(esp_ip4_addr_t *ip4_addr, esp_netif_t *esp_netif)
{
char rx_buffer[128] = {0};
const char *netif_name = esp_netif_get_desc(esp_netif);
/* Create a socket */
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to create socket: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ESP_LOGI(TAG, "\"%s\" Socket created", netif_name);
/* Bind the socket to an interface (based on example config option)
* - using netif local IP address
* - using netif name
*/
#if CONFIG_EXAMPLE_BIND_SOCKET_TO_NETIF_NAME
struct ifreq ifr;
esp_netif_get_netif_impl_name(esp_netif, ifr.ifr_name);
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
#else
esp_netif_ip_info_t ip;
memset(&ip, 0, sizeof(esp_netif_ip_info_t));
ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif, &ip));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(0);
addr.sin_addr.s_addr = ip.ip.addr;
int ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
#endif /* CONFIG_EXAMPLE_BIND_SOCKET_TO_NETIF_NAME */
/* Connect to the host by the network interface */
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = ip4_addr->addr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(HOST_IP_PORT);
ret = connect(sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (ret != 0) {
ESP_LOGE(TAG, "\"%s\" Socket unable to connect: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ESP_LOGI(TAG, "\"%s\" Successfully connected", netif_name);
ret = send(sock, payload, strlen(payload), 0);
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Error occured during sending: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ret = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Error occured during receiving: errno %d", netif_name, errno);
} else if (ret > 0){
rx_buffer[ret] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "\"%s\" Received Data %d bytes", netif_name, ret);
ESP_LOGI(TAG, "%s", rx_buffer);
} else {
ESP_LOGE(TAG, "\"%s\" Closed connection during receiving", netif_name);
}
app_multiple_handle_fail:
close(sock);
}
static void app_connection_task(void *pvParameters)
{
esp_ip4_addr_t ip4_addr;
const char *netif_desc = pvParameters;
esp_netif_t *netif = get_example_netif_from_desc(netif_desc);
ESP_LOGD(TAG, "netif described as \"%s\" corresponds to esp-netif ptr:%p", netif_desc, netif);
while(netif) {
/* Wait for the host name to get */
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
int err = getaddrinfo(HOST_NAME, NULL, &hints, &res);
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
break;
}
memcpy(&ip4_addr, &((struct sockaddr_in *)(res->ai_addr))->sin_addr, sizeof(ip4_addr));
freeaddrinfo(res);
/* Connect the host using the corresponding network interface */
app_multiple_handle(&ip4_addr, netif);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
ESP_LOGE(TAG, "%s with netif desc:%s Failed! exiting", __func__, netif_desc);
vTaskDelete(NULL);
}
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
esp_netif_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
xTaskCreate(&app_connection_task, "app_ethernet_task", 4096, "eth", 5, NULL);
xTaskCreate(&app_connection_task, "app_wifi_task", 4096, "sta", 5, NULL);
}

View File

@@ -0,0 +1,2 @@
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y

View File

@@ -0,0 +1,10 @@
# 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)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_server)

View File

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

View File

@@ -0,0 +1,66 @@
# 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 example_test.py could be used as a counter part to the tcp-server application,
IP address and the message to be send to the server shall be stated as arguments. Example:
```
python example_test.py 192.168.0.167 Message
```
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported;
please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`.
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
idf.py menuconfig
```
Set following parameters under Example Configuration Options:
* Set `IP version` of the example to be IPV4 or IPV6.
* Set `Port` number of the socket, that server example will create.
Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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,89 @@
# 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 __future__ import print_function
from __future__ import unicode_literals
import os
import sys
import re
import socket
import ttfw_idf
# ----------- Config ----------
PORT = 3333
INTERFACE = 'eth0'
# -------------------------------
def tcp_client(address, payload):
for res in socket.getaddrinfo(address, PORT, socket.AF_UNSPEC,
socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
family_addr, socktype, proto, canonname, addr = res
try:
sock = socket.socket(family_addr, socket.SOCK_STREAM)
except socket.error as msg:
print('Could not create socket: ' + str(msg[0]) + ': ' + msg[1])
raise
try:
sock.connect(addr)
except socket.error as msg:
print('Could not open socket: ', msg)
sock.close()
raise
sock.sendall(payload)
data = sock.recv(1024)
if not data:
return
print('Reply : ' + data.decode())
sock.close()
return data
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_socket(env, extra_data):
MESSAGE = "Data to ESP"
"""
steps:
1. join AP
2. have the board connect to the server
3. send and receive data
"""
dut1 = env.get_dut("tcp_client", "examples/protocols/sockets/tcp_server", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "tcp_server.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("tcp_server_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("tcp_server_bin_size", bin_size // 1024, dut1.TARGET)
# start test
dut1.start_app()
ipv4 = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)[0]
ipv6_r = r':'.join((r'[0-9a-fA-F]{4}',) * 8) # expect all 8 octets from IPv6 (assumes it's printed in the long form)
ipv6 = dut1.expect(re.compile(r' IPv6 address: ({})'.format(ipv6_r)), timeout=30)[0]
print("Connected with IPv4={} and IPv6={}".format(ipv4, ipv6))
# test IPv4
received = tcp_client(ipv4, MESSAGE)
if not received == MESSAGE:
raise
dut1.expect(MESSAGE)
# test IPv6
received = tcp_client("{}%{}".format(ipv6, INTERFACE), MESSAGE)
if not received == MESSAGE:
raise
dut1.expect(MESSAGE)
if __name__ == '__main__':
if sys.argv[2:]: # if two arguments provided:
# Usage: example_test.py <server_address> <message_to_send_to_server>
tcp_client(sys.argv[1], sys.argv[2])
else: # otherwise run standard example test as in the CI
test_examples_protocol_socket()

View File

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

View File

@@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_IPV4
bool "IPV4"
default y
config EXAMPLE_IPV6
bool "IPV6"
default n
select EXAMPLE_CONNECT_IPV6
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,159 @@
/* 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 "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#define PORT CONFIG_EXAMPLE_PORT
static const char *TAG = "example";
static void do_retransmit(const int sock)
{
int len;
char rx_buffer[128];
do {
len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0) {
ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed");
} else {
rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
// send() can return less bytes than supplied length.
// Walk-around for robust implementation.
int to_write = len;
while (to_write > 0) {
int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
if (written < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
}
to_write -= written;
}
}
} while (len > 0);
}
static void tcp_server_task(void *pvParameters)
{
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
struct sockaddr_in6 dest_addr;
if (addr_family == AF_INET) {
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(PORT);
ip_protocol = IPPROTO_IP;
} else if (addr_family == AF_INET6) {
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
ip_protocol = IPPROTO_IPV6;
}
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelete(NULL);
return;
}
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
// Note that by default IPV6 binds to both protocols, it is must be disabled
// if both protocols used at the same time (used in CI)
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
err = listen(listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
goto CLEAN_UP;
}
while (1) {
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
uint addr_len = sizeof(source_addr);
int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
// Convert ip address to string
if (source_addr.sin6_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.sin6_family == PF_INET6) {
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
do_retransmit(sock);
shutdown(sock, 0);
close(sock);
}
CLEAN_UP:
close(listen_sock);
vTaskDelete(NULL);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
#ifdef CONFIG_EXAMPLE_IPV4
xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
#endif
}

View File

@@ -0,0 +1,2 @@
CONFIG_EXAMPLE_IPV4=y
CONFIG_EXAMPLE_IPV6=y

View File

@@ -0,0 +1,10 @@
# 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)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(udp_client)

View File

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

View File

@@ -0,0 +1,81 @@
# 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 3333
```
### Python scripts
Script example_test.py could be used as a counter part to the udp-client application, ip protocol name (IPv4 or IPv6) shall be stated as argument. Example:
```
python example_test.py IPv4
```
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported;
please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`.
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
idf.py menuconfig
```
Set following parameters under Example Configuration Options:
* 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.
Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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,118 @@
# 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 __future__ import print_function
from __future__ import unicode_literals
from builtins import input
import os
import re
import netifaces
import socket
from threading import Thread, Event
import ttfw_idf
import sys
# ----------- Config ----------
PORT = 3333
INTERFACE = 'eth0'
# -------------------------------
def get_my_ip(type):
for i in netifaces.ifaddresses(INTERFACE)[type]:
return i['addr'].replace("%{}".format(INTERFACE), "")
class UdpServer:
def __init__(self, port, family_addr, persist=False):
self.port = port
self.family_addr = family_addr
self.socket = socket.socket(family_addr, socket.SOCK_DGRAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.settimeout(30.0)
self.shutdown = Event()
self.persist = persist
def __enter__(self):
try:
self.socket.bind(('', self.port))
except socket.error as e:
print("Bind failed:{}".format(e))
raise
self.server_thread = Thread(target=self.run_server)
self.server_thread.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.persist:
sock = socket.socket(self.family_addr, socket.SOCK_DGRAM)
sock.sendto(b'Stop', ('localhost', self.port))
sock.close()
self.shutdown.set()
self.server_thread.join()
self.socket.close()
def run_server(self):
while not self.shutdown.is_set():
try:
data, addr = self.socket.recvfrom(1024)
if not data:
return
data = data.decode()
print('Reply[' + addr[0] + ':' + str(addr[1]) + '] - ' + data)
reply = 'OK: ' + data
self.socket.sendto(reply.encode(), addr)
except socket.error as e:
print("Running server failed:{}".format(e))
raise
if not self.persist:
break
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_socket(env, extra_data):
"""
steps:
1. join AP
2. have the board connect to the server
3. send and receive data
"""
dut1 = env.get_dut("udp_client", "examples/protocols/sockets/udp_client", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "udp_client.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("udp_client_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("udp_client_bin_size", bin_size // 1024, dut1.TARGET)
# start test
dut1.start_app()
data = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)
print("Connected with IPv4: {}".format(data[0]))
# test IPv4
with UdpServer(PORT, socket.AF_INET):
dut1.write(get_my_ip(netifaces.AF_INET))
dut1.expect(re.compile(r"OK: Message from ESP32"))
# test IPv6
with UdpServer(PORT, socket.AF_INET6):
dut1.write(get_my_ip(netifaces.AF_INET6))
dut1.expect(re.compile(r"OK: Message from ESP32"))
if __name__ == '__main__':
if sys.argv[1:] and sys.argv[1].startswith("IPv"): # if additional arguments provided:
# Usage: example_test.py <IPv4|IPv6>
family_addr = socket.AF_INET6 if sys.argv[1] == "IPv6" else socket.AF_INET
with UdpServer(PORT, family_addr, persist=True) as s:
print(input("Press Enter stop the server..."))
else:
test_examples_protocol_socket()

View File

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

View File

@@ -0,0 +1,52 @@
menu "Example Configuration"
choice EXAMPLE_IP_MODE
prompt "IP Version"
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
help
Example can use either IPV4 or IPV6.
config EXAMPLE_IPV4
bool "IPV4"
config EXAMPLE_IPV6
bool "IPV6"
select EXAMPLE_CONNECT_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.
choice EXAMPLE_SOCKET_IP_INPUT
prompt "Socket example source"
default EXAMPLE_SOCKET_IP_INPUT_STRING
help
Selects the input source of the IP used in the example.
config EXAMPLE_SOCKET_IP_INPUT_STRING
bool "From string"
config EXAMPLE_SOCKET_IP_INPUT_STDIN
bool "From stdin"
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,132 @@
/* 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.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "addr_from_stdin.h"
#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif
#define PORT CONFIG_EXAMPLE_PORT
static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";
static void udp_client_task(void *pvParameters)
{
char rx_buffer[128];
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
struct sockaddr_in6 dest_addr = { 0 };
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
struct sockaddr_in6 dest_addr = { 0 };
ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_DGRAM, &ip_protocol, &addr_family, &dest_addr));
#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, sending to %s:%d", HOST_IP_ADDR, PORT);
while (1) {
int err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Message sent");
struct sockaddr_in source_addr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(source_addr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
// Error occurred 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, host_ip);
ESP_LOGI(TAG, "%s", rx_buffer);
if (strncmp(rx_buffer, "OK: ", 4) == 0) {
ESP_LOGI(TAG, "Received expected message, reconnecting");
break;
}
}
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(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1 @@
CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y

View File

@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(udp-multicast)

View File

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

View File

@@ -0,0 +1,69 @@
# 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
Open the project configuration menu (`idf.py menuconfig`).
Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
The "Example Configuration" menu allows you to configure the details of the example:
* 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,2 @@
idf_component_register(SRCS "udp_multicast_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,82 @@
menu "Example Configuration"
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
select EXAMPLE_CONNECT_IPV6 if IDF_TARGET_ESP32
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"
default EXAMPLE_MULTICAST_LISTEN_DEFAULT_IF
help
Multicast socket can bind to default interface, or all interfaces.
config EXAMPLE_MULTICAST_LISTEN_ALL_IF
bool "All interfaces (IPV4 only)"
depends on !EXAMPLE_IPV6_ONLY
config EXAMPLE_MULTICAST_LISTEN_DEFAULT_IF
bool "Default 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,497 @@
/* 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.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
/* The examples use simple configuration that you can set via
project configuration.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define UDP_PORT 3333
*/
#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_ALL_IF EXAMPLE_MULTICAST_LISTEN_ALL_IF
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
#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 LISTEN_ALL_IF
imreq.imr_interface.s_addr = IPADDR_ANY;
#else
esp_netif_ip_info_t ip_info = { 0 };
err = esp_netif_get_ip_info(get_example_netif(), &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 // LISTEN_ALL_IF
// 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);
// Errors in the return value have to be negative
err = -1;
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(void)
{
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(void)
{
struct sockaddr_in6 saddr = { 0 };
int netif_index;
struct in6_addr if_inaddr = { 0 };
struct ip6_addr if_ipaddr = { 0 };
struct ipv6_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 LISTEN_ALL_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 = esp_netif_get_ip6_linklocal(EXAMPLE_INTERFACE, (esp_ip6_addr_t*)&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 // LISTEN_ALL_IF
// search for netif index
netif_index = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
if(netif_index < 0) {
ESP_LOGE(V6TAG, "Failed to get netif index");
goto err;
}
// Assign the multicast source interface, via its IP
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &netif_index,sizeof(uint8_t));
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...
#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);
}
// Configure source interface
v6imreq.ipv6mr_interface = (unsigned int)netif_index;
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
&v6imreq, sizeof(struct ipv6_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) {
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;
}
if (res == 0) {
ESP_LOGE(TAG, "getaddrinfo() did not return any addresses");
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(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
xTaskCreate(&mcast_example_task, "mcast_task", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1,10 @@
# 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)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(udp_server)

View File

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

View File

@@ -0,0 +1,76 @@
# 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 example_test.py could be used as a counter part to the udp-server application,
IP address and the message to be send to the server shall be stated as arguments. Example:
```
python example_test.py 192.168.0.167 Message
```
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported;
please add `$IDF_PATH/tools/ci/python_packages` to `PYTHONPATH`.
## Hardware Required
This example can be run on any commonly available ESP32 development board.
## Configure the project
```
idf.py menuconfig
```
Set following parameters under Example Configuration Options:
* Set `IP version` of the example to be IPV4 or IPV6.
* Set `Port` number that represents remote port the example will create.
Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
## Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT 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,87 @@
# 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 __future__ import print_function
from __future__ import unicode_literals
import os
import sys
import re
import socket
import ttfw_idf
# ----------- Config ----------
PORT = 3333
INTERFACE = 'eth0'
# -------------------------------
def udp_client(address, payload):
for res in socket.getaddrinfo(address, PORT, socket.AF_UNSPEC,
socket.SOCK_DGRAM, 0, socket.AI_PASSIVE):
family_addr, socktype, proto, canonname, addr = res
try:
sock = socket.socket(family_addr, socket.SOCK_DGRAM)
except socket.error as msg:
print('Could not create socket: ' + str(msg[0]) + ': ' + msg[1])
raise
try:
sock.sendto(payload, addr)
reply, addr = sock.recvfrom(128)
if not reply:
return
print('Reply[' + addr[0] + ':' + str(addr[1]) + '] - ' + str(reply))
except socket.error as msg:
print('Error Code : ' + str(msg[0]) + ' Message: ' + msg[1])
sock.close()
raise
return reply
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_socket(env, extra_data):
MESSAGE = "Data to ESP"
"""
steps:
1. join AP
2. have the board connect to the server
3. send and receive data
"""
dut1 = env.get_dut("udp_server", "examples/protocols/sockets/udp_server", dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "udp_server.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("udp_server_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("udp_server_bin_size", bin_size // 1024, dut1.TARGET)
# start test
dut1.start_app()
ipv4 = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)[0]
ipv6_r = r':'.join((r'[0-9a-fA-F]{4}',) * 8) # expect all 8 octets from IPv6 (assumes it's printed in the long form)
ipv6 = dut1.expect(re.compile(r' IPv6 address: ({})'.format(ipv6_r)), timeout=30)[0]
print("Connected with IPv4={} and IPv6={}".format(ipv4, ipv6))
# test IPv4
received = udp_client(ipv4, MESSAGE)
if not received == MESSAGE:
raise
dut1.expect(MESSAGE)
# test IPv6
received = udp_client("{}%{}".format(ipv6, INTERFACE), MESSAGE)
if not received == MESSAGE:
raise
dut1.expect(MESSAGE)
if __name__ == '__main__':
if sys.argv[2:]: # if two arguments provided:
# Usage: example_test.py <server_address> <message_to_send_to_server>
udp_client(sys.argv[1], sys.argv[2])
else: # otherwise run standard example test as in the CI
test_examples_protocol_socket()

View File

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

View File

@@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_IPV4
bool "IPV4"
default y
config EXAMPLE_IPV6
bool "IPV6"
default n
select EXAMPLE_CONNECT_IPV6
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,137 @@
/* 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 "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#define PORT CONFIG_EXAMPLE_PORT
static const char *TAG = "example";
static void udp_server_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
struct sockaddr_in6 dest_addr;
while (1) {
if (addr_family == AF_INET) {
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(PORT);
ip_protocol = IPPROTO_IP;
} else if (addr_family == AF_INET6) {
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
ip_protocol = IPPROTO_IPV6;
}
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");
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
if (addr_family == AF_INET6) {
// Note that by default IPV6 binds to both protocols, it is must be disabled
// if both protocols used at the same time (used in CI)
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
}
#endif
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
while (1) {
ESP_LOGI(TAG, "Waiting for data");
struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(source_addr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
// Error occurred 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 (source_addr.sin6_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.sin6_family == PF_INET6) {
inet6_ntoa_r(source_addr.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 *)&source_addr, sizeof(source_addr));
if (err < 0) {
ESP_LOGE(TAG, "Error occurred 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(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
#ifdef CONFIG_EXAMPLE_IPV4
xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET6, 5, NULL);
#endif
}

View File

@@ -0,0 +1,2 @@
CONFIG_EXAMPLE_IPV4=y
CONFIG_EXAMPLE_IPV6=y