使用Aruino Ethernet使ESP32具有以太网通讯能力
ESP32的Ethernet通讯
- Arduino的Ethernet库
- Ethernet库
- 库函数介绍
- 总结
Arduino的Ethernet库
Arduino很早就支持Ethernet通讯。硬件主要是支持W5100,W5200和W5500以太网通讯模块通过SPI接口与ESP32通讯。在Arduino 1.8.19版本以前,Arduino不支持库的引用位置。比如,在例子中说调用Ethernet.h,但如果想看程序中的头文件,不知道这个库放在那个目录中,查找起来非常麻烦,用VSCODE可以看到调用的库在那个目录,可以直接打开库的位置,但让VSCODE运行Arduino也运行也并不是一件容易的事情。好在现在的Arduino 1.8.20可以解决这个问题了。我的库按照在Arduino-1.8.19\\library\\src\\ehternet. 有许多的例子说明库是使用。下面介绍一下这些库的应用情况。
Ethernet库
一般的应用都会调用Ethernet.h文件。调用这个文件就会调用其他的两个关键的库,Client.h 和Server.h库。如下:
#include <Arduino.h>
#include "Client.h"
#include "Server.h"
#include "Udp.h"
在ESP32中如果不对Server.h文件修改的话就会报错。许多网友使用这个库时会报错,我的博客中对此有解决方法。按此修改了Server.h后,应该是无法正常使用无线网了,具体的解决方法好没有找到。
下面介绍库里面的函数:
库函数介绍
enum EthernetLinkStatus {Unknown,LinkON,LinkOFF
};
枚举EthernerLinkStatus,以太网模块的连接状态。
enum EthernetHardwareStatus {EthernetNoHardware,EthernetW5100,EthernetW5200,EthernetW5500
};
以太网硬件,有三种模块,分别为W5100,W5200和W5500. 现在大部分使用的是W5500,通过速度100/10 MHz自动切换,最多可以建立8个连接。
主要的类定义:
class EthernetClass {
private:static IPAddress _dnsServerAddress;static DhcpClass* _dhcp;
public:// Initialise the Ethernet shield to use the provided MAC address and// gain the rest of the configuration through DHCP.// Returns 0 if the DHCP configuration failed, and 1 if it succeededstatic int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);static int maintain();static EthernetLinkStatus linkStatus();static EthernetHardwareStatus hardwareStatus();// Manaul configurationstatic void begin(uint8_t *mac, IPAddress ip);static void begin(uint8_t *mac, IPAddress ip, IPAddress dns);static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway);static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet);static void init(uint8_t sspin = 10);static void MACAddress(uint8_t *mac_address);static IPAddress localIP();static IPAddress subnetMask();static IPAddress gatewayIP();static IPAddress dnsServerIP() { return _dnsServerAddress; }void setMACAddress(const uint8_t *mac_address);void setLocalIP(const IPAddress local_ip);void setSubnetMask(const IPAddress subnet);void setGatewayIP(const IPAddress gateway);void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; }void setRetransmissionTimeout(uint16_t milliseconds);void setRetransmissionCount(uint8_t num);friend class EthernetClient;friend class EthernetServer;friend class EthernetUDP;
private:// Opens a socket(TCP or UDP or IP_RAW mode)static uint8_t socketBegin(uint8_t protocol, uint16_t port);static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port);static uint8_t socketStatus(uint8_t s);// Close socketstatic void socketClose(uint8_t s);// Establish TCP connection (Active connection)static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);// disconnect the connectionstatic void socketDisconnect(uint8_t s);// Establish TCP connection (Passive connection)static uint8_t socketListen(uint8_t s);// Send data (TCP)static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len);static uint16_t socketSendAvailable(uint8_t s);// Receive data (TCP)static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);static uint16_t socketRecvAvailable(uint8_t s);static uint8_t socketPeek(uint8_t s);// sets up a UDP datagram, the data for which will be provided by one// or more calls to bufferData and then finally sent with sendUDP.// return true if the datagram was successfully set up, or false if there was an errorstatic bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port);// copy up to len bytes of data from buf into a UDP datagram to be// sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls.// return Number of bytes successfully bufferedstatic uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len);// Send a UDP datagram built up from a sequence of startUDP followed by one or more// calls to bufferData.// return true if the datagram was successfully sent, or false if there was an errorstatic bool socketSendUDP(uint8_t s);// Initialize the "random" source port numberstatic void socketPortRand(uint16_t n);
};
extern EthernetClass Ethernet;
其中有用的函数:
linkStatus(); // 显示当前的连接状态,返回是前面枚举:
- Unknown,0
- LinkON,1
- LinkOFF,2
程序例子:
auto link = Ethernet.linkStatus();Serial.print("Link status: ");switch (link) {case Unknown:Serial.println("Unknown");break;case LinkON:Serial.println("ON");break;case LinkOFF:Serial.println("OFF");break;}
通过这个程序可以判断网线是否连接了模块。
hardwareStatus(); // 返回安装硬件的模块
这个在实际使用中意义不大,对于实验,判断模块是否正常有用,返回的也是前面定义的枚举:
- EthernetNoHardware, 0
- EthernetW5100, 1
- EthernetW5200, 2
- EthernetW5500 3
例子:
auto hardStatus = Ethernet.hardwareStatus();switch (hardStatus){case EthernetNoHardware:Serial.println("Ethernet No Hardware");break;case EthernetW5100:Serial.println("Ethernet W5100");break;case EthernetW5200:Serial.println("Ethernet W5200");break;case EthernetW5500:Serial.println("Ethernet W5500");break;}
Ethernet.maintain(); // 更新DHCP的地址
描述:
允许续订 DHCP 租约。当通过 DHCP 分配 IP 地址时,以太网设备将在该地址上租约一段时间。使用 Ethernet.maintain(),可以从 DHCP 服务器请求续订。根据服务器的配置,您可能会收到相同的地址、新地址或根本没有。
您可以根据需要随时调用此函数,它只会在需要时重新请求 DHCP 租约(在所有其他情况下返回 0)。最简单的方法是在每次 loop() 调用时调用它,但少一点也没关系。不调用此函数(或每秒调用它明显少于一次)将阻止在 DHCP 协议需要时续订租约,而是继续使用过期的租约(这不会直接中断连接,但如果 DHCP 服务器将同一地址租给其他人,事情可能会中断)。
还有一个比较需要强调一下的函数,这个函数是在Client类中的函数:
virtual uint8_t connected(); // client已经连接了server.
例子:
if (!client.connected() && alreadyConnected){Serial.println("TCP connection closed!");alreadyConnected = false;}
用上面的程序可以检测到TCP断开了连接。
总结
用ESP32做服务器,外部的设备做客户端,可与通过TCP建立modbusTCP进行通讯。也可以接收多个TCP连接,但服务器程序需要可以接收多个客户端。可以参考例子程序中的AdvicedChatSever。