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 succeeded
static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
static int maintain();
static EthernetLinkStatus linkStatus();
static EthernetHardwareStatus hardwareStatus();
// Manaul configuration
static 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 socket
static void socketClose(uint8_t s);
// Establish TCP connection (Active connection)
static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
// disconnect the connection
static 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 error
static 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 buffered
static 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 error
static bool socketSendUDP(uint8_t s);
// Initialize the "random" source port number
static 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。