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; +}