forked from espressif/esp-idf
tcp_client example: support for Linux target.
This commit is contained in:
@@ -229,6 +229,8 @@ examples/protocols/sockets/tcp_client:
|
|||||||
- if: IDF_TARGET != "esp32"
|
- if: IDF_TARGET != "esp32"
|
||||||
temporary: true
|
temporary: true
|
||||||
reason: lack of runners
|
reason: lack of runners
|
||||||
|
enable:
|
||||||
|
- if: INCLUDE_DEFAULT == 1 or IDF_TARGET == "linux"
|
||||||
|
|
||||||
examples/protocols/sockets/tcp_client_multi_net:
|
examples/protocols/sockets/tcp_client_multi_net:
|
||||||
disable:
|
disable:
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
# The following five lines of boilerplate have to be in your project's
|
# The following lines of boilerplate have to be in your project's
|
||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# (Not part of the boilerplate)
|
if(${IDF_TARGET} STREQUAL "linux")
|
||||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
|
||||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
set(COMPONENTS main)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
else()
|
||||||
|
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
|
||||||
project(tcp_client)
|
project(tcp_client)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | Linux |
|
||||||
| ----------------- | ----- | -------- | -------- | -------- | -------- |
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | ----- |
|
||||||
|
|
||||||
|
|
||||||
# TCP Client example
|
# TCP Client example
|
||||||
@@ -8,13 +8,38 @@
|
|||||||
|
|
||||||
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.
|
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
|
## Configure the project
|
||||||
|
This example can be configured to run on ESP32 and Linux target to communicate over IPv4 and IPv6.
|
||||||
|
|
||||||
|
```
|
||||||
|
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.
|
||||||
|
* For IPv6 there's an additional option for ```Interface selection```.
|
||||||
|
* Enter the name of the interface to explicitely establish communication over a specific interface.
|
||||||
|
* On selecting ```Auto``` the example will find the first interface with an IPv6 address and use it.
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
|
||||||
|
Note: please replace `192.168.0.167 3333` with desired IPV4/IPV6 address (displayed in monitor console) and port number in the following command.
|
||||||
|
|
||||||
|
|
||||||
|
## How to use example:
|
||||||
|
|
||||||
In order to create TCP server that communicates with TCP Client example, choose one of the following options.
|
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.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
@@ -24,35 +49,24 @@ nc -l 192.168.0.167 3333
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Python scripts
|
### 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:
|
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.
|
||||||
|
|
||||||
|
Note that this script is used in automated tests, as well, so the IDF test framework packages need to be imported.
|
||||||
|
Please run the following commands to configure the terminal to execute the script.
|
||||||
|
```
|
||||||
|
export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages"
|
||||||
|
python -m pip install -r $IDF_PATH/tools/ci/python_packages/ttfw_idf/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
```
|
```
|
||||||
python example_test.py IPv4
|
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
|
## 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.
|
||||||
|
This example can also run on any Linux environment.
|
||||||
|
|
||||||
## 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 and Flash
|
||||||
|
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
if(${IDF_TARGET} STREQUAL "linux")
|
||||||
|
idf_component_register(SRCS
|
||||||
|
esp_stubs/esp_stubs.c
|
||||||
|
INCLUDE_DIRS . include/stubs
|
||||||
|
REQUIRES main)
|
||||||
|
endif()
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
extern void app_main(void);
|
||||||
|
|
||||||
|
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
|
||||||
|
{
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Failed with esp_err_t: 0x%x", rc);
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Expression: %s", expression);
|
||||||
|
ESP_LOGE("ESP_ERROR_CHECK", "Functions: %s %s(%d)", function, file, line);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_event_loop_create_default(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_netif_init(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t example_connect(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t nvs_flash_init(void)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
app_main();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t esp_event_loop_create_default(void);
|
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t esp_netif_init(void);
|
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t nvs_flash_init(void);
|
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t example_connect(void);
|
@@ -1,2 +1,13 @@
|
|||||||
idf_component_register(SRCS "tcp_client.c"
|
if(${IDF_TARGET} STREQUAL "linux")
|
||||||
INCLUDE_DIRS ".")
|
set(requires esp_stubs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${CONFIG_EXAMPLE_IPV4}" STREQUAL y)
|
||||||
|
set(tcp_client_ip tcp_client_v4.c)
|
||||||
|
else()
|
||||||
|
set(tcp_client_ip tcp_client_v6.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS "tcp_client_main.c" "${tcp_client_ip}"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES ${requires})
|
||||||
|
@@ -35,6 +35,26 @@ 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_INTERFACE
|
||||||
|
prompt "Interface selection"
|
||||||
|
depends on EXAMPLE_IPV6
|
||||||
|
help
|
||||||
|
Example can use either "Auto" or "User specified".
|
||||||
|
|
||||||
|
config EXAMPLE_IFACE_AUTO
|
||||||
|
bool "Auto"
|
||||||
|
|
||||||
|
config EXAMPLE_USER_SPECIFIED_IFACE
|
||||||
|
bool "User specified interface"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_USER_SPECIFIED_IFACE_NAME
|
||||||
|
string "User specified interface name"
|
||||||
|
default "st1"
|
||||||
|
depends on EXAMPLE_USER_SPECIFIED_IFACE
|
||||||
|
help
|
||||||
|
This interface will be used for communication.
|
||||||
|
|
||||||
choice EXAMPLE_SOCKET_IP_INPUT
|
choice EXAMPLE_SOCKET_IP_INPUT
|
||||||
prompt "Socket example source"
|
prompt "Socket example source"
|
||||||
default EXAMPLE_SOCKET_IP_INPUT_STRING
|
default EXAMPLE_SOCKET_IP_INPUT_STRING
|
||||||
|
27
examples/protocols/sockets/tcp_client/main/tcp_client_main.c
Normal file
27
examples/protocols/sockets/tcp_client/main/tcp_client_main.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern void tcp_client(void);
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
tcp_client();
|
||||||
|
}
|
@@ -1,33 +1,24 @@
|
|||||||
/* BSD Socket API Example
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
Unless required by applicable law or agreed to in writing, this
|
*/
|
||||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
#include "sdkconfig.h"
|
||||||
CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*/
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/param.h>
|
#include <unistd.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include <sys/socket.h>
|
||||||
#include "freertos/task.h"
|
#include <errno.h>
|
||||||
#include "freertos/event_groups.h"
|
#include <netdb.h> // struct addrinfo
|
||||||
#include "esp_system.h"
|
#include <arpa/inet.h>
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "esp_event.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
#include "protocol_examples_common.h"
|
#include "esp_log.h"
|
||||||
|
#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
|
||||||
#include "addr_from_stdin.h"
|
#include "addr_from_stdin.h"
|
||||||
#include "lwip/err.h"
|
#endif
|
||||||
#include "lwip/sockets.h"
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CONFIG_EXAMPLE_IPV4)
|
#if defined(CONFIG_EXAMPLE_IPV4)
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
|
||||||
#elif defined(CONFIG_EXAMPLE_IPV6)
|
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
|
||||||
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
|
|
||||||
#else
|
|
||||||
#define HOST_IP_ADDR ""
|
#define HOST_IP_ADDR ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -36,7 +27,8 @@
|
|||||||
static const char *TAG = "example";
|
static const char *TAG = "example";
|
||||||
static const char *payload = "Message from ESP32 ";
|
static const char *payload = "Message from ESP32 ";
|
||||||
|
|
||||||
static void tcp_client_task(void *pvParameters)
|
|
||||||
|
void tcp_client(void)
|
||||||
{
|
{
|
||||||
char rx_buffer[128];
|
char rx_buffer[128];
|
||||||
char host_ip[] = HOST_IP_ADDR;
|
char host_ip[] = HOST_IP_ADDR;
|
||||||
@@ -46,23 +38,16 @@ static void tcp_client_task(void *pvParameters)
|
|||||||
while (1) {
|
while (1) {
|
||||||
#if defined(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);
|
inet_pton(AF_INET, host_ip, &dest_addr.sin_addr);
|
||||||
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;
|
||||||
#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)
|
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
|
||||||
struct sockaddr_storage dest_addr = { 0 };
|
struct sockaddr_storage dest_addr = { 0 };
|
||||||
ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
|
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);
|
||||||
@@ -70,7 +55,7 @@ static void tcp_client_task(void *pvParameters)
|
|||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
|
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
|
||||||
|
|
||||||
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
|
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||||
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;
|
||||||
@@ -96,8 +81,6 @@ static void tcp_client_task(void *pvParameters)
|
|||||||
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
|
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
|
||||||
ESP_LOGI(TAG, "%s", rx_buffer);
|
ESP_LOGI(TAG, "%s", rx_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sock != -1) {
|
if (sock != -1) {
|
||||||
@@ -106,20 +89,4 @@ static void tcp_client_task(void *pvParameters)
|
|||||||
close(sock);
|
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);
|
|
||||||
}
|
}
|
255
examples/protocols/sockets/tcp_client/main/tcp_client_v6.c
Normal file
255
examples/protocols/sockets/tcp_client/main/tcp_client_v6.c
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h> // struct addrinfo
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
|
||||||
|
#include "addr_from_stdin.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV6_ADDR)
|
||||||
|
#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 ";
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
// Checks for Global address, Unique Unicast(RFC4193) and link-local address.
|
||||||
|
#define ip6_addr_isglobal(ip6addr) ((((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xe0000000UL)) & htonl(0x20000000UL)) || \
|
||||||
|
(((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfc000000UL)) || \
|
||||||
|
(((ip6addr)->sin6_addr.s6_addr[0] & htonl(0xff000000UL)) & htonl(0xfe800000UL)))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief In case of Auto mode returns the interface name with a valid IPv6 address or
|
||||||
|
* In case the user has specified interface, validates and returns the interface name.
|
||||||
|
*
|
||||||
|
* @param[out] interface Name of the interface in as a string.
|
||||||
|
*
|
||||||
|
* @return 0 incase of success.
|
||||||
|
*/
|
||||||
|
static int get_src_iface(char *interface)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifap, *ifa;
|
||||||
|
char src_addr_str[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
if (getifaddrs(&ifap) == -1) {
|
||||||
|
ESP_LOGE(TAG, "getifaddrs failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
||||||
|
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
|
||||||
|
#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
|
||||||
|
if (0 == strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, ifa->ifa_name)) {
|
||||||
|
strcpy(interface, ifa->ifa_name);
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
ESP_LOGI(TAG, "Interface: %s", interface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
strcpy(interface, ifa->ifa_name);
|
||||||
|
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), src_addr_str,
|
||||||
|
sizeof(src_addr_str), NULL, 0, NI_NUMERICHOST);
|
||||||
|
|
||||||
|
struct sockaddr_in6 *src_addr = (struct sockaddr_in6 *) ifa->ifa_addr;
|
||||||
|
inet_ntop(AF_INET6, &(src_addr->sin6_addr), src_addr_str, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
if (ip6_addr_isglobal(src_addr)) {
|
||||||
|
//Return as we have the source address
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
ESP_LOGI(TAG, "Interface: %s", interface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
static esp_netif_t *get_esp_netif_from_iface(char *interface_i)
|
||||||
|
{
|
||||||
|
esp_netif_t *netif = NULL;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
char iface[10];
|
||||||
|
|
||||||
|
// Get interface details and own global ipv6 address
|
||||||
|
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
|
||||||
|
netif = esp_netif_next(netif);
|
||||||
|
ret = esp_netif_get_netif_impl_name(netif, iface);
|
||||||
|
if ((ESP_FAIL == ret) || (NULL == netif)) {
|
||||||
|
ESP_LOGE(TAG, "No interface available");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strcmp(interface_i, iface)) {
|
||||||
|
return netif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief In case of Auto mode returns the interface name with a valid IPv6 address or
|
||||||
|
* In case the user has specified interface, validates and returns the interface name.
|
||||||
|
*
|
||||||
|
* @param[out] interface Name of the interface in as a string.
|
||||||
|
*
|
||||||
|
* @return 0 incase of success.
|
||||||
|
*/
|
||||||
|
static int get_src_iface(char *interface)
|
||||||
|
{
|
||||||
|
esp_netif_t *netif = NULL;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
int ip6_addrs_count = 0;
|
||||||
|
esp_ip6_addr_t ip6[LWIP_IPV6_NUM_ADDRESSES];
|
||||||
|
|
||||||
|
// Get interface details and own global ipv6 address
|
||||||
|
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
|
||||||
|
netif = esp_netif_next(netif);
|
||||||
|
ret = esp_netif_get_netif_impl_name(netif, interface);
|
||||||
|
|
||||||
|
if ((ESP_FAIL == ret) || (NULL == netif)) {
|
||||||
|
ESP_LOGE(TAG, "No interface available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
|
||||||
|
if (!strcmp(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE_NAME, interface)) {
|
||||||
|
ESP_LOGI(TAG, "Interface: %s", interface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ip6_addrs_count = esp_netif_get_all_ip6(netif, ip6);
|
||||||
|
for (int j = 0; j < ip6_addrs_count; ++j) {
|
||||||
|
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
|
||||||
|
|
||||||
|
if ((ESP_IP6_ADDR_IS_GLOBAL == ipv6_type) ||
|
||||||
|
(ESP_IP6_ADDR_IS_UNIQUE_LOCAL == ipv6_type) ||
|
||||||
|
(ESP_IP6_ADDR_IS_LINK_LOCAL == ipv6_type)) {
|
||||||
|
// Break as we have the source address
|
||||||
|
ESP_LOGI(TAG, "Interface: %s", interface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // #if defined(CONFIG_EXAMPLE_USER_SPECIFIED_IFACE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif // #if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
|
||||||
|
|
||||||
|
void tcp_client(void)
|
||||||
|
{
|
||||||
|
char rx_buffer[128];
|
||||||
|
char host_ip[] = HOST_IP_ADDR;
|
||||||
|
int addr_family = 0;
|
||||||
|
int ip_protocol = 0;
|
||||||
|
char interface[10];
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
struct ifreq ifr;
|
||||||
|
#else
|
||||||
|
esp_netif_t *netif = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV6)
|
||||||
|
struct sockaddr_in6 dest_addr = { 0 };
|
||||||
|
inet_pton(AF_INET6, host_ip, &dest_addr.sin6_addr);
|
||||||
|
dest_addr.sin6_family = AF_INET6;
|
||||||
|
dest_addr.sin6_port = htons(PORT);
|
||||||
|
addr_family = AF_INET6;
|
||||||
|
ip_protocol = IPPROTO_TCP;
|
||||||
|
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
|
||||||
|
struct sockaddr_storage 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);
|
||||||
|
|
||||||
|
if (0 != get_src_iface(interface)) {
|
||||||
|
ESP_LOGE(TAG, "Interface: Unavailable\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||||
|
memset (&ifr, 0, sizeof(ifr));
|
||||||
|
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
|
||||||
|
if (ioctl (sock, SIOCGIFINDEX, &ifr) < 0) {
|
||||||
|
ESP_LOGE(TAG, "ioctl() failed to find interface ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV6)
|
||||||
|
dest_addr.sin6_scope_id = ifr.ifr_ifindex;
|
||||||
|
ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
if (NULL == (netif = get_esp_netif_from_iface(interface))) {
|
||||||
|
ESP_LOGE(TAG, "Failed to find interface ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV6)
|
||||||
|
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(netif);
|
||||||
|
ESP_LOGI(TAG, "Interface index: %d\n", dest_addr.sin6_scope_id);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock != -1) {
|
||||||
|
ESP_LOGE(TAG, "Shutting down socket and restarting...");
|
||||||
|
shutdown(sock, 0);
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
examples/protocols/sockets/tcp_client/sdkconfig.defaults
Normal file
4
examples/protocols/sockets/tcp_client/sdkconfig.defaults
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||||
|
#
|
||||||
|
# CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER is not set
|
Reference in New Issue
Block a user