diff --git a/libraries/Ethernet/Dhcp.cpp b/libraries/Ethernet/Dhcp.cpp index 62bbfebc0..e4d27f722 100755 --- a/libraries/Ethernet/Dhcp.cpp +++ b/libraries/Ethernet/Dhcp.cpp @@ -11,13 +11,33 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { - uint8_t dhcp_state = STATE_DHCP_START; - uint8_t messageType = 0; - - // zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp - memset(_dhcpMacAddr, 0, 26); + _dhcpLeaseTime=0; + _dhcpT1=0; + _dhcpT2=0; + _lastCheck=0; + _timeout = timeout; + _responseTimeout = responseTimeout; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + _dhcp_state = STATE_DHCP_START; + return request_DHCP_lease(); +} + +void DhcpClass::reset_DHCP_lease(){ + // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp + memset(_dhcpLocalIp, 0, 20); +} + +//return:0 on error, 1 if request is sent and response is received +int DhcpClass::request_DHCP_lease(){ + + uint8_t messageType = 0; + + // Pick an initial transaction ID _dhcpTransactionId = random(1UL, 2000UL); @@ -35,55 +55,75 @@ int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long unsigned long startTime = millis(); - while(dhcp_state != STATE_DHCP_LEASED) + while(_dhcp_state != STATE_DHCP_LEASED) { - if(dhcp_state == STATE_DHCP_START) + if(_dhcp_state == STATE_DHCP_START) { _dhcpTransactionId++; send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); - dhcp_state = STATE_DHCP_DISCOVER; + _dhcp_state = STATE_DHCP_DISCOVER; } - else if(dhcp_state == STATE_DHCP_DISCOVER) + else if(_dhcp_state == STATE_DHCP_REREQUEST){ + _dhcpTransactionId++; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000)); + _dhcp_state = STATE_DHCP_REQUEST; + } + else if(_dhcp_state == STATE_DHCP_DISCOVER) { uint32_t respId; - messageType = parseDHCPResponse(responseTimeout, respId); + messageType = parseDHCPResponse(_responseTimeout, respId); if(messageType == DHCP_OFFER) { // We'll use the transaction ID that the offer came with, // rather than the one we were up to _dhcpTransactionId = respId; send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); - dhcp_state = STATE_DHCP_REQUEST; + _dhcp_state = STATE_DHCP_REQUEST; } } - else if(dhcp_state == STATE_DHCP_REQUEST) + else if(_dhcp_state == STATE_DHCP_REQUEST) { uint32_t respId; - messageType = parseDHCPResponse(responseTimeout, respId); + messageType = parseDHCPResponse(_responseTimeout, respId); if(messageType == DHCP_ACK) { - dhcp_state = STATE_DHCP_LEASED; + _dhcp_state = STATE_DHCP_LEASED; result = 1; + //use default lease time if we didn't get it + if(_dhcpLeaseTime == 0){ + _dhcpLeaseTime = DEFAULT_LEASE; + } + //calculate T1 & T2 if we didn't get it + if(_dhcpT1 == 0){ + //T1 should be 50% of _dhcpLeaseTime + _dhcpT1 = _dhcpLeaseTime >> 1; + } + if(_dhcpT2 == 0){ + //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime + _dhcpT2 = _dhcpT1 << 1; + } + _renewInSec = _dhcpT1; + _rebindInSec = _dhcpT2; } else if(messageType == DHCP_NAK) - dhcp_state = STATE_DHCP_START; + _dhcp_state = STATE_DHCP_START; } if(messageType == 255) { messageType = 0; - dhcp_state = STATE_DHCP_START; + _dhcp_state = STATE_DHCP_START; } - if(result != 1 && ((millis() - startTime) > timeout)) + if(result != 1 && ((millis() - startTime) > _timeout)) break; } // We're done with the socket now _dhcpUdpSocket.stop(); _dhcpTransactionId++; - + return result; } @@ -302,8 +342,26 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr } } break; - + + case dhcpT1value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); + _dhcpT1 = ntohl(_dhcpT1); + break; + + case dhcpT2value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); + _dhcpT2 = ntohl(_dhcpT2); + break; + case dhcpIPaddrLeaseTime : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); + _dhcpLeaseTime = ntohl(_dhcpLeaseTime); + _renewInSec = _dhcpLeaseTime; + break; + default : opt_len = _dhcpUdpSocket.read(); // Skip over the rest of this option @@ -322,6 +380,68 @@ uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& tr return type; } + +/* + returns: + 0/DHCP_CHECK_NONE: nothing happened + 1/DHCP_CHECK_RENEW_FAIL: renew failed + 2/DHCP_CHECK_RENEW_OK: renew success + 3/DHCP_CHECK_REBIND_FAIL: rebind fail + 4/DHCP_CHECK_REBIND_OK: rebind success +*/ +int DhcpClass::checkLease(){ + //this uses a signed / unsigned trick to deal with millis overflow + unsigned long now = millis(); + signed long snow = (long)now; + int rc=DHCP_CHECK_NONE; + if (_lastCheck != 0){ + signed long factor; + //calc how many ms past the timeout we are + factor = snow - (long)_secTimeout; + //if on or passed the timeout, reduce the counters + if ( factor >= 0 ){ + //next timeout should be now plus 1000 ms minus parts of second in factor + _secTimeout = snow + 1000 - factor % 1000; + //how many seconds late are we, minimum 1 + factor = factor / 1000 +1; + + //reduce the counters by that mouch + //if we can assume that the cycle time (factor) is fairly constant + //and if the remainder is less than cycle time * 2 + //do it early instead of late + if(_renewInSec < factor*2 ) + _renewInSec = 0; + else + _renewInSec -= factor; + + if(_rebindInSec < factor*2 ) + _rebindInSec = 0; + else + _rebindInSec -= factor; + } + + //if we have a lease but should renew, do it + if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ + _dhcp_state = STATE_DHCP_REREQUEST; + rc = 1 + request_DHCP_lease(); + } + + //if we have a lease or is renewing but should bind, do it + if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ + //this should basically restart completely + _dhcp_state = STATE_DHCP_START; + reset_DHCP_lease(); + rc = 3 + request_DHCP_lease(); + } + } + else{ + _secTimeout = snow + 1000; + } + + _lastCheck = now; + return rc; +} + IPAddress DhcpClass::getLocalIp() { return IPAddress(_dhcpLocalIp); diff --git a/libraries/Ethernet/Dhcp.h b/libraries/Ethernet/Dhcp.h index 149876d79..4a47936f0 100755 --- a/libraries/Ethernet/Dhcp.h +++ b/libraries/Ethernet/Dhcp.h @@ -45,6 +45,13 @@ #define MAX_DHCP_OPT 16 #define HOST_NAME "WIZnet" +#define DEFAULT_LEASE (900) //default lease time in seconds + +#define DHCP_CHECK_NONE (0) +#define DHCP_CHECK_RENEW_FAIL (1) +#define DHCP_CHECK_RENEW_OK (2) +#define DHCP_CHECK_REBIND_FAIL (3) +#define DHCP_CHECK_REBIND_OK (4) enum { @@ -139,8 +146,19 @@ private: uint8_t _dhcpGatewayIp[4]; uint8_t _dhcpDhcpServerIp[4]; uint8_t _dhcpDnsServerIp[4]; + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + signed long _renewInSec; + signed long _rebindInSec; + signed long _lastCheck; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _secTimeout; + uint8_t _dhcp_state; EthernetUDP _dhcpUdpSocket; + int request_DHCP_lease(); + void reset_DHCP_lease(); void presend_DHCP(); void send_DHCP_MESSAGE(uint8_t, uint16_t); void printByte(char *, uint8_t); @@ -154,6 +172,7 @@ public: IPAddress getDnsServerIp(); int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); }; #endif diff --git a/libraries/Ethernet/Ethernet.cpp b/libraries/Ethernet/Ethernet.cpp index c298f3d17..5d28f71f9 100644 --- a/libraries/Ethernet/Ethernet.cpp +++ b/libraries/Ethernet/Ethernet.cpp @@ -10,7 +10,8 @@ uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { int EthernetClass::begin(uint8_t *mac_address) { - DhcpClass dhcp; + _dhcp = new DhcpClass(); + // Initialise the basic info W5100.init(); @@ -18,15 +19,15 @@ int EthernetClass::begin(uint8_t *mac_address) W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); // Now try to get our config info from a DHCP server - int ret = dhcp.beginWithDHCP(mac_address); + int ret = _dhcp->beginWithDHCP(mac_address); if(ret == 1) { // We've successfully found a DHCP server and got our configuration info, so set things // accordingly - W5100.setIPAddress(dhcp.getLocalIp().raw_address()); - W5100.setGatewayIp(dhcp.getGatewayIp().raw_address()); - W5100.setSubnetMask(dhcp.getSubnetMask().raw_address()); - _dnsServerAddress = dhcp.getDnsServerIp(); + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); } return ret; @@ -66,6 +67,31 @@ void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server _dnsServerAddress = dns_server; } +int EthernetClass::maintain(){ + int rc = DHCP_CHECK_NONE; + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + //we might have got a new IP. + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +} + IPAddress EthernetClass::localIP() { IPAddress ret; diff --git a/libraries/Ethernet/Ethernet.h b/libraries/Ethernet/Ethernet.h index c916ddae5..2a07ff35f 100644 --- a/libraries/Ethernet/Ethernet.h +++ b/libraries/Ethernet/Ethernet.h @@ -6,12 +6,14 @@ #include "IPAddress.h" #include "EthernetClient.h" #include "EthernetServer.h" +#include "Dhcp.h" #define MAX_SOCK_NUM 4 class EthernetClass { private: IPAddress _dnsServerAddress; + DhcpClass* _dhcp; public: static uint8_t _state[MAX_SOCK_NUM]; static uint16_t _server_port[MAX_SOCK_NUM]; @@ -23,6 +25,7 @@ public: void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + int maintain(); IPAddress localIP(); IPAddress subnetMask();