From 22bef90bd3eb771a4b7c129d3fee62cffbdb5ca6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 21 Nov 2018 00:40:26 +0800 Subject: [PATCH] examples: add component for protocol examples network functionality --- .../protocol_examples_common/CMakeLists.txt | 5 + .../Kconfig.projbuild | 153 +++++++++ .../protocol_examples_common/component.mk | 0 .../protocol_examples_common/connect.c | 291 ++++++++++++++++++ .../include/protocol_examples_common.h | 59 ++++ .../protocol_examples_common/stdin_out.c | 30 ++ examples/protocols/README.md | 20 ++ 7 files changed, 558 insertions(+) create mode 100644 examples/common_components/protocol_examples_common/CMakeLists.txt create mode 100644 examples/common_components/protocol_examples_common/Kconfig.projbuild create mode 100644 examples/common_components/protocol_examples_common/component.mk create mode 100644 examples/common_components/protocol_examples_common/connect.c create mode 100644 examples/common_components/protocol_examples_common/include/protocol_examples_common.h create mode 100644 examples/common_components/protocol_examples_common/stdin_out.c diff --git a/examples/common_components/protocol_examples_common/CMakeLists.txt b/examples/common_components/protocol_examples_common/CMakeLists.txt new file mode 100644 index 000000000..784909da0 --- /dev/null +++ b/examples/common_components/protocol_examples_common/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_SRCS "connect.c" + "stdin_out.c") +set(COMPONENT_ADD_INCLUDEDIRS "include") + +register_component() diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild new file mode 100644 index 000000000..fe095582b --- /dev/null +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -0,0 +1,153 @@ +menu "Example Connection Configuration" + + choice EXAMPLE_CONNECT_INTERFACE + prompt "Connect using" + default EXAMPLE_CONNECT_WIFI + help + Protocol examples can use Wi-Fi or Ethernet to connect to the network. + Choose which interface to use. + + config EXAMPLE_CONNECT_WIFI + bool "Wi-Fi" + + config EXAMPLE_CONNECT_ETHERNET + bool "Ethernet" + + endchoice + + config EXAMPLE_WIFI_SSID + depends on EXAMPLE_CONNECT_WIFI + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config EXAMPLE_WIFI_PASSWORD + depends on EXAMPLE_CONNECT_WIFI + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + Can be left blank if the network has no security set. + + + choice PHY_MODEL + prompt "Ethernet PHY" + depends on EXAMPLE_CONNECT_ETHERNET + default CONFIG_PHY_TLK110 + help + Select the PHY driver to use for the example. + + config PHY_IP101 + bool "IP101" + help + IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver. + Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it. + + config PHY_TLK110 + bool "TI TLK110 PHY" + help + Select this to use the TI TLK110 PHY + + config PHY_LAN8720 + bool "Microchip LAN8720 PHY" + help + Select this to use the Microchip LAN8720 PHY + + endchoice + + + config PHY_ADDRESS + int "PHY Address (0-31)" + depends on EXAMPLE_CONNECT_ETHERNET + default 31 + range 0 31 + help + Select the PHY Address (0-31) for the hardware configuration and PHY model. + TLK110 default 31 + LAN8720 default 1 or 0 + + + choice PHY_CLOCK_MODE + prompt "EMAC clock mode" + depends on EXAMPLE_CONNECT_ETHERNET + default PHY_CLOCK_GPIO0_IN + help + Select external (input on GPIO0) or internal (output on GPIO16 or GPIO17) clock + + + config PHY_CLOCK_GPIO0_IN + bool "GPIO0 input" + depends on EXAMPLE_CONNECT_ETHERNET + help + Input of 50MHz PHY clock on GPIO0. + + config PHY_CLOCK_GPIO0_OUT + bool "GPIO0 Output" + help + Output the internal 50MHz RMII clock on GPIO0. + + config PHY_CLOCK_GPIO16_OUT + bool "GPIO16 output" + depends on EXAMPLE_CONNECT_ETHERNET + help + Output the internal 50MHz APLL clock on GPIO16. + + config PHY_CLOCK_GPIO17_OUT + bool "GPIO17 output (inverted)" + depends on EXAMPLE_CONNECT_ETHERNET + help + Output the internal 50MHz APLL clock on GPIO17 (inverted signal). + + endchoice + + config PHY_CLOCK_MODE + int + depends on EXAMPLE_CONNECT_ETHERNET + default 0 if PHY_CLOCK_GPIO0_IN + default 1 if PHY_CLOCK_GPIO0_OUT + default 2 if PHY_CLOCK_GPIO16_OUT + default 3 if PHY_CLOCK_GPIO17_OUT + + + config PHY_USE_POWER_PIN + bool "Use PHY Power (enable/disable) pin" + depends on EXAMPLE_CONNECT_ETHERNET + default y + help + Use a GPIO "power pin" to power the PHY on/off during operation. + Consult the example README for more details + + config PHY_POWER_PIN + int "PHY Power GPIO" + depends on EXAMPLE_CONNECT_ETHERNET + default 17 + range 0 33 + depends on PHY_USE_POWER_PIN + help + GPIO number to use for powering on/off the PHY. + + config PHY_SMI_MDC_PIN + int "SMI MDC Pin" + depends on EXAMPLE_CONNECT_ETHERNET + default 23 + range 0 33 + help + GPIO number to use for SMI clock output MDC to PHY. + + config PHY_SMI_MDIO_PIN + int "SMI MDIO Pin" + depends on EXAMPLE_CONNECT_ETHERNET + default 18 + range 0 33 + help + GPIO number to use for SMI data pin MDIO to/from PHY. + + config EXAMPLE_CONNECT_IPV6 + bool "Obtain IPv6 link-local address" + default y + help + By default, examples will wait until IPv4 and IPv6 addresses are obtained. + Disable this option if the network does not support IPv6. + +endmenu diff --git a/examples/common_components/protocol_examples_common/component.mk b/examples/common_components/protocol_examples_common/component.mk new file mode 100644 index 000000000..e69de29bb diff --git a/examples/common_components/protocol_examples_common/connect.c b/examples/common_components/protocol_examples_common/connect.c new file mode 100644 index 000000000..37d9df6db --- /dev/null +++ b/examples/common_components/protocol_examples_common/connect.c @@ -0,0 +1,291 @@ +/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#include +#include "protocol_examples_common.h" +#include "sdkconfig.h" +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_eth.h" +#include "esp_log.h" +#include "tcpip_adapter.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "lwip/err.h" +#include "lwip/sys.h" + +#define GOT_IPV4_BIT BIT(0) +#define GOT_IPV6_BIT BIT(1) + +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 +#define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT) +#else +#define CONNECTED_BITS (GOT_IPV4_BIT) +#endif + +static EventGroupHandle_t s_connect_event_group; +static ip4_addr_t s_ip_addr; +static const char* s_connection_name; + +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 +static ip6_addr_t s_ipv6_addr; +#endif + + +static const char *TAG = "example_connect"; + +/* set up connection, Wi-Fi or Ethernet */ +static void start(); + +/* tear down connection, release resources */ +static void stop(); + +static void on_got_ip(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr)); + xEventGroupSetBits(s_connect_event_group, GOT_IPV4_BIT); +} + +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + +static void on_got_ipv6(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + ip_event_got_ip6_t* event = (ip_event_got_ip6_t*) event_data; + 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 + +esp_err_t example_connect() +{ + if (s_connect_event_group != NULL) { + return ESP_ERR_INVALID_STATE; + } + s_connect_event_group = xEventGroupCreate(); + start(); + xEventGroupWaitBits(s_connect_event_group, CONNECTED_BITS, true, true, portMAX_DELAY); + ESP_LOGI(TAG, "Connected to %s", s_connection_name); + ESP_LOGI(TAG, "IPv4 address: " IPSTR, IP2STR(&s_ip_addr)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_LOGI(TAG, "IPv6 address: " IPV6STR, IPV62STR(s_ipv6_addr)); +#endif + return ESP_OK; +} + +esp_err_t example_disconnect() +{ + if (s_connect_event_group == NULL) { + return ESP_ERR_INVALID_STATE; + } + vEventGroupDelete(s_connect_event_group); + s_connect_event_group = NULL; + stop(); + ESP_LOGI(TAG, "Disconnected from %s", s_connection_name); + s_connection_name = NULL; + return ESP_OK; +} + +#ifdef CONFIG_EXAMPLE_CONNECT_WIFI + +static void on_wifi_disconnect(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect..."); + ESP_ERROR_CHECK( esp_wifi_connect() ); +} + +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + +static void on_wifi_connect(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); +} + +#endif // CONFIG_EXAMPLE_CONNECT_IPV6 + +static void start() +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL)); +#endif + + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_EXAMPLE_WIFI_SSID, + .password = CONFIG_EXAMPLE_WIFI_PASSWORD, + }, + }; + ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); + s_connection_name = CONFIG_EXAMPLE_WIFI_SSID; +} + +static void stop() +{ + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect)); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6)); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect)); +#endif + ESP_ERROR_CHECK(esp_wifi_stop()); + ESP_ERROR_CHECK(esp_wifi_deinit()); +} +#endif // CONFIG_EXAMPLE_CONNECT_WIFI + + +#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET + +#include "driver/gpio.h" + +#ifdef CONFIG_PHY_LAN8720 +#include "eth_phy/phy_lan8720.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config +#endif +#ifdef CONFIG_PHY_TLK110 +#include "eth_phy/phy_tlk110.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config +#elif CONFIG_PHY_IP101 +#include "eth_phy/phy_ip101.h" +#define DEFAULT_ETHERNET_PHY_CONFIG phy_ip101_default_ethernet_config +#endif + +#define PIN_PHY_POWER CONFIG_PHY_POWER_PIN +#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN +#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN + +#ifdef CONFIG_PHY_USE_POWER_PIN +/** + * @brief re-define power enable func for phy + * + * @param enable true to enable, false to disable + * + * @note This function replaces the default PHY power on/off function. + * If this GPIO is not connected on your device (and PHY is always powered), + * you can use the default PHY-specific power on/off function. + */ +static void phy_device_power_enable_via_gpio(bool enable) +{ + assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable); + + if (!enable) { + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false); + } + + gpio_pad_select_gpio(PIN_PHY_POWER); + gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT); + if (enable == true) { + gpio_set_level(PIN_PHY_POWER, 1); + ESP_LOGI(TAG, "Power On Ethernet PHY"); + } else { + gpio_set_level(PIN_PHY_POWER, 0); + ESP_LOGI(TAG, "Power Off Ethernet PHY"); + } + + vTaskDelay(1); // Allow the power up/down to take effect, min 300us + + if (enable) { + /* call the default PHY-specific power on function */ + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true); + } +} +#endif + +/** + * @brief gpio specific init + * + * @note RMII data pins are fixed in esp32: + * TXD0 <=> GPIO19 + * TXD1 <=> GPIO22 + * TX_EN <=> GPIO21 + * RXD0 <=> GPIO25 + * RXD1 <=> GPIO26 + * CLK <=> GPIO0 + * + */ +static void eth_gpio_config_rmii(void) +{ + phy_rmii_configure_data_interface_pins(); + phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO); +} + +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + +/** Event handler for Ethernet events */ +static void on_eth_event(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + ESP_LOGI(TAG, "Ethernet Link Up"); + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_ETH); + break; + default: + break; + } +} + +#endif // CONFIG_EXAMPLE_CONNECT_IPV6 + +static void start() +{ + eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG; + config.phy_addr = CONFIG_PHY_ADDRESS; + config.gpio_config = eth_gpio_config_rmii; + config.tcpip_input = tcpip_adapter_eth_input; + config.clock_mode = CONFIG_PHY_CLOCK_MODE; + +#ifdef CONFIG_PHY_USE_POWER_PIN + /* Replace the default 'power enable' function with an example-specific one + that toggles a power GPIO. */ + config.phy_power_enable = phy_device_power_enable_via_gpio; +#endif + + ESP_ERROR_CHECK(esp_eth_init(&config)); + + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip, NULL)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL)); +#endif + + ESP_ERROR_CHECK(esp_eth_enable()); + + s_connection_name = "Ethernet"; +} + +static void stop() +{ + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip)); +#ifdef CONFIG_EXAMPLE_CONNECT_IPV6 + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6)); + ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event)); +#endif + + ESP_ERROR_CHECK(esp_eth_disable()); + ESP_ERROR_CHECK(esp_eth_deinit()); +} + +#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET diff --git a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h new file mode 100644 index 000000000..e250db14d --- /dev/null +++ b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h @@ -0,0 +1,59 @@ +/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection. + + 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 "esp_err.h" +#include "tcpip_adapter.h" + +#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET +#define EXAMPLE_INTERFACE TCPIP_ADAPTER_IF_ETH +#endif + +#ifdef CONFIG_EXAMPLE_CONNECT_WIFI +#define EXAMPLE_INTERFACE TCPIP_ADAPTER_IF_STA +#endif + +/** + * @brief Configure Wi-Fi or Ethernet, connect, wait for IP + * + * This all-in-one helper function is used in protocols examples to + * reduce the amount of boilerplate in the example. + * + * It is not intended to be used in real world applications. + * See examples under examples/wifi/getting_started/ and examples/ethernet/ + * for more complete Wi-Fi or Ethernet initialization code. + * + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + * + * @return ESP_OK on successful connection + */ +esp_err_t example_connect(); + +/** + * Counterpart to example_connect, de-initializes Wi-Fi or Ethernet + */ +esp_err_t example_disconnect(); + +/** + * @brief Configure stdin and stdout to use blocking I/O + * + * This helper function is used in ASIO examples. It wraps installing the + * UART driver and configuring VFS layer to use UART driver for console I/O. + */ +esp_err_t example_configure_stdin_stdout(); + +#ifdef __cplusplus +} +#endif diff --git a/examples/common_components/protocol_examples_common/stdin_out.c b/examples/common_components/protocol_examples_common/stdin_out.c new file mode 100644 index 000000000..a4bc2cca4 --- /dev/null +++ b/examples/common_components/protocol_examples_common/stdin_out.c @@ -0,0 +1,30 @@ +/* Common functions for protocol examples, to configure stdin and stdout. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#include "protocol_examples_common.h" +#include "esp_err.h" +#include "esp_vfs_dev.h" +#include "driver/uart.h" +#include "sdkconfig.h" + +esp_err_t example_configure_stdin_stdout() +{ + // Initialize VFS & UART so we can use std::cout/cin + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0) ); + /* Tell VFS to use UART driver */ + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + /* Move the caret to the beginning of the next line on '\n' */ + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + return ESP_OK; +} diff --git a/examples/protocols/README.md b/examples/protocols/README.md index 0fef29383..0e31668a7 100644 --- a/examples/protocols/README.md +++ b/examples/protocols/README.md @@ -3,3 +3,23 @@ Implementation of internet communication protocols and services. See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples. + +## Establishing Wi-Fi or Ethernet Connection + +### About the `example_connect()` Function + +Protocols examples use a simple helper function, `example_connect()`, to establish Wi-Fi or Ethernet connection. This function is implemented in [examples/common_components/protocol_examples/common/connect.c](../common_components/protocol_examples/common/connect.c), and has a very simple behavior: block until connection is established and IP address is obtained, then return. This function is used to reduce the amount of boilerplate and to keep the example code focused on the protocol or library being demonstrated. + +The simple `example_connect()` function does not handle timeouts, does not gracefully handle various error conditions, and is only suited for use in examples. When developing real applications, this helper function needs to be replaced with full Wi-Fi / Ethernet connection handling code. Such code can be found in [examples/wifi/getting_started/](../examples/wifi/getting_started) and [examples/ethernet/ethernet/](../examples/ethernet/ethernet) examples. + +### Configuring the Example + +To configure the example to use Wi-Fi or Ethernet connection, run `make menuconfig` (or `idf.py menuconfig` if using CMake build system) and navigate to "Example Connection Configuration" menu. Select either "Wi-Fi" or "Ethernet" in the "Connect using" choice. + +When connecting using Wi-Fi, enter SSID and password of your Wi-Fi access point into the corresponding fields. If connecting to an open Wi-Fi network, keep the password field empty. + +When connecting using Ethernet, set up PHY type and configuration in the provided fields. If using Ethernet for the first time, it is recommended to start with the [Ethernet example readme](../examples/ethernet/ethernet/README.md), which contains instructions for connecting and configuring the PHY. Once Ethernet example obtains IP address successfully, proceed to the protocols example and set the same configuration options. + +### Disabling IPv6 + +By default, `example_connect()` function waits until Wi-Fi or Ethernet connection is established, and IPv4 address and IPv6 link-local address are obtained. In network environments where IPv6 link-local address cannot be obtained, disable "Obtain IPv6 link-local address" option found in "Example Connection Configuration" menu.