/* ENC28J60 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 #include #include "sdkconfig.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 "esp_eth_enc28j60.h" #include "driver/spi_master.h" static const char *TAG = "eth_example"; /** 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_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); ESP_LOGI(TAG, "Ethernet Link Up"); 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]); 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, "~~~~~~~~~~~"); } void app_main(void) { ESP_ERROR_CHECK(gpio_install_isr_service(0)); // 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_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); esp_netif_t *eth_netif = esp_netif_new(&netif_cfg); // Set default handlers to process TCP/IP stuffs ESP_ERROR_CHECK(esp_eth_set_default_handlers(eth_netif)); // Register user defined event handers ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); spi_bus_config_t buscfg = { .miso_io_num = CONFIG_EXAMPLE_ENC28J60_MISO_GPIO, .mosi_io_num = CONFIG_EXAMPLE_ENC28J60_MOSI_GPIO, .sclk_io_num = CONFIG_EXAMPLE_ENC28J60_SCLK_GPIO, .quadwp_io_num = -1, .quadhd_io_num = -1, }; ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); /* ENC28J60 ethernet driver is based on spi driver */ spi_device_interface_config_t devcfg = { .command_bits = 3, .address_bits = 5, .mode = 0, .clock_speed_hz = CONFIG_EXAMPLE_ENC28J60_SPI_CLOCK_MHZ * 1000 * 1000, .spics_io_num = CONFIG_EXAMPLE_ENC28J60_CS_GPIO, .queue_size = 20, .cs_ena_posttrans = enc28j60_cal_spi_cs_hold_time(CONFIG_EXAMPLE_ENC28J60_SPI_CLOCK_MHZ), }; spi_device_handle_t spi_handle = NULL; ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ENC28J60_SPI_HOST, &devcfg, &spi_handle)); eth_enc28j60_config_t enc28j60_config = ETH_ENC28J60_DEFAULT_CONFIG(spi_handle); enc28j60_config.int_gpio_num = CONFIG_EXAMPLE_ENC28J60_INT_GPIO; eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); mac_config.smi_mdc_gpio_num = -1; // ENC28J60 doesn't have SMI interface mac_config.smi_mdio_gpio_num = -1; esp_eth_mac_t *mac = esp_eth_mac_new_enc28j60(&enc28j60_config, &mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); phy_config.autonego_timeout_ms = 0; // ENC28J60 doesn't support auto-negotiation phy_config.reset_gpio_num = -1; // ENC28J60 doesn't have a pin to reset internal PHY esp_eth_phy_t *phy = esp_eth_phy_new_enc28j60(&phy_config); esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); esp_eth_handle_t eth_handle = NULL; ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle)); /* ENC28J60 doesn't burn any factory MAC address, we need to set it manually. 02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control. */ mac->set_addr(mac, (uint8_t[]) { 0x02, 0x00, 0x00, 0x12, 0x34, 0x56 }); // ENC28J60 Errata #1 check if (emac_enc28j60_get_chip_info(mac) < ENC28J60_REV_B5 && CONFIG_EXAMPLE_ENC28J60_SPI_CLOCK_MHZ < 8) { ESP_LOGE(TAG, "SPI frequency must be at least 8 MHz for chip revision less than 5"); ESP_ERROR_CHECK(ESP_FAIL); } /* attach Ethernet driver to TCP/IP stack */ ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))); /* start Ethernet driver state machine */ ESP_ERROR_CHECK(esp_eth_start(eth_handle)); /* It is recommended to use ENC28J60 in Full Duplex mode since multiple errata exist to the Half Duplex mode */ #if CONFIG_EXAMPLE_ENC28J60_DUPLEX_FULL /* Set duplex needs to be called after esp_eth_start since the driver is started with auto-negotiation by default */ enc28j60_set_phy_duplex(phy, ETH_DUPLEX_FULL); #endif }