forked from espressif/esp-idf
Merge branch 'bugfix/ipv6_examples' into 'master'
IPv6 related updates: esp-netif, common-connect, socket-examples See merge request espressif/esp-idf!7500
This commit is contained in:
@@ -99,6 +99,24 @@ typedef struct _ip_addr {
|
|||||||
} u_addr;
|
} u_addr;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
} esp_ip_addr_t;
|
} esp_ip_addr_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ESP_IP6_ADDR_IS_UNKNOWN,
|
||||||
|
ESP_IP6_ADDR_IS_GLOBAL,
|
||||||
|
ESP_IP6_ADDR_IS_LINK_LOCAL,
|
||||||
|
ESP_IP6_ADDR_IS_SITE_LOCAL,
|
||||||
|
ESP_IP6_ADDR_IS_UNIQUE_LOCAL,
|
||||||
|
ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6
|
||||||
|
} esp_ip6_addr_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the IPv6 address type
|
||||||
|
*
|
||||||
|
* @param[in] ip6_addr IPv6 type
|
||||||
|
*
|
||||||
|
* @return IPv6 type in form of enum esp_ip6_addr_type_t
|
||||||
|
*/
|
||||||
|
esp_ip6_addr_type_t esp_netif_ip6_get_addr_type(esp_ip6_addr_t* ip6_addr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@@ -120,6 +120,7 @@ typedef struct {
|
|||||||
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
|
int if_index; /*!< Interface index for which the event is received (left for legacy compilation) */
|
||||||
esp_netif_t *esp_netif; /*!< Pointer to corresponding esp-netif object */
|
esp_netif_t *esp_netif; /*!< Pointer to corresponding esp-netif object */
|
||||||
esp_netif_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
|
esp_netif_ip6_info_t ip6_info; /*!< IPv6 address of the interface */
|
||||||
|
int ip_index; /*!< IPv6 address index */
|
||||||
} ip_event_got_ip6_t;
|
} ip_event_got_ip6_t;
|
||||||
|
|
||||||
/** Event structure for IP_EVENT_AP_STAIPASSIGNED event */
|
/** Event structure for IP_EVENT_AP_STAIPASSIGNED event */
|
||||||
|
@@ -1370,7 +1370,26 @@ esp_err_t esp_netif_get_dns_info(esp_netif_t *esp_netif, esp_netif_dns_type_t ty
|
|||||||
return esp_netif_lwip_ipc_call(esp_netif_get_dns_info_api, esp_netif, (void *)&dns_param);
|
return esp_netif_lwip_ipc_call(esp_netif_get_dns_info_api, esp_netif, (void *)&dns_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void esp_netif_nd6_cb(struct netif *p_netif, uint8_t ip_idex)
|
esp_ip6_addr_type_t esp_netif_ip6_get_addr_type(esp_ip6_addr_t* ip6_addr)
|
||||||
|
{
|
||||||
|
ip6_addr_t* lwip_ip6_info = (ip6_addr_t*)ip6_addr;
|
||||||
|
|
||||||
|
if (ip6_addr_isglobal(lwip_ip6_info)) {
|
||||||
|
return ESP_IP6_ADDR_IS_GLOBAL;
|
||||||
|
} else if (ip6_addr_islinklocal(lwip_ip6_info)) {
|
||||||
|
return ESP_IP6_ADDR_IS_LINK_LOCAL;
|
||||||
|
} else if (ip6_addr_issitelocal(lwip_ip6_info)) {
|
||||||
|
return ESP_IP6_ADDR_IS_SITE_LOCAL;
|
||||||
|
} else if (ip6_addr_isuniquelocal(lwip_ip6_info)) {
|
||||||
|
return ESP_IP6_ADDR_IS_UNIQUE_LOCAL;
|
||||||
|
} else if (ip6_addr_isipv4mappedipv6(lwip_ip6_info)) {
|
||||||
|
return ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6;
|
||||||
|
}
|
||||||
|
return ESP_IP6_ADDR_IS_UNKNOWN;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void esp_netif_nd6_cb(struct netif *p_netif, uint8_t ip_index)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "%s lwip-netif:%p", __func__, p_netif);
|
ESP_LOGD(TAG, "%s lwip-netif:%p", __func__, p_netif);
|
||||||
if (!p_netif) {
|
if (!p_netif) {
|
||||||
@@ -1381,9 +1400,9 @@ static void esp_netif_nd6_cb(struct netif *p_netif, uint8_t ip_idex)
|
|||||||
esp_netif_ip6_info_t ip6_info;
|
esp_netif_ip6_info_t ip6_info;
|
||||||
ip6_addr_t lwip_ip6_info;
|
ip6_addr_t lwip_ip6_info;
|
||||||
//notify event
|
//notify event
|
||||||
ip_event_got_ip6_t evt = { .esp_netif = p_netif->state, .if_index = -1 };
|
ip_event_got_ip6_t evt = { .esp_netif = p_netif->state, .if_index = -1, .ip_index = ip_index };
|
||||||
|
|
||||||
ip6_addr_set(&lwip_ip6_info, ip_2_ip6(&p_netif->ip6_addr[ip_idex]));
|
ip6_addr_set(&lwip_ip6_info, ip_2_ip6(&p_netif->ip6_addr[ip_index]));
|
||||||
#if LWIP_IPV6_SCOPES
|
#if LWIP_IPV6_SCOPES
|
||||||
memcpy(&ip6_info.ip, &lwip_ip6_info, sizeof(esp_ip6_addr_t));
|
memcpy(&ip6_info.ip, &lwip_ip6_info, sizeof(esp_ip6_addr_t));
|
||||||
#else
|
#else
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
idf_component_register(SRCS "connect.c" "stdin_out.c"
|
idf_component_register(SRCS "connect.c" "stdin_out.c" "addr_from_stdin.c"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_REQUIRES esp_netif
|
PRIV_REQUIRES esp_netif
|
||||||
)
|
)
|
||||||
|
@@ -175,9 +175,44 @@ menu "Example Connection Configuration"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
config EXAMPLE_CONNECT_IPV6
|
config EXAMPLE_CONNECT_IPV6
|
||||||
bool "Obtain IPv6 link-local address"
|
bool "Obtain IPv6 address"
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
By default, examples will wait until IPv4 and IPv6 addresses are obtained.
|
By default, examples will wait until IPv4 and IPv6 local link addresses are obtained.
|
||||||
Disable this option if the network does not support IPv6.
|
Disable this option if the network does not support IPv6.
|
||||||
|
Choose the preferred IPv6 address type if the connection code should wait until other than
|
||||||
|
the local link address gets assigned.
|
||||||
|
|
||||||
|
if EXAMPLE_CONNECT_IPV6
|
||||||
|
choice EXAMPLE_CONNECT_PREFERRED_IPV6
|
||||||
|
prompt "Preferred IPv6 Type"
|
||||||
|
default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
|
||||||
|
help
|
||||||
|
Select which kind of IPv6 address the connect logic waits for.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
|
||||||
|
bool "Local Link Address"
|
||||||
|
help
|
||||||
|
Blocks until Local link address assigned.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL
|
||||||
|
bool "Global Address"
|
||||||
|
help
|
||||||
|
Blocks until Global address assigned.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL
|
||||||
|
bool "Site Local Address"
|
||||||
|
help
|
||||||
|
Blocks until Site link address assigned.
|
||||||
|
|
||||||
|
config EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL
|
||||||
|
bool "Unique Local Link Address"
|
||||||
|
help
|
||||||
|
Blocks until Unique local address assigned.
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@@ -0,0 +1,65 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
|
||||||
|
#include "lwip/sockets.h"
|
||||||
|
#include <lwip/netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define HOST_IP_SIZE 128
|
||||||
|
|
||||||
|
esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_in6 *dest_addr)
|
||||||
|
{
|
||||||
|
char host_ip[HOST_IP_SIZE];
|
||||||
|
int len;
|
||||||
|
static bool already_init = false;
|
||||||
|
|
||||||
|
// this function could be called multiple times -> make sure UART init runs only once
|
||||||
|
if (!already_init) {
|
||||||
|
example_configure_stdin_stdout();
|
||||||
|
already_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore empty or LF only string (could receive from DUT class)
|
||||||
|
do {
|
||||||
|
fgets(host_ip, HOST_IP_SIZE, stdin);
|
||||||
|
len = strlen(host_ip);
|
||||||
|
} while (len<=1 && host_ip[0] == '\n');
|
||||||
|
host_ip[len - 1] = '\0';
|
||||||
|
|
||||||
|
struct addrinfo hints, *addr_list, *cur;
|
||||||
|
memset( &hints, 0, sizeof( hints ) );
|
||||||
|
|
||||||
|
// run getaddrinfo() to decide on the IP protocol
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = sock_type;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
for( cur = addr_list; cur != NULL; cur = cur->ai_next ) {
|
||||||
|
memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr));
|
||||||
|
if (cur->ai_family == AF_INET) {
|
||||||
|
*ip_protocol = IPPROTO_IP;
|
||||||
|
*addr_family = AF_INET;
|
||||||
|
// add port number and return on first IPv4 match
|
||||||
|
((struct sockaddr_in*)dest_addr)->sin_port = htons(port);
|
||||||
|
freeaddrinfo( addr_list );
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
} else if (cur->ai_family == AF_INET6) {
|
||||||
|
*ip_protocol = IPPROTO_IPV6;
|
||||||
|
*addr_family = AF_INET6;
|
||||||
|
// add port and interface number and return on first IPv6 match
|
||||||
|
dest_addr->sin6_port = htons(port);
|
||||||
|
dest_addr->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
|
||||||
|
freeaddrinfo( addr_list );
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no match found
|
||||||
|
freeaddrinfo( addr_list );
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
@@ -30,6 +30,17 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||||
#define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT)
|
#define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT)
|
||||||
|
|
||||||
|
#if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK)
|
||||||
|
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||||
|
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL)
|
||||||
|
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_GLOBAL
|
||||||
|
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL)
|
||||||
|
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_SITE_LOCAL
|
||||||
|
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL)
|
||||||
|
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_UNIQUE_LOCAL
|
||||||
|
#endif // if-elif CONFIG_EXAMPLE_CONNECT_IPV6_PREF_...
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define CONNECTED_BITS (GOT_IPV4_BIT)
|
#define CONNECTED_BITS (GOT_IPV4_BIT)
|
||||||
#endif
|
#endif
|
||||||
@@ -41,6 +52,16 @@ static esp_netif_t *s_example_esp_netif = NULL;
|
|||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||||
static esp_ip6_addr_t s_ipv6_addr;
|
static esp_ip6_addr_t s_ipv6_addr;
|
||||||
|
|
||||||
|
/* types of ipv6 addresses to be displayed on ipv6 events */
|
||||||
|
static const char *s_ipv6_addr_types[] = {
|
||||||
|
"ESP_IP6_ADDR_IS_UNKNOWN",
|
||||||
|
"ESP_IP6_ADDR_IS_GLOBAL",
|
||||||
|
"ESP_IP6_ADDR_IS_LINK_LOCAL",
|
||||||
|
"ESP_IP6_ADDR_IS_SITE_LOCAL",
|
||||||
|
"ESP_IP6_ADDR_IS_UNIQUE_LOCAL",
|
||||||
|
"ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6"
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char *TAG = "example_connect";
|
static const char *TAG = "example_connect";
|
||||||
@@ -70,9 +91,12 @@ static void on_got_ipv6(void *arg, esp_event_base_t event_base,
|
|||||||
ESP_LOGD(TAG, "Got IPv6 from another netif: ignored");
|
ESP_LOGD(TAG, "Got IPv6 from another netif: ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Got IPv6 event!");
|
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
|
||||||
memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr));
|
ESP_LOGI(TAG, "Got IPv6 address: " IPV6STR ", type: %s", IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]);
|
||||||
xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT);
|
if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) {
|
||||||
|
memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr));
|
||||||
|
xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
|
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
/* Common utilities for socket address input interface:
|
||||||
|
The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured).
|
||||||
|
This option is typically used in the CI, but could be enabled in the project configuration.
|
||||||
|
In that case this component is used to receive a string that is evaluated and processed to output
|
||||||
|
socket structures to open a connectio
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lwip/sys.h"
|
||||||
|
#include <lwip/netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read and evaluate IP address from stdin
|
||||||
|
*
|
||||||
|
* This API reads stdin and parses the input address using getaddrinfo()
|
||||||
|
* to fill in struct sockaddr_in6 (for both IPv4 and IPv6) used to open
|
||||||
|
* a socket. IP protocol is guessed from the IP address string.
|
||||||
|
*
|
||||||
|
* @param[in] port port number of expected connection
|
||||||
|
* @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM
|
||||||
|
* @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6
|
||||||
|
* @param[out] addr_family resultant address family: AF_INET or AF_INET6
|
||||||
|
* @param[out] dest_addr sockaddr_in6 structure (for both IPv4 and IPv6)
|
||||||
|
* @return ESP_OK on success, ESP_FAIL otherwise
|
||||||
|
*/
|
||||||
|
esp_err_t get_addr_from_stdin(int port, int sock_type,
|
||||||
|
int *ip_protocol,
|
||||||
|
int *addr_family,
|
||||||
|
struct sockaddr_in6 *dest_addr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@@ -63,15 +63,36 @@ nc -l 192.168.0.167 -p 3333
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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;
|
PORT = 3333
|
||||||
IP_VERSION = 'IPv4'
|
INTERFACE = 'en0'
|
||||||
IPV4 = '192.168.0.167'
|
|
||||||
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
## Hardware Required
|
||||||
|
|
||||||
This example can be run on any commonly available ESP32 development board.
|
This example can be run on any commonly available ESP32 development board.
|
||||||
|
@@ -1,52 +0,0 @@
|
|||||||
# 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()
|
|
@@ -1,54 +0,0 @@
|
|||||||
# 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()
|
|
@@ -1,47 +0,0 @@
|
|||||||
# 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:
|
|
||||||
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()
|
|
@@ -1,52 +0,0 @@
|
|||||||
# 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()
|
|
@@ -17,16 +17,17 @@ In addition to those tools, simple Python scripts can be found under sockets/scr
|
|||||||
|
|
||||||
### TCP server using netcat
|
### TCP server using netcat
|
||||||
```
|
```
|
||||||
nc -l 192.168.0.167 -p 3333
|
nc -l 192.168.0.167 3333
|
||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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:
|
||||||
|
|
||||||
```
|
```
|
||||||
IP_VERSION = 'IPv4'
|
python example_test.py IPv4
|
||||||
PORT = 3333;
|
|
||||||
```
|
```
|
||||||
|
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
|
## Hardware Required
|
||||||
|
|
||||||
|
125
examples/protocols/sockets/tcp_client/example_test.py
Normal file
125
examples/protocols/sockets/tcp_client/example_test.py
Normal 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()
|
@@ -2,6 +2,7 @@ menu "Example Configuration"
|
|||||||
|
|
||||||
choice EXAMPLE_IP_MODE
|
choice EXAMPLE_IP_MODE
|
||||||
prompt "IP Version"
|
prompt "IP Version"
|
||||||
|
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
|
||||||
help
|
help
|
||||||
Example can use either IPV4 or IPV6.
|
Example can use either IPV4 or IPV6.
|
||||||
|
|
||||||
@@ -34,4 +35,17 @@ menu "Example Configuration"
|
|||||||
help
|
help
|
||||||
The remote port to which the client example will connect to.
|
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
|
endmenu
|
||||||
|
@@ -18,17 +18,17 @@
|
|||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
#include "protocol_examples_common.h"
|
#include "protocol_examples_common.h"
|
||||||
|
#include "addr_from_stdin.h"
|
||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
#include "lwip/sys.h"
|
|
||||||
#include <lwip/netdb.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
#if defined(CONFIG_EXAMPLE_IPV4)
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
||||||
#else
|
#elif defined(CONFIG_EXAMPLE_IPV6)
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
|
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
|
||||||
|
#else
|
||||||
|
#define HOST_IP_ADDR ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PORT CONFIG_EXAMPLE_PORT
|
#define PORT CONFIG_EXAMPLE_PORT
|
||||||
@@ -39,38 +39,38 @@ static const char *payload = "Message from ESP32 ";
|
|||||||
static void tcp_client_task(void *pvParameters)
|
static void tcp_client_task(void *pvParameters)
|
||||||
{
|
{
|
||||||
char rx_buffer[128];
|
char rx_buffer[128];
|
||||||
char addr_str[128];
|
char host_ip[] = HOST_IP_ADDR;
|
||||||
int addr_family;
|
int addr_family = 0;
|
||||||
int ip_protocol;
|
int ip_protocol = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV4)
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
|
||||||
struct sockaddr_in dest_addr;
|
struct sockaddr_in dest_addr;
|
||||||
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
|
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
|
||||||
dest_addr.sin_family = AF_INET;
|
dest_addr.sin_family = AF_INET;
|
||||||
dest_addr.sin_port = htons(PORT);
|
dest_addr.sin_port = htons(PORT);
|
||||||
addr_family = AF_INET;
|
addr_family = AF_INET;
|
||||||
ip_protocol = IPPROTO_IP;
|
ip_protocol = IPPROTO_IP;
|
||||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
#elif defined(CONFIG_EXAMPLE_IPV6)
|
||||||
#else // IPV6
|
struct sockaddr_in6 dest_addr = { 0 };
|
||||||
struct sockaddr_in6 dest_addr;
|
inet6_aton(host_ip, &dest_addr.sin6_addr);
|
||||||
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
|
|
||||||
dest_addr.sin6_family = AF_INET6;
|
dest_addr.sin6_family = AF_INET6;
|
||||||
dest_addr.sin6_port = htons(PORT);
|
dest_addr.sin6_port = htons(PORT);
|
||||||
|
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
|
||||||
addr_family = AF_INET6;
|
addr_family = AF_INET6;
|
||||||
ip_protocol = IPPROTO_IPV6;
|
ip_protocol = IPPROTO_IPV6;
|
||||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
#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
|
#endif
|
||||||
|
|
||||||
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
|
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", HOST_IP_ADDR, PORT);
|
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
|
||||||
|
|
||||||
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
|
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
|
||||||
break;
|
break;
|
||||||
@@ -93,7 +93,7 @@ static void tcp_client_task(void *pvParameters)
|
|||||||
// Data received
|
// Data received
|
||||||
else {
|
else {
|
||||||
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
|
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, "Received %d bytes from %s:", len, host_ip);
|
||||||
ESP_LOGI(TAG, "%s", rx_buffer);
|
ESP_LOGI(TAG, "%s", rx_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
examples/protocols/sockets/tcp_client/sdkconfig.ci
Normal file
1
examples/protocols/sockets/tcp_client/sdkconfig.ci
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y
|
@@ -21,14 +21,14 @@ nc 192.168.0.167 3333
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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:
|
||||||
|
|
||||||
```
|
```
|
||||||
PORT = 3333;
|
python example_test.py 192.168.0.167 Message
|
||||||
IP_VERSION = 'IPv4'
|
|
||||||
IPV4 = '192.168.0.167'
|
|
||||||
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
|
|
||||||
```
|
```
|
||||||
|
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
|
## Hardware Required
|
||||||
|
|
||||||
|
88
examples/protocols/sockets/tcp_server/example_test.py
Normal file
88
examples/protocols/sockets/tcp_server/example_test.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# 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 = dut1.expect(re.compile(r" IPv6 address: ([0-9A-Fa-f\:]+)"), 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()
|
@@ -1,18 +1,13 @@
|
|||||||
menu "Example Configuration"
|
menu "Example Configuration"
|
||||||
|
|
||||||
choice EXAMPLE_IP_MODE
|
config EXAMPLE_IPV4
|
||||||
prompt "IP Version"
|
bool "IPV4"
|
||||||
help
|
default y
|
||||||
Example can use either IPV4 or IPV6.
|
|
||||||
|
|
||||||
config EXAMPLE_IPV4
|
config EXAMPLE_IPV6
|
||||||
bool "IPV4"
|
bool "IPV6"
|
||||||
|
default n
|
||||||
config EXAMPLE_IPV6
|
select EXAMPLE_CONNECT_IPV6
|
||||||
bool "IPV6"
|
|
||||||
select EXAMPLE_CONNECT_IPV6
|
|
||||||
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
config EXAMPLE_PORT
|
config EXAMPLE_PORT
|
||||||
int "Port"
|
int "Port"
|
||||||
|
@@ -60,27 +60,22 @@ static void do_retransmit(const int sock)
|
|||||||
static void tcp_server_task(void *pvParameters)
|
static void tcp_server_task(void *pvParameters)
|
||||||
{
|
{
|
||||||
char addr_str[128];
|
char addr_str[128];
|
||||||
int addr_family;
|
int addr_family = (int)pvParameters;
|
||||||
int ip_protocol;
|
int ip_protocol = 0;
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
|
||||||
struct sockaddr_in dest_addr;
|
|
||||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
dest_addr.sin_family = AF_INET;
|
|
||||||
dest_addr.sin_port = htons(PORT);
|
|
||||||
addr_family = AF_INET;
|
|
||||||
ip_protocol = IPPROTO_IP;
|
|
||||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
|
||||||
#else // IPV6
|
|
||||||
struct sockaddr_in6 dest_addr;
|
struct sockaddr_in6 dest_addr;
|
||||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
|
||||||
dest_addr.sin6_family = AF_INET6;
|
if (addr_family == AF_INET) {
|
||||||
dest_addr.sin6_port = htons(PORT);
|
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
|
||||||
addr_family = AF_INET6;
|
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
ip_protocol = IPPROTO_IPV6;
|
dest_addr_ip4->sin_family = AF_INET;
|
||||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
dest_addr_ip4->sin_port = htons(PORT);
|
||||||
#endif
|
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);
|
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
|
||||||
if (listen_sock < 0) {
|
if (listen_sock < 0) {
|
||||||
@@ -88,11 +83,20 @@ static void tcp_server_task(void *pvParameters)
|
|||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
return;
|
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");
|
ESP_LOGI(TAG, "Socket created");
|
||||||
|
|
||||||
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
|
ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
|
||||||
goto CLEAN_UP;
|
goto CLEAN_UP;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
|
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
|
||||||
@@ -146,5 +150,10 @@ void app_main(void)
|
|||||||
*/
|
*/
|
||||||
ESP_ERROR_CHECK(example_connect());
|
ESP_ERROR_CHECK(example_connect());
|
||||||
|
|
||||||
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
|
#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
|
||||||
}
|
}
|
||||||
|
2
examples/protocols/sockets/tcp_server/sdkconfig.ci
Normal file
2
examples/protocols/sockets/tcp_server/sdkconfig.ci
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_EXAMPLE_IPV4=y
|
||||||
|
CONFIG_EXAMPLE_IPV6=y
|
@@ -27,16 +27,18 @@ echo "Hello from PC" | nc -w1 -u 192.168.0.167 3333
|
|||||||
|
|
||||||
### UDP server using netcat
|
### UDP server using netcat
|
||||||
```
|
```
|
||||||
nc -u -l 192.168.0.167 -p 3333
|
nc -u -l 192.168.0.167 3333
|
||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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:
|
||||||
|
|
||||||
```
|
```
|
||||||
IP_VERSION = 'IPv4'
|
python example_test.py IPv4
|
||||||
PORT = 3333;
|
|
||||||
```
|
```
|
||||||
|
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
|
## Hardware Required
|
||||||
|
|
||||||
|
118
examples/protocols/sockets/udp_client/example_test.py
Normal file
118
examples/protocols/sockets/udp_client/example_test.py
Normal 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()
|
@@ -2,6 +2,7 @@ menu "Example Configuration"
|
|||||||
|
|
||||||
choice EXAMPLE_IP_MODE
|
choice EXAMPLE_IP_MODE
|
||||||
prompt "IP Version"
|
prompt "IP Version"
|
||||||
|
depends on EXAMPLE_SOCKET_IP_INPUT_STRING
|
||||||
help
|
help
|
||||||
Example can use either IPV4 or IPV6.
|
Example can use either IPV4 or IPV6.
|
||||||
|
|
||||||
@@ -35,4 +36,17 @@ menu "Example Configuration"
|
|||||||
help
|
help
|
||||||
The remote port to which the client example will send data.
|
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
|
endmenu
|
||||||
|
@@ -23,12 +23,14 @@
|
|||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
#include "lwip/sys.h"
|
#include "lwip/sys.h"
|
||||||
#include <lwip/netdb.h>
|
#include <lwip/netdb.h>
|
||||||
|
#include "addr_from_stdin.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV4)
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
||||||
#else
|
#elif defined(CONFIG_EXAMPLE_IPV6)
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
|
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
|
||||||
|
#else
|
||||||
|
#define HOST_IP_ADDR ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PORT CONFIG_EXAMPLE_PORT
|
#define PORT CONFIG_EXAMPLE_PORT
|
||||||
@@ -40,13 +42,13 @@ static const char *payload = "Message from ESP32 ";
|
|||||||
static void udp_client_task(void *pvParameters)
|
static void udp_client_task(void *pvParameters)
|
||||||
{
|
{
|
||||||
char rx_buffer[128];
|
char rx_buffer[128];
|
||||||
char addr_str[128];
|
char host_ip[] = HOST_IP_ADDR;
|
||||||
int addr_family;
|
int addr_family = 0;
|
||||||
int ip_protocol;
|
int ip_protocol = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
#if defined(CONFIG_EXAMPLE_IPV4)
|
||||||
struct sockaddr_in dest_addr;
|
struct sockaddr_in dest_addr;
|
||||||
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
|
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
|
||||||
dest_addr.sin_family = AF_INET;
|
dest_addr.sin_family = AF_INET;
|
||||||
@@ -54,14 +56,18 @@ static void udp_client_task(void *pvParameters)
|
|||||||
addr_family = AF_INET;
|
addr_family = AF_INET;
|
||||||
ip_protocol = IPPROTO_IP;
|
ip_protocol = IPPROTO_IP;
|
||||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
||||||
#else // IPV6
|
#elif defined(CONFIG_EXAMPLE_IPV6)
|
||||||
struct sockaddr_in6 dest_addr;
|
struct sockaddr_in6 dest_addr = { 0 };
|
||||||
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
|
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
|
||||||
dest_addr.sin6_family = AF_INET6;
|
dest_addr.sin6_family = AF_INET6;
|
||||||
dest_addr.sin6_port = htons(PORT);
|
dest_addr.sin6_port = htons(PORT);
|
||||||
|
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
|
||||||
addr_family = AF_INET6;
|
addr_family = AF_INET6;
|
||||||
ip_protocol = IPPROTO_IPV6;
|
ip_protocol = IPPROTO_IPV6;
|
||||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||||
|
#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
|
#endif
|
||||||
|
|
||||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
|
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
|
||||||
@@ -92,8 +98,12 @@ static void udp_client_task(void *pvParameters)
|
|||||||
// Data received
|
// Data received
|
||||||
else {
|
else {
|
||||||
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
|
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, "Received %d bytes from %s:", len, host_ip);
|
||||||
ESP_LOGI(TAG, "%s", rx_buffer);
|
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);
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
1
examples/protocols/sockets/udp_client/sdkconfig.ci
Normal file
1
examples/protocols/sockets/udp_client/sdkconfig.ci
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y
|
@@ -31,14 +31,14 @@ nc -u 192.168.0.167 3333
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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:
|
||||||
|
|
||||||
```
|
```
|
||||||
PORT = 3333;
|
python example_test.py 192.168.0.167 Message
|
||||||
IP_VERSION = 'IPv4'
|
|
||||||
IPV4 = '192.168.0.167'
|
|
||||||
IPV6 = 'FE80::32AE:A4FF:FE80:5288'
|
|
||||||
```
|
```
|
||||||
|
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
|
## Hardware Required
|
||||||
|
|
||||||
|
86
examples/protocols/sockets/udp_server/example_test.py
Normal file
86
examples/protocols/sockets/udp_server/example_test.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# 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 = dut1.expect(re.compile(r" IPv6 address: ([0-9A-Fa-f\:]+)"), 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()
|
@@ -1,18 +1,13 @@
|
|||||||
menu "Example Configuration"
|
menu "Example Configuration"
|
||||||
|
|
||||||
choice EXAMPLE_IP_MODE
|
config EXAMPLE_IPV4
|
||||||
prompt "IP Version"
|
bool "IPV4"
|
||||||
help
|
default y
|
||||||
Example can use either IPV4 or IPV6.
|
|
||||||
|
|
||||||
config EXAMPLE_IPV4
|
config EXAMPLE_IPV6
|
||||||
bool "IPV4"
|
bool "IPV6"
|
||||||
|
default n
|
||||||
config EXAMPLE_IPV6
|
select EXAMPLE_CONNECT_IPV6
|
||||||
bool "IPV6"
|
|
||||||
select EXAMPLE_CONNECT_IPV6
|
|
||||||
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
config EXAMPLE_PORT
|
config EXAMPLE_PORT
|
||||||
int "Port"
|
int "Port"
|
||||||
|
@@ -31,28 +31,24 @@ static void udp_server_task(void *pvParameters)
|
|||||||
{
|
{
|
||||||
char rx_buffer[128];
|
char rx_buffer[128];
|
||||||
char addr_str[128];
|
char addr_str[128];
|
||||||
int addr_family;
|
int addr_family = (int)pvParameters;
|
||||||
int ip_protocol;
|
int ip_protocol = 0;
|
||||||
|
struct sockaddr_in6 dest_addr;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_IPV4
|
if (addr_family == AF_INET) {
|
||||||
struct sockaddr_in dest_addr;
|
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
|
||||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
dest_addr.sin_family = AF_INET;
|
dest_addr_ip4->sin_family = AF_INET;
|
||||||
dest_addr.sin_port = htons(PORT);
|
dest_addr_ip4->sin_port = htons(PORT);
|
||||||
addr_family = AF_INET;
|
ip_protocol = IPPROTO_IP;
|
||||||
ip_protocol = IPPROTO_IP;
|
} else if (addr_family == AF_INET6) {
|
||||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
||||||
#else // IPV6
|
dest_addr.sin6_family = AF_INET6;
|
||||||
struct sockaddr_in6 dest_addr;
|
dest_addr.sin6_port = htons(PORT);
|
||||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
|
ip_protocol = IPPROTO_IPV6;
|
||||||
dest_addr.sin6_family = AF_INET6;
|
}
|
||||||
dest_addr.sin6_port = htons(PORT);
|
|
||||||
addr_family = AF_INET6;
|
|
||||||
ip_protocol = IPPROTO_IPV6;
|
|
||||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
|
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
@@ -61,6 +57,16 @@ static void udp_server_task(void *pvParameters)
|
|||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Socket created");
|
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));
|
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
@@ -121,5 +127,11 @@ void app_main(void)
|
|||||||
*/
|
*/
|
||||||
ESP_ERROR_CHECK(example_connect());
|
ESP_ERROR_CHECK(example_connect());
|
||||||
|
|
||||||
xTaskCreate(udp_server_task, "udp_server", 4096, NULL, 5, NULL);
|
#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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
examples/protocols/sockets/udp_server/sdkconfig.ci
Normal file
2
examples/protocols/sockets/udp_server/sdkconfig.ci
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_EXAMPLE_IPV4=y
|
||||||
|
CONFIG_EXAMPLE_IPV6=y
|
@@ -3,7 +3,7 @@
|
|||||||
# Examples shouldn't include rom headers directly
|
# Examples shouldn't include rom headers directly
|
||||||
|
|
||||||
output=$(find ${IDF_PATH}/examples -name "*.[chS]" -o -name "*.cpp" -not -path "**/build/**")
|
output=$(find ${IDF_PATH}/examples -name "*.[chS]" -o -name "*.cpp" -not -path "**/build/**")
|
||||||
files=$(grep ".*include.*rom.*h" ${output} | cut -d ":" -f 1)
|
files=$(egrep ".*include.*\<rom\>.*h" ${output} | cut -d ":" -f 1)
|
||||||
found_rom=0
|
found_rom=0
|
||||||
for file in ${files}
|
for file in ${files}
|
||||||
do
|
do
|
||||||
|
Reference in New Issue
Block a user