connect_eth.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. *
  6. * https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_eth.html#basic-ethernet-concepts
  7. */
  8. #include "sdkconfig.h"
  9. #if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500)
  10. #include "defines.h"
  11. #include "Helper.h"
  12. #include "connect_eth.h"
  13. #include <string>
  14. #include <esp_event.h>
  15. #include <netdb.h>
  16. #include <esp_system.h>
  17. #include <esp_wnm.h>
  18. #include <esp_rrm.h>
  19. #include <esp_mbo.h>
  20. #include <esp_mac.h>
  21. #include <esp_log.h>
  22. #include <esp_eth.h>
  23. #include <esp_netif.h>
  24. #include <esp_netif_sntp.h>
  25. #include "driver/gpio.h"
  26. #include <driver/spi_master.h>
  27. // define `gpio_pad_select_gpip` for newer versions of IDF
  28. #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
  29. #include "esp_rom_gpio.h"
  30. #define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
  31. #endif
  32. #include "time_sntp.h"
  33. #include "ClassLogFile.h"
  34. #include "statusled.h"
  35. #include "read_network_config.h"
  36. #include "interface_mqtt.h"
  37. static const char *TAG = "ETH w5500";
  38. static bool gpio_isr_svc_init_by_eth = false; // indicates that we initialized the GPIO ISR service
  39. esp_eth_handle_t my_w5500_handle = NULL;
  40. esp_netif_t *my_w5500_netif = NULL;
  41. esp_eth_netif_glue_handle_t my_w5500_netif_glue = NULL;
  42. static bool eth_initialized = false;
  43. static bool eth_connected = false;
  44. static bool eth_connection_uccessful = false;
  45. static int eth_reconnect_cnt = 0;
  46. /**
  47. * @brief SPI bus initialization (to be used by Ethernet SPI modules)
  48. *
  49. * @return
  50. * - ESP_OK on success
  51. */
  52. static esp_err_t spi_bus_init(void)
  53. {
  54. esp_err_t retVal = ESP_OK;
  55. // Configure IO Pad as General Purpose IO,
  56. // so that it can be connected to internal Matrix,
  57. // then combined with one or more peripheral signals.
  58. gpio_pad_select_gpio(ETH_SPI_EN);
  59. ESP_ERROR_CHECK(gpio_set_direction(ETH_SPI_EN, GPIO_MODE_OUTPUT));
  60. ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 1));
  61. vTaskDelay(pdMS_TO_TICKS(500));
  62. if (ETH_SPI_INT0_GPIO != GPIO_NUM_NC)
  63. {
  64. // Install GPIO ISR handler to be able to service SPI Eth modules interrupts
  65. ESP_LOGI(TAG, "spi_bus_init(): Install GPIO ISR handler...");
  66. retVal = gpio_install_isr_service(0);
  67. if (retVal == ESP_OK)
  68. {
  69. gpio_isr_svc_init_by_eth = true;
  70. ESP_LOGI(TAG, "spi_bus_init(): GPIO ISR handler install successful");
  71. }
  72. else if (retVal == ESP_ERR_INVALID_STATE)
  73. {
  74. ESP_LOGW(TAG, "spi_bus_init(): GPIO ISR handler has been already installed");
  75. retVal = ESP_OK; // ISR handler has been already installed so no issues
  76. }
  77. else
  78. {
  79. ESP_LOGE(TAG, "spi_bus_init(): GPIO ISR handler install failed");
  80. return retVal;
  81. }
  82. }
  83. // Init SPI bus
  84. spi_bus_config_t buscfg = {
  85. .mosi_io_num = ETH_SPI_MOSI_GPIO,
  86. .miso_io_num = ETH_SPI_MISO_GPIO,
  87. .sclk_io_num = ETH_SPI_SCLK_GPIO,
  88. .quadwp_io_num = GPIO_NUM_NC,
  89. .quadhd_io_num = GPIO_NUM_NC,
  90. };
  91. retVal = spi_bus_initialize(ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
  92. if (retVal != ESP_OK)
  93. {
  94. ESP_LOGE(TAG, "spi_bus_init(): SPI host #%d init failed", ETH_SPI_HOST);
  95. }
  96. return retVal;
  97. }
  98. static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
  99. {
  100. if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START)
  101. {
  102. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Started");
  103. // Typically nothing special here
  104. }
  105. else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_STOP)
  106. {
  107. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Stopped");
  108. eth_connected = false;
  109. }
  110. else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED)
  111. {
  112. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Link Up");
  113. uint8_t mac_addr[6] = {0};
  114. /* we can get the ethernet driver handle from event data */
  115. esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
  116. ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr));
  117. 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]);
  118. eth_connected = true; // Not IP-ready until IP_EVENT_ETH_GOT_IP
  119. }
  120. else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED)
  121. {
  122. LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Ethernet Link Down");
  123. eth_connected = false;
  124. // Optionally, try to reconnect or handle fallback LED:
  125. set_status_led(WLAN_CONN, 1, false);
  126. eth_reconnect_cnt++;
  127. }
  128. else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP)
  129. {
  130. // We have a valid IP
  131. eth_connection_uccessful = true;
  132. eth_connected = true;
  133. eth_reconnect_cnt = 0;
  134. ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
  135. network_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.ip));
  136. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress);
  137. network_config.netmask = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.netmask));
  138. network_config.gateway = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.gw));
  139. esp_netif_dns_info_t dnsInfo;
  140. ESP_ERROR_CHECK(esp_netif_get_dns_info(event->esp_netif, ESP_NETIF_DNS_MAIN, &dnsInfo));
  141. network_config.dns = std::string(ip4addr_ntoa((const ip4_addr *)&dnsInfo.ip));
  142. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress + ", Subnet: " + network_config.netmask + ", Gateway: " + network_config.gateway + ", DNS: " + network_config.dns);
  143. if (getMQTTisEnabled())
  144. {
  145. vTaskDelay(500 / portTICK_PERIOD_MS);
  146. MQTT_Init(); // Init when Ethernet is getting connected
  147. }
  148. // Optionally set a status LED
  149. set_status_led(WLAN_CONN, 0, true); // e.g., 0 means "ok"
  150. }
  151. }
  152. esp_err_t eth_init_W5500(void)
  153. {
  154. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init...");
  155. // Set log level for netif component to WARN level (default: INFO; only relevant for serial console)
  156. // ********************************************
  157. esp_log_level_set("netif", ESP_LOG_WARN);
  158. esp_err_t retVal = esp_netif_init();
  159. if (retVal != ESP_OK)
  160. {
  161. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retVal));
  162. return retVal;
  163. }
  164. retVal = esp_event_loop_create_default();
  165. if (retVal != ESP_OK)
  166. {
  167. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retVal));
  168. return retVal;
  169. }
  170. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SPI init");
  171. retVal = spi_bus_init();
  172. if (retVal != ESP_OK)
  173. {
  174. ESP_LOGE(TAG, "Failed to init spi bus, error=0x%x", retVal);
  175. return retVal;
  176. }
  177. spi_device_interface_config_t devcfg = {
  178. .mode = 0, // SPI mode 0
  179. .clock_speed_hz = ETH_SPI_CLOCK_MHZ * 1000 * 1000,
  180. .spics_io_num = ETH_SPI_CS0_GPIO,
  181. .queue_size = 30,
  182. };
  183. eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(ETH_SPI_HOST, &devcfg);
  184. // Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically
  185. w5500_config.int_gpio_num = ETH_SPI_INT0_GPIO;
  186. // Period in ms to poll rx status when interrupt mode is not used
  187. if (gpio_isr_svc_init_by_eth == false)
  188. {
  189. w5500_config.poll_period_ms = ETH_SPI_POLLING0_MS;
  190. }
  191. eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
  192. esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
  193. // Update PHY config based on board specific configuration
  194. eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
  195. phy_config.phy_addr = ETH_SPI_PHY_ADDR0; // alter the PHY address
  196. phy_config.reset_gpio_num = ETH_SPI_PHY_RST0_GPIO; // alter the GPIO used for PHY reset
  197. esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);
  198. esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
  199. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver install...");
  200. retVal = esp_eth_driver_install(&eth_config, &my_w5500_handle);
  201. if (retVal != ESP_OK)
  202. {
  203. ESP_LOGE(TAG, "Failed to install Ethernet driver, error=0x%x", retVal);
  204. return retVal;
  205. }
  206. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver installed");
  207. uint8_t base_mac_addr[6];
  208. retVal = esp_efuse_mac_get_default(base_mac_addr);
  209. if (retVal != ESP_OK)
  210. {
  211. ESP_LOGE(TAG, "Failed to get efuse base MAC, error=0x%x", retVal);
  212. return retVal;
  213. }
  214. uint8_t local_mac[6];
  215. retVal = esp_derive_local_mac(local_mac, base_mac_addr);
  216. if (retVal != ESP_OK)
  217. {
  218. ESP_LOGE(TAG, "Failed to derive local MAC address from universal MAC address, error=0x%x", retVal);
  219. return retVal;
  220. }
  221. retVal = esp_eth_ioctl(my_w5500_handle, ETH_CMD_S_MAC_ADDR, local_mac);
  222. if (retVal != ESP_OK)
  223. {
  224. ESP_LOGE(TAG, "Failed to set W5500 MAC, error=0x%x", retVal);
  225. return retVal;
  226. }
  227. esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
  228. my_w5500_netif = esp_netif_new(&netif_cfg);
  229. my_w5500_netif_glue = esp_eth_new_netif_glue(my_w5500_handle);
  230. if (!network_config.ipaddress.empty() && !network_config.gateway.empty() && !network_config.netmask.empty())
  231. {
  232. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + network_config.ipaddress + ", Gateway: " + std::string(network_config.gateway) + ", Netmask: " + std::string(network_config.netmask));
  233. esp_netif_dhcpc_stop(my_w5500_netif); // Stop DHCP service
  234. esp_netif_ip_info_t ip_info;
  235. int a, b, c, d;
  236. string_to_ip4(network_config.ipaddress.c_str(), a, b, c, d);
  237. IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address
  238. string_to_ip4(network_config.gateway.c_str(), a, b, c, d);
  239. IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway
  240. string_to_ip4(network_config.netmask.c_str(), a, b, c, d);
  241. IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask
  242. esp_netif_set_ip_info(my_w5500_netif, &ip_info); // Set static IP configuration
  243. if (network_config.dns.empty())
  244. {
  245. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway");
  246. network_config.dns = network_config.gateway;
  247. }
  248. else
  249. {
  250. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + network_config.dns);
  251. }
  252. esp_netif_dns_info_t dns_info;
  253. ip4_addr_t ip;
  254. ip.addr = esp_ip4addr_aton(network_config.dns.c_str());
  255. ip_addr_set_ip4_u32(&dns_info.ip, ip.addr);
  256. retVal = esp_netif_set_dns_info(my_w5500_netif, ESP_NETIF_DNS_MAIN, &dns_info);
  257. if (retVal != ESP_OK)
  258. {
  259. LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retVal));
  260. return retVal;
  261. }
  262. }
  263. else
  264. {
  265. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service");
  266. }
  267. // Register an event handler to the system event loop (legacy).
  268. ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
  269. ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &eth_event_handler, NULL));
  270. retVal = esp_netif_attach(my_w5500_netif, my_w5500_netif_glue);
  271. if (retVal != ESP_OK)
  272. {
  273. ESP_LOGE(TAG, "Failed to attaches esp_netif instance to the io driver handle, error=0x%x", retVal);
  274. return retVal;
  275. }
  276. retVal = esp_eth_start(my_w5500_handle);
  277. if (retVal != ESP_OK)
  278. {
  279. ESP_LOGE(TAG, "Failed to start Ethernet driver, error=0x%x", retVal);
  280. return retVal;
  281. }
  282. eth_initialized = true;
  283. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init done");
  284. return ESP_OK;
  285. }
  286. void eth_deinit_W5500(void)
  287. {
  288. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit...");
  289. esp_err_t retVal = ESP_OK;
  290. eth_initialized = false;
  291. ESP_LOGD(TAG, "esp_eth_stop(my_w5500_handle)");
  292. ESP_ERROR_CHECK(esp_eth_stop(my_w5500_handle));
  293. ESP_LOGD(TAG, "esp_eth_del_netif_glue(my_w5500_netif_glue)");
  294. ESP_ERROR_CHECK(esp_eth_del_netif_glue(my_w5500_netif_glue));
  295. ESP_LOGD(TAG, "esp_netif_destroy(my_w5500_netif)");
  296. esp_netif_destroy(my_w5500_netif);
  297. ESP_LOGD(TAG, "esp_netif_deinit()");
  298. ESP_ERROR_CHECK(esp_netif_deinit());
  299. esp_eth_mac_t *mac = NULL;
  300. esp_eth_phy_t *phy = NULL;
  301. ESP_LOGD(TAG, "esp_eth_get_mac_instance(my_w5500_handle, &mac)");
  302. ESP_ERROR_CHECK(esp_eth_get_mac_instance(my_w5500_handle, &mac));
  303. ESP_LOGD(TAG, "esp_eth_get_phy_instance(my_w5500_handle, &phy)");
  304. ESP_ERROR_CHECK(esp_eth_get_phy_instance(my_w5500_handle, &phy));
  305. ESP_LOGD(TAG, "esp_eth_driver_uninstall(my_w5500_handle)");
  306. retVal = esp_eth_driver_uninstall(my_w5500_handle);
  307. if (retVal != ESP_OK)
  308. {
  309. ESP_LOGE(TAG, "Ethernet driver %p uninstall failed", my_w5500_handle);
  310. }
  311. ESP_LOGD(TAG, "spi_bus_free(ETH_SPI_HOST)");
  312. retVal = spi_bus_free(ETH_SPI_HOST);
  313. if (retVal != ESP_OK)
  314. {
  315. ESP_LOGE(TAG, "spi_bus_free failed");
  316. }
  317. // We installed the GPIO ISR service so let's uninstall it too.
  318. // BE CAREFUL HERE though since the service might be used by other functionality!
  319. if (gpio_isr_svc_init_by_eth == true)
  320. {
  321. ESP_LOGW(TAG, "uninstalling GPIO ISR service!");
  322. gpio_uninstall_isr_service();
  323. }
  324. ESP_LOGD(TAG, "free(my_w5500_handle)");
  325. free(my_w5500_handle);
  326. ESP_LOGD(TAG, "esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler)");
  327. ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler));
  328. ESP_LOGD(TAG, "esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)");
  329. ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
  330. ESP_LOGD(TAG, "esp_event_loop_delete_default()");
  331. ESP_ERROR_CHECK(esp_event_loop_delete_default());
  332. ESP_LOGD(TAG, "gpio_set_level(ETH_SPI_EN, 0)");
  333. ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 0));
  334. LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit done");
  335. }
  336. bool getETHisConnected(void)
  337. {
  338. return eth_connected;
  339. }
  340. #endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500)