diff --git a/README.md b/README.md index 9b997bf..c7d7411 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +First an important note: There are more suitable libraries for ENC28J60 with ESP8266, RP2040, ESP32 and Mbed Arduino platforms: + +* With ESP8266 and RP2040 platform you can use the EthernetCompat.h from the lwIP_enc28j60 bundled library. +* With ESP32 platform version 3 you can use the [EthernetESP32](https://github.com/Networking-for-Arduino/EthernetESP32) library which integrates with networking on the ESP32 platform. +* With Arduino Mbed Core boards you can use the [ENC28J60-EMAC](https://github.com/Networking-for-Arduino/ENC28J60-EMAC) library with the platforms Ethernet library + +--- + +# EthernetENC EthernetENC is the Ethernet library for ENC28J60. It is a modern version of [the UIPEthernet library](https://github.com/jandrassy/EthernetENC/wiki/UIPEthernet). @@ -9,6 +18,7 @@ The modernization includes: * SPI communication at 20 MHz if the MCU supports it, else on the maximum supported by the MCU * client.flush() to send the packet immediately * calls yield() in blocking functions +* has UDP backlog to receive more than one message at time * Arduino 1.5 library format built with dot_a_linkage option for optimal build result [The documentation of Arduino Ethernet library](https://www.arduino.cc/en/Reference/Ethernet) applies for classes and functions descriptions. @@ -17,9 +27,9 @@ Limitations: * UDP.beginMulticast is not supported, because the uIP stack doesn't support multicast * UDP broadcasts receiving is turned off on ENC to lower the processing load on the library -This library doesn't have examples, because examples of the Arduino Ethernet library apply. You can find them in the Arduino IDE Examples menu Ethernet section. Only change `#include ` to `#include `. Some examples require [a little change](https://github.com/jandrassy/EthernetENC/wiki/Examples). +This library doesn't have examples, because examples of the Arduino Ethernet library apply. You can find them in the Arduino IDE Examples menu Ethernet section. Only change `#include ` to `#include `. Some examples require [a little change](https://github.com/jandrassy/EthernetENC/wiki/Examples). -This library is based on the Norbert Truchsess's arduino-uip original source code repository and uses experience from the development of the multi-architecture support by Cassy. Applicable fixes and enhancements from developed of EthernetENC were transfered to Cassy's UIPEthernet. +This library is based on the Norbert Truchsess's arduino-uip original source code repository and uses experience from the development of the multi-architecture support by Cassy. Applicable fixes and enhancements from development of EthernetENC were transfered to Cassy's UIPEthernet. **You can find more information in project's [Wiki](https://github.com/jandrassy/EthernetENC/wiki).** diff --git a/library.properties b/library.properties index 6c53862..eecd621 100644 --- a/library.properties +++ b/library.properties @@ -3,9 +3,9 @@ author=Norbert Truchsess, Juraj Andrassy maintainer=Juraj Andrassy sentence=Ethernet library for ENC28J60. Only include EthernetENC.h instead of Ethernet.h paragraph=This is a modern version of the UIPEthernet library. EthernetENC library is compatible with all Arduino architectures with Arduino SPI library with transactions support. -url=https://github.com/jandrassy/EthernetENC/wiki +url=https://github.com/Networking-for-Arduino/EthernetENC/wiki architectures=* -version=2.0.1 +version=2.0.5 category=Communication includes=EthernetENC.h dot_a_linkage=true diff --git a/src/Dhcp.cpp b/src/Dhcp.cpp index 74280a3..cfcd2f6 100644 --- a/src/Dhcp.cpp +++ b/src/Dhcp.cpp @@ -24,8 +24,11 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long } void DhcpClass::reset_DHCP_lease(){ - // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp - memset(_dhcpLocalIp, 0, 20); + memset(_dhcpLocalIp, 0, 4); + memset(_dhcpSubnetMask, 0, 4); + memset(_dhcpGatewayIp, 0, 4); + memset(_dhcpDhcpServerIp, 0, 4); + memset(_dhcpDnsServerIp, 0, 4); } //return:0 on error, 1 if request is sent and response is received @@ -201,6 +204,7 @@ void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) // OPT - host name buffer[16] = hostName; + if (_hostname == nullptr) { buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address strcpy((char*)&(buffer[18]), HOST_NAME); @@ -210,6 +214,12 @@ void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) //put data in W5100 transmit buffer _dhcpUdpSocket.write(buffer, 30); + } else { + uint8_t len = strlen(_hostname); + buffer[17] = len; + _dhcpUdpSocket.write(buffer, 18); + _dhcpUdpSocket.write(_hostname, len); + } if(messageType == DHCP_REQUEST) { @@ -465,6 +475,10 @@ IPAddress DhcpClass::getDnsServerIp() return IPAddress(_dhcpDnsServerIp); } +void DhcpClass::setHostname(const char* hostname) { + _hostname = hostname; +} + void DhcpClass::printByte(char * buf, uint8_t n ) { char *str = &buf[1]; buf[0]='0'; diff --git a/src/Dhcp.h b/src/Dhcp.h index 35760f8..ec99072 100644 --- a/src/Dhcp.h +++ b/src/Dhcp.h @@ -141,11 +141,20 @@ class DhcpClass { uint32_t _dhcpInitialTransactionId; uint32_t _dhcpTransactionId; uint8_t _dhcpMacAddr[6]; + const char* _hostname = nullptr; +#ifdef __arm__ + uint8_t _dhcpLocalIp[4] __attribute__((aligned(4))); + uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4))); + uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4))); + uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4))); +#else uint8_t _dhcpLocalIp[4]; uint8_t _dhcpSubnetMask[4]; uint8_t _dhcpGatewayIp[4]; uint8_t _dhcpDhcpServerIp[4]; uint8_t _dhcpDnsServerIp[4]; +#endif uint32_t _dhcpLeaseTime; uint32_t _dhcpT1, _dhcpT2; signed long _renewInSec; @@ -173,6 +182,8 @@ class DhcpClass { int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); int checkLease(); + + void setHostname(const char* hostname); }; #endif diff --git a/src/Ethernet.cpp b/src/Ethernet.cpp index 919003d..df1c77f 100644 --- a/src/Ethernet.cpp +++ b/src/Ethernet.cpp @@ -19,6 +19,7 @@ #include #include "Ethernet.h" +#include "Dns.h" #include "utility/Enc28J60Network.h" extern "C" @@ -40,6 +41,7 @@ IPAddress UIPEthernetClass::_dnsServerAddress; DhcpClass* UIPEthernetClass::_dhcp(NULL); unsigned long UIPEthernetClass::periodic_timer; +unsigned long UIPEthernetClass::arp_timer; // Because uIP isn't encapsulated within a class we have to use global // variables, so we can only have one TCP/IP stack per program. @@ -54,11 +56,21 @@ void UIPEthernetClass::init(uint8_t csPin) } #if UIP_UDP +void +UIPEthernetClass::setHostname(const char* hostname) +{ + if (_dhcp == NULL) { + _dhcp = new DhcpClass(); + } + _dhcp->setHostname(hostname); +} + int UIPEthernetClass::begin(const uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) { - static DhcpClass s_dhcp; - _dhcp = &s_dhcp; + if (_dhcp == NULL) { + _dhcp = new DhcpClass(); + } // Initialise the basic info init(mac); @@ -105,7 +117,29 @@ UIPEthernetClass::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddre configure(ip,dns,gateway,subnet); } +void UIPEthernetClass::end() +{ + //close all clients + for (int i = 0; i < UIP_CONNS; i++) + { + if (EthernetClient::all_data[i].state) { + EthernetClient client(&EthernetClient::all_data[i]); + client.stop(); + } + } + // handle clients closings + uint32_t st = millis(); + while (millis() - st < 3000) + { + tick(); + } + initialized = false; + configure(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); +} + int UIPEthernetClass::maintain(){ + if (!initialized) + return 0; tick(); int rc = DHCP_CHECK_NONE; #if UIP_UDP @@ -144,6 +178,12 @@ EthernetHardwareStatus UIPEthernetClass::hardwareStatus() return EthernetENC28J60; } +uint8_t* UIPEthernetClass::macAddress(uint8_t* mac) +{ + memcpy(mac, uip_ethaddr.addr, 6); + return mac; +} + IPAddress UIPEthernetClass::localIP() { IPAddress ret; @@ -173,11 +213,34 @@ IPAddress UIPEthernetClass::dnsServerIP() return _dnsServerAddress; } +IPAddress UIPEthernetClass::dnsIP(int n) { + return (n == 0) ? _dnsServerAddress : IPAddress(); +} + +int UIPEthernetClass::hostByName(const char* hostname, IPAddress& result) +{ + // Look up the host first + int ret = 0; +#if UIP_UDP + DNSClient dns; + dns.begin(_dnsServerAddress); + ret = dns.getHostByName(hostname, result); +#endif + return ret; +} + void UIPEthernetClass::tick() { if (!initialized) return; + + // run ARP table cleanup every 10 seconds as required by a comment in uip_arp.h + if (millis() - arp_timer > 10000) { + arp_timer = millis(); + uip_arp_timer(); + } + if (in_packet == NOBLOCK) { in_packet = Enc28J60Network::receivePacket(); @@ -488,3 +551,9 @@ uip_udpchksum(void) #endif UIPEthernetClass Ethernet; + +extern "C" void serialPrint(int i); +void serialPrint(int i) { + Serial.println(i); + Serial.flush(); +} diff --git a/src/Ethernet.h b/src/Ethernet.h index 62d2c35..3f9ddbf 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -85,6 +85,8 @@ class UIPEthernetClass void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway); void begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); + void end(); + // maintain() must be called at regular intervals to process the incoming serial // data and issue IP events to the sketch. It does not return until all IP // events have been processed. Renews dhcp-lease if required. @@ -92,11 +94,22 @@ class UIPEthernetClass EthernetLinkStatus linkStatus(); EthernetHardwareStatus hardwareStatus(); + + uint8_t* macAddress(uint8_t* mac); + void MACAddress(uint8_t *mac_address) { macAddress(mac_address); } IPAddress localIP(); IPAddress subnetMask(); IPAddress gatewayIP(); IPAddress dnsServerIP(); + IPAddress dnsIP(int n = 0); + + void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; } + void setDNS(IPAddress dns_server) { _dnsServerAddress = dns_server; } + + void setHostname(const char* hostname); // only the pointer is stored! + + int hostByName(const char* hostname, IPAddress& result); private: static bool initialized; @@ -109,6 +122,7 @@ class UIPEthernetClass static DhcpClass* _dhcp; static unsigned long periodic_timer; + static unsigned long arp_timer; static void init(const uint8_t* mac); static void configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet); diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp index 30a4ce3..636bc1d 100644 --- a/src/EthernetClient.cpp +++ b/src/EthernetClient.cpp @@ -25,7 +25,6 @@ extern "C" } #include "Ethernet.h" #include "EthernetClient.h" -#include "Dns.h" #define UIP_TCP_PHYH_LEN UIP_LLH_LEN+UIP_IPTCPH_LEN @@ -53,7 +52,7 @@ UIPClient::connect(IPAddress ip, uint16_t port) if (conn) { #if UIP_CONNECT_TIMEOUT > 0 - uint32_t timeout = millis() + 1000 * UIP_CONNECT_TIMEOUT; + uint32_t timeout = millis() + connectTimeout; #endif while((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) { @@ -87,11 +86,8 @@ UIPClient::connect(const char *host, uint16_t port) // Look up the host first int ret = 0; #if UIP_UDP - DNSClient dns; IPAddress remote_addr; - - dns.begin(UIPEthernetClass::_dnsServerAddress); - ret = dns.getHostByName(host, remote_addr); + ret = Ethernet.hostByName(host, remote_addr); if (ret == 1) { return connect(remote_addr, port); } @@ -170,7 +166,7 @@ UIPClient::write(const uint8_t *buf, size_t size) UIPEthernetClass::tick(); if (u && u->state && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) { - uint8_t p = _currentBlock(u->packets_out, u->out_pos); + uint8_t p = _currentBlock(u->packets_out); if (u->packets_out[p] == NOBLOCK) { newpacket: @@ -240,7 +236,7 @@ UIPClient::availableForWrite() UIPEthernetClass::tick(); if (data->packets_out[0] == NOBLOCK) return MAX_AVAILABLE; - uint8_t p = _currentBlock(data->packets_out, data->out_pos); + uint8_t p = _currentBlock(data->packets_out); int used = UIP_SOCKET_DATALEN * p + data->out_pos; return MAX_AVAILABLE - used; } @@ -369,7 +365,13 @@ uint16_t UIPClient::remotePort(void) { return data ? ntohs(uip_conns[data->conn_index].rport) : 0; -} +} + +uint8_t +UIPClient::status() +{ + return !data ? UIP_CLOSED : uip_conns[data->conn_index].tcpstateflags & UIP_TS_MASK; +} void uipclient_appcall(void) @@ -532,9 +534,9 @@ UIPClient::_allocateData() uip_userdata_t* data = &UIPClient::all_data[sock]; if (!data->state) { - data->conn_index = uip_conn - uip_conns; // pointer arithmetics - data->state = UIP_CLIENT_CONNECTED; - memset(&data->packets_in[0],0,sizeof(uip_userdata_t)-sizeof(data->state)); + *data = uip_userdata_t(); + data->conn_index = uip_conn - uip_conns; // pointer arithmetics + data->state = UIP_CLIENT_CONNECTED; return data; } } @@ -542,18 +544,12 @@ UIPClient::_allocateData() } uint8_t -UIPClient::_currentBlock(memhandle* block, uint16_t out_pos) +UIPClient::_currentBlock(memhandle* block) { - if (block[0] == NOBLOCK) - return 0; for (uint8_t i = 1; i < UIP_SOCKET_NUMPACKETS; i++) { if (block[i] == NOBLOCK) - { - if (out_pos == Enc28J60Network::blockSize(block[i-1])) // block is full - return i; return i-1; - } } return UIP_SOCKET_NUMPACKETS-1; } @@ -604,10 +600,22 @@ void UIPClient::_dumpAllData() { for (uint8_t i=0; i < UIP_CONNS; i++) { + if (!all_data[i].state) + continue; Serial.print(F("UIPClient::all_data[")); Serial.print(i); Serial.print(F("], state:")); Serial.println(all_data[i].state, BIN); + struct uip_conn& conn = uip_conns[all_data[i].conn_index]; + Serial.println(ip_addr_uip(conn.ripaddr)); + Serial.print(F("ix: ")); + Serial.print(all_data[i].conn_index); + Serial.print(F(" tcp flags: 0x")); + Serial.print(conn.tcpstateflags, HEX); + Serial.print(F(" retransmission: timer ")); + Serial.print(conn.timer); + Serial.print(F(" nrtx ")); + Serial.println(conn.nrtx); Serial.print(F("packets_in: ")); for (uint8_t j=0; j < UIP_SOCKET_NUMPACKETS; j++) { @@ -632,6 +640,24 @@ UIPClient::_dumpAllData() { Serial.print(F("out_pos: ")); Serial.println(all_data[i].out_pos); } + Serial.println(); + } + for (uint8_t i=0; i < UIP_CONNS; i++) + { + struct uip_conn& conn = uip_conns[i]; + Serial.print(i); + Serial.print(' '); + Serial.print(ip_addr_uip(conn.ripaddr)); + Serial.print(':'); + Serial.print(ntohs(conn.rport)); + Serial.print(' '); + Serial.print(ntohs(conn.lport)); + Serial.print(F(" tcp flags: 0x")); + Serial.print(conn.tcpstateflags, HEX); + Serial.print(F(" retransmission: timer ")); + Serial.print(conn.timer); + Serial.print(F(" nrtx ")); + Serial.println(conn.nrtx); } } #endif diff --git a/src/EthernetClient.h b/src/EthernetClient.h index 324cfac..8807ece 100644 --- a/src/EthernetClient.h +++ b/src/EthernetClient.h @@ -77,17 +77,22 @@ class EthernetClient : public Client { IPAddress remoteIP(); uint16_t remotePort(); + + uint8_t status(); + + void setConnectionTimeout(uint16_t millis) {connectTimeout = millis;} private: EthernetClient(struct uip_conn *_conn); EthernetClient(uip_userdata_t* conn_data); uip_userdata_t* data; + uint16_t connectTimeout = 1000 * UIP_CONNECT_TIMEOUT; static uip_userdata_t all_data[UIP_CONNS]; static uip_userdata_t* _allocateData(); - static uint8_t _currentBlock(memhandle* blocks, uint16_t out_pos); + static uint8_t _currentBlock(memhandle* blocks); static void _eatBlock(memhandle* blocks); static void _flushBlocks(memhandle* blocks); diff --git a/src/EthernetServer.cpp b/src/EthernetServer.cpp index 4a51a4f..9237ad9 100644 --- a/src/EthernetServer.cpp +++ b/src/EthernetServer.cpp @@ -64,6 +64,12 @@ void UIPServer::begin() listening = true; } +void UIPServer::begin(uint16_t port) +{ + _port = port; + begin(); +} + void UIPServer::end() { uip_unlisten(_port); listening = false; diff --git a/src/EthernetServer.h b/src/EthernetServer.h index 1dae460..fec7006 100644 --- a/src/EthernetServer.h +++ b/src/EthernetServer.h @@ -24,10 +24,11 @@ class EthernetServer { public: - EthernetServer(uint16_t); + EthernetServer(uint16_t port = 80); EthernetClient available(); EthernetClient accept(); void begin(); + void begin(uint16_t port); void end(); operator bool(); @@ -42,7 +43,7 @@ class EthernetServer { class EthernetServerPrint : public EthernetServer, public Print { public: - EthernetServerPrint(uint16_t port) : EthernetServer(port) {} + EthernetServerPrint(uint16_t port = 80) : EthernetServer(port) {} virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buf, size_t size); diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp index bbfd2ae..979037b 100644 --- a/src/EthernetUdp.cpp +++ b/src/EthernetUdp.cpp @@ -19,7 +19,6 @@ #include "Ethernet.h" #include "EthernetUdp.h" -#include "Dns.h" extern "C" { #include "utility/uip-conf.h" @@ -35,9 +34,9 @@ extern "C" { // Constructor UIPUDP::UIPUDP() : - _uip_udp_conn(NULL) + _uip_udp_conn(NULL), + appdata() { - memset(&appdata,0,sizeof(appdata)); } // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use @@ -69,7 +68,7 @@ UIPUDP::stop() Enc28J60Network::freeBlock(appdata.packet_in); _flushBlocks(appdata.packet_next); Enc28J60Network::freeBlock(appdata.packet_out); - memset(&appdata,0,sizeof(appdata)); + appdata = uip_udp_userdata_t(); } } @@ -81,7 +80,7 @@ int UIPUDP::beginPacket(IPAddress ip, uint16_t port) { UIPEthernetClass::tick(); - if (ip && port) + if ((ip[0] || ip[1] || ip[2] || ip[3]) && port) { uip_ipaddr_t ripaddr; uip_ip_addr(&ripaddr, ip); @@ -149,11 +148,8 @@ UIPUDP::beginPacket(const char *host, uint16_t port) { // Look up the host first int ret = 0; - DNSClient dns; IPAddress remote_addr; - - dns.begin(Ethernet.dnsServerIP()); - ret = dns.getHostByName(host, remote_addr); + ret = Ethernet.hostByName(host, remote_addr); if (ret == 1) { return beginPacket(remote_addr, port); } else { diff --git a/src/tcp_states.h b/src/tcp_states.h new file mode 100644 index 0000000..02864da --- /dev/null +++ b/src/tcp_states.h @@ -0,0 +1,21 @@ + +#ifndef UIP_TCP_STATES_H +#define UIP_TCP_STATES_H + +#include + +// common constants for client.state() return values +enum uip_tcp_state { + CLOSED = UIP_CLOSED, + SYN_SENT = UIP_SYN_SENT, + SYN_RCVD = UIP_SYN_RCVD, + ESTABLISHED = UIP_ESTABLISHED, + FIN_WAIT_1 = UIP_FIN_WAIT_1, + FIN_WAIT_2 = UIP_FIN_WAIT_2, + CLOSE_WAIT = 10, // not used + CLOSING = UIP_CLOSING, + LAST_ACK = UIP_LAST_ACK, + TIME_WAIT = UIP_TIME_WAIT +}; + +#endif diff --git a/src/utility/Enc28J60Network.cpp b/src/utility/Enc28J60Network.cpp index 97ff438..065230c 100644 --- a/src/utility/Enc28J60Network.cpp +++ b/src/utility/Enc28J60Network.cpp @@ -302,12 +302,16 @@ Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buf memblock *packet = &blocks[handle]; uint16_t start = packet->begin + position; + if (len > packet->size - position) + len = packet->size - position; + + if (len == 0) + return 0; + SPI.beginTransaction(SPI_ETHERNET_SETTINGS); writeRegPair(EWRPTL, start); - if (len > packet->size - position) - len = packet->size - position; writeBuffer(len, buffer); SPI.endTransaction(); diff --git a/src/utility/uip_arp.c b/src/utility/uip_arp.c index 44ca7c4..86b4df8 100644 --- a/src/utility/uip_arp.c +++ b/src/utility/uip_arp.c @@ -170,8 +170,7 @@ uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr) /* Check if the source IP address of the incoming packet matches the IP address in this ARP table entry. */ - if(ipaddr[0] == tabptr->ipaddr[0] && - ipaddr[1] == tabptr->ipaddr[1]) { + if(memcmp(ipaddr, tabptr->ipaddr, 4) == 0) { /* An old entry found, update this and return. */ memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);