前言
虽然esp32支持无线联网,也可以同时使用espnow和wifi功能,但是切换这两种网络比较麻烦。使用有线网络也可以提供更低的网络延时,和更稳定的网络访问。这里我们使用常见的w5500模块作为扩展模块 ,如下图所示。
该模块使用SPI接口,ESP32C6可以使用SPI2作为通信设备。
1.1 引脚设置
我们先在esp idf官方示范代码里,复制eth网络基本工程(目录是esp-idf/examples/ethernet/basic/)到一个目录里。
复制了工程之后,我们需要设置spi的引脚(使用idf.py menuconfig命令),将对应引脚设置成图示的内容。
1.2 参考代码
我在官网Eth网络示范代码的基础上,添加了socket响应部分的代码,完整代码如下
- /* 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 "esp_eth.h"
- #include "esp_event.h"
- #include "esp_log.h"
- #include "esp_netif.h"
- #include "ethernet_init.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "nvs_flash.h"
- #include "sdkconfig.h"
- #include <stdio.h>
- #include <string.h>
-
- #include "lwip/err.h"
- #include "lwip/sockets.h"
- #include "lwip/sys.h"
- #include <lwip/netdb.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 w5500(void) {
- // Initialize Ethernet driver
- uint8_t eth_port_cnt = 0;
- esp_eth_handle_t *eth_handles;
- ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
-
- // Create instance(s) of esp-netif for Ethernet(s)
- if (eth_port_cnt == 1) {
- // Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and
- // you don't need to modify default esp-netif configuration parameters.
- esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
- esp_netif_t *eth_netif = esp_netif_new(&cfg);
- // Attach Ethernet driver to TCP/IP stack
- ESP_ERROR_CHECK(
- esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0])));
- } else {
- // Use ESP_NETIF_INHERENT_DEFAULT_ETH when multiple Ethernet interfaces are
- // used and so you need to modify esp-netif configuration parameters for
- // each interface (name, priority, etc.).
- 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};
- char if_key_str[10];
- char if_desc_str[10];
- char num_str[3];
- for (int i = 0; i < eth_port_cnt; i++) {
- itoa(i, num_str, 10);
- strcat(strcpy(if_key_str, "ETH_"), num_str);
- strcat(strcpy(if_desc_str, "eth"), num_str);
- esp_netif_config.if_key = if_key_str;
- esp_netif_config.if_desc = if_desc_str;
- esp_netif_config.route_prio -= i * 5;
- esp_netif_t *eth_netif = esp_netif_new(&cfg_spi);
-
- // Attach Ethernet driver to TCP/IP stack
- ESP_ERROR_CHECK(
- esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i])));
- }
- }
-
- // 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));
-
- // Start Ethernet driver state machine
- for (int i = 0; i < eth_port_cnt; i++) {
- ESP_ERROR_CHECK(esp_eth_start(eth_handles[i]));
- }
- }
-
- #define PORT 3333
- #define KEEPALIVE_IDLE 5
- #define KEEPALIVE_INTERVAL 5
- #define KEEPALIVE_COUNT 3
-
- static void do_retransmit(const int sock) {
- int len;
- char rx_buffer[128];
-
- do {
- len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
- if (len < 0) {
- ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
- } else if (len == 0) {
- ESP_LOGW(TAG, "Connection closed");
- } else {
- rx_buffer[len] =
- 0; // Null-terminate whatever is received and treat it like a string
- ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
-
- // send() can return less bytes than supplied length.
- // Walk-around for robust implementation.
- int to_write = len;
- while (to_write > 0) {
- int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
- if (written < 0) {
- ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
- // Failed to retransmit, giving up
- return;
- }
- to_write -= written;
- }
- }
- } while (len > 0);
- }
-
- static void tcp_server_task(void *pvParameters) {
- char addr_str[128];
- int addr_family = (int)pvParameters;
- int ip_protocol = 0;
- int keepAlive = 1;
- int keepIdle = KEEPALIVE_IDLE;
- int keepInterval = KEEPALIVE_INTERVAL;
- int keepCount = KEEPALIVE_COUNT;
- struct sockaddr_storage dest_addr;
-
- if (addr_family == AF_INET) {
- struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
- dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
- dest_addr_ip4->sin_family = AF_INET;
- dest_addr_ip4->sin_port = htons(PORT);
- ip_protocol = IPPROTO_IP;
- }
-
- int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
- if (listen_sock < 0) {
- ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
- vTaskDelete(NULL);
- return;
- }
- int opt = 1;
- setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- ESP_LOGI(TAG, "Socket created");
-
- int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
- if (err != 0) {
- ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
- ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
- goto CLEAN_UP;
- }
- ESP_LOGI(TAG, "Socket bound, port %d", PORT);
-
- err = listen(listen_sock, 1);
- if (err != 0) {
- ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
- goto CLEAN_UP;
- }
-
- while (1) {
-
- ESP_LOGI(TAG, "Socket listening");
-
- struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
- socklen_t addr_len = sizeof(source_addr);
- int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
- if (sock < 0) {
- ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
- break;
- }
-
- // Set tcp keepalive option
- setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
- setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
- setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
- setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
- // Convert ip address to string
-
- if (source_addr.ss_family == PF_INET) {
- inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str,
- sizeof(addr_str) - 1);
- }
-
- ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
-
- do_retransmit(sock);
-
- shutdown(sock, 0);
- close(sock);
- }
-
- CLEAN_UP:
- close(listen_sock);
- vTaskDelete(NULL);
- }
-
- void app_main(void) {
- ESP_ERROR_CHECK(nvs_flash_init());
- ESP_ERROR_CHECK(esp_netif_init());
- ESP_ERROR_CHECK(esp_event_loop_create_default());
-
- w5500();
- xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 5, NULL);
- }
复制代码
1.3 编译调试
在命令行运行idf.py flash monitor,可以看到终端有以下的输出
可以看到,已经正确获取到了动态IP地址,接下来在另一个命令行输入,然后随意输入一些内容,可以看到esp32也将对应内容发送了回来。
复制代码
|