mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-01 12:24:14 +01:00
Adding Ethernet.maintain() to update DHCP leases (Peter Magnusson)
returns: 0: nothing happened 1: renew failed 2: renew success 3: rebind fail 4: rebind success http://code.google.com/p/arduino/issues/detail?id=716
This commit is contained in:
parent
a6eb9ea5eb
commit
9f3438c189
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user