/* Ethernet Basic Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_netif_net_stack.h"
#include "esp_mac.h"
#include "lwip/netif.h"
#include "ping/ping_sock.h"
#include "netdb.h"
#include "driver/spi_master.h"
#define USE_IPV6_ADDRESS
#define color_default "\x1B[0m"
#define color_red "\x1B[1;31m"
#define color_green "\x1B[1;32m"
#define color_yellow "\x1B[1;33m"
#define color_blue "\x1B[1;34m"
#define color_magenta "\x1B[1;35m"
#define color_cyan "\x1B[1;36m"
#define color_white "\x1B[1;37m"
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args);
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args);
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args);
static int do_ping_cmd(void);
static bool example_is_our_netif(const char *prefix, esp_netif_t *netif);
static const char *TAG = "eth_ipv6";
typedef struct
{
uint8_t spi_cs_gpio;
uint8_t int_gpio;
int8_t phy_reset_gpio;
uint8_t phy_addr;
} spi_eth_module_config_t;
/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
uint8_t mac_addr[6] = {0};
/* we can get the ethernet driver handle from event data */
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
switch (event_id)
{
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
struct netif *lwip_netif = esp_netif_get_netif_impl(arg);
netif_set_flags(lwip_netif, NETIF_FLAG_MLD6);
ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(arg));
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(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;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
static void got_ip6_event_handler(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;
if (!example_is_our_netif("eth0", event->esp_netif))
{
ESP_LOGE(TAG, "Is not my interface %s", esp_netif_get_desc(event->esp_netif));
return;
}
const esp_netif_ip6_info_t *ip6_info = &event->ip6_info;
printf("%08lX %08lX %08lX %08lX\n", ip6_info->ip.addr[0], ip6_info->ip.addr[1], ip6_info->ip.addr[2], ip6_info->ip.addr[3]);
ESP_LOGI(TAG, "Ethernet Got IP6 Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP6:" IPV6STR, IPV62STR(ip6_info->ip));
ESP_LOGI(TAG, "Zone: %d", ip6_info->ip.zone);
ESP_LOGI(TAG, "~~~~~~~~~~~");
do_ping_cmd();
}
static bool example_is_our_netif(const char *prefix, esp_netif_t *netif)
{
return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0;
}
void app_main(void)
{
// Initialize TCP/IP network interface (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_log_level_set(TAG, ESP_LOG_DEBUG);
esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG);
// Create instance(s) of esp-netif for SPI Ethernet(s)
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
esp_netif_config_t cfg_spi = {
.base = &esp_netif_config,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH};
esp_netif_t *eth_netif_spi = NULL;
strcat(strcpy(if_key_str, "ETH_SPI_0"), num_str);
strcat(strcpy(if_desc_str, "eth0"), num_str);
esp_netif_config.if_key = if_key_str;
esp_netif_config.if_desc = if_desc_str;
esp_netif_config.route_prio = 30;
eth_netif_spi[i] = esp_netif_new(&cfg_spi);
// Init MAC and PHY configs to default
eth_mac_config_t mac_config_spi = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config_spi = ETH_PHY_DEFAULT_CONFIG();
// Install GPIO ISR handler to be able to service SPI Eth modlues interrupts
gpio_install_isr_service(0);
// Init SPI bus
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
// Init specific SPI Ethernet module configuration from Kconfig (CS GPIO, Interrupt GPIO, etc.)
spi_eth_module_config_t spi_eth_module_config;
spi_eth_module_config.spi_cs_gpio = CONFIG_EXAMPLE_ETH_SPI_CS0_GPIO;
spi_eth_module_config.int_gpio = CONFIG_EXAMPLE_ETH_SPI_INT0_GPIO;
spi_eth_module_config.phy_reset_gpio = CONFIG_EXAMPLE_ETH_SPI_PHY_RST0_GPIO;
spi_eth_module_config.phy_addr = CONFIG_EXAMPLE_ETH_SPI_PHY_ADDR0;
// Configure SPI interface and Ethernet driver for specific SPI module
esp_eth_mac_t *mac_spi;
esp_eth_phy_t *phy_spi;
esp_eth_handle_t eth_handle_spi = NULL;
spi_device_interface_config_t spi_devcfg = {
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.queue_size = 20};
// Set SPI module Chip Select GPIO
spi_devcfg.spics_io_num = spi_eth_module_config.spi_cs_gpio;
// Set remaining GPIO numbers and configuration used by the SPI module
phy_config_spi.phy_addr = spi_eth_module_config.phy_addr;
phy_config_spi.reset_gpio_num = spi_eth_module_config.phy_reset_gpio;
ESP_LOGI(TAG, "CONFIG_EXAMPLE_USE_KSZ8851SNL");
eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);
ksz8851snl_config.int_gpio_num = spi_eth_module_config.int_gpio;
mac_spi = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config_spi);
phy_spi = esp_eth_phy_new_ksz8851snl(&phy_config_spi);
esp_eth_config_t eth_config_spi = ETH_DEFAULT_CONFIG(mac_spi, phy_spi);
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config_spi, ð_handle_spi));
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_ETH);
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle_spi, ETH_CMD_S_MAC_ADDR, mac));
// attach Ethernet driver to TCP/IP stack
ESP_ERROR_CHECK(esp_netif_attach(eth_netif_spi, esp_eth_new_netif_glue(eth_handle_spi)));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_netif_spi));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, got_ip6_event_handler, NULL));
/* start Ethernet driver state machine */
ESP_ERROR_CHECK(esp_eth_start(eth_handle_spi));
}
#define EXAMPLE_PING_IP_V6 "fd00::c0ed:8d79:672f:e9b1"
#define EXAMPLE_PING_IP_V4 "192.168.81.68"
// #define EXAMPLE_PING_IP "192.168.81.1"
#define EXAMPLE_PING_INTERVAL 1
#define EXAMPLE_PING_COUNT 5
static int do_ping_cmd(void)
{
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
static esp_ping_handle_t ping;
config.interval_ms = (uint32_t)(EXAMPLE_PING_INTERVAL * 1000);
config.count = (uint32_t)(EXAMPLE_PING_COUNT);
// parse IP address
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
/* convert domain name to IP address */
if (getaddrinfo(EXAMPLE_PING_IP_V6, NULL, &hint, &res) != 0)
{
printf("ping: unknown host %s\n", EXAMPLE_PING_IP_V6);
return 1;
}
if (res->ai_family == AF_INET)
{
struct in_addr addr4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
printf(color_magenta IPSTR "\n" color_default, IP2STR(&target_addr.u_addr.ip4));
printf(color_magenta "Type %d\n" color_default, target_addr.type);
}
else
{
struct in6_addr addr6 = ((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
IP_SET_TYPE(&target_addr, IPADDR_TYPE_V6);
printf(color_magenta IPV6STR "\n" color_default, IPV62STR(target_addr.u_addr.ip6));
printf(color_magenta "Type %d\n" color_default, target_addr.type);
}
freeaddrinfo(res);
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL};
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%ld bytes from %s icmp_seq=%d ttl=%d time=%ld ms\n",
recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
{
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
if (IP_IS_V4(&target_addr))
{
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
}
else
{
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
printf("%ld packets transmitted, %ld received, %ld%% packet loss, time %ldms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
esp_ping_delete_session(hdl);
}