1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-02-18 12:54:25 +01:00

Merge branch 'dhcp' of github.com:amcewen/Arduino.

This includes DCHP support and new UDP API for the Ethernet library.
This commit is contained in:
David A. Mellis 2011-03-23 23:28:33 -04:00
parent efae89ea0e
commit f43c0918ff
25 changed files with 1021 additions and 191 deletions

View File

@ -16,7 +16,7 @@ uint16_t Client::_srcport = 1024;
Client::Client(uint8_t sock) : _sock(sock) {
}
Client::Client(uint8_t *ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) {
Client::Client(IPAddress& ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) {
}
uint8_t Client::connect() {
@ -38,7 +38,7 @@ uint8_t Client::connect() {
if (_srcport == 0) _srcport = 1024;
socket(_sock, SnMR::TCP, _srcport, 0);
if (!::connect(_sock, _ip, _port)) {
if (!::connect(_sock, _ip.raw_address(), _port)) {
_sock = MAX_SOCK_NUM;
return 0;
}

View File

@ -7,8 +7,8 @@ class Client : public Stream {
public:
Client();
Client(uint8_t);
Client(uint8_t *, uint16_t);
Client(uint8_t sock);
Client(IPAddress& ip, uint16_t port);
uint8_t status();
uint8_t connect();
@ -29,7 +29,7 @@ public:
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
IPAddress _ip;
uint16_t _port;
};

341
libraries/Ethernet/Dhcp.cpp Executable file
View File

@ -0,0 +1,341 @@
// DHCP Library v0.3 - April 25, 2009
// Author: Jordan Terrell - blog.jordanterrell.com
#include "w5100.h"
#include <string.h>
#include <stdlib.h>
#include "Dhcp.h"
#include "Arduino.h"
#include "util.h"
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);
memcpy((void*)_dhcpMacAddr, (void*)mac, 6);
// Pick an initial transaction ID
_dhcpTransactionId = random(1UL, 2000UL);
_dhcpInitialTransactionId = _dhcpTransactionId;
if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0)
{
// Couldn't get a socket
return 0;
}
presend_DHCP();
int result = 0;
unsigned long startTime = millis();
while(dhcp_state != STATE_DHCP_LEASED)
{
if(dhcp_state == STATE_DHCP_START)
{
_dhcpTransactionId++;
send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000));
dhcp_state = STATE_DHCP_DISCOVER;
}
else if(dhcp_state == STATE_DHCP_DISCOVER)
{
uint32_t 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;
}
}
else if(dhcp_state == STATE_DHCP_REQUEST)
{
uint32_t respId;
messageType = parseDHCPResponse(responseTimeout, respId);
if(messageType == DHCP_ACK)
{
dhcp_state = STATE_DHCP_LEASED;
result = 1;
}
else if(messageType == DHCP_NAK)
dhcp_state = STATE_DHCP_START;
}
if(messageType == 255)
{
messageType = 0;
dhcp_state = STATE_DHCP_START;
}
if(result != 1 && ((millis() - startTime) > timeout))
break;
}
// We're done with the socket now
_dhcpUdpSocket.stop();
_dhcpTransactionId++;
return result;
}
void DhcpClass::presend_DHCP()
{
}
void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed)
{
uint8_t buffer[32];
memset(buffer, 0, 32);
IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address
if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT))
{
// FIXME Need to return errors
return;
}
buffer[0] = DHCP_BOOTREQUEST; // op
buffer[1] = DHCP_HTYPE10MB; // htype
buffer[2] = DHCP_HLENETHERNET; // hlen
buffer[3] = DHCP_HOPS; // hops
// xid
unsigned long xid = htonl(_dhcpTransactionId);
memcpy(buffer + 4, &(xid), 4);
// 8, 9 - seconds elapsed
buffer[8] = ((secondsElapsed & 0xff00) >> 8);
buffer[9] = (secondsElapsed & 0x00ff);
// flags
unsigned short flags = htons(DHCP_FLAGSBROADCAST);
memcpy(buffer + 10, &(flags), 2);
// ciaddr: already zeroed
// yiaddr: already zeroed
// siaddr: already zeroed
// giaddr: already zeroed
//put data in W5100 transmit buffer
_dhcpUdpSocket.write(buffer, 28);
memset(buffer, 0, 32); // clear local buffer
memcpy(buffer, _dhcpMacAddr, 6); // chaddr
//put data in W5100 transmit buffer
_dhcpUdpSocket.write(buffer, 16);
memset(buffer, 0, 32); // clear local buffer
// leave zeroed out for sname && file
// put in W5100 transmit buffer x 6 (192 bytes)
for(int i = 0; i < 6; i++) {
_dhcpUdpSocket.write(buffer, 32);
}
// OPT - Magic Cookie
buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF);
buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF);
buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF);
buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF);
// OPT - message type
buffer[4] = dhcpMessageType;
buffer[5] = 0x01;
buffer[6] = messageType; //DHCP_REQUEST;
// OPT - client identifier
buffer[7] = dhcpClientIdentifier;
buffer[8] = 0x07;
buffer[9] = 0x01;
memcpy(buffer + 10, _dhcpMacAddr, 6);
// OPT - host name
buffer[16] = hostName;
buffer[17] = strlen(HOST_NAME) + 3; // length of hostname + last 3 bytes of mac address
strcpy((char*)&(buffer[18]), HOST_NAME);
buffer[24] = _dhcpMacAddr[3];
buffer[25] = _dhcpMacAddr[4];
buffer[26] = _dhcpMacAddr[5];
//put data in W5100 transmit buffer
_dhcpUdpSocket.write(buffer, 27);
if(messageType == DHCP_REQUEST)
{
buffer[0] = dhcpRequestedIPaddr;
buffer[1] = 0x04;
buffer[2] = _dhcpLocalIp[0];
buffer[3] = _dhcpLocalIp[1];
buffer[4] = _dhcpLocalIp[2];
buffer[5] = _dhcpLocalIp[3];
buffer[6] = dhcpServerIdentifier;
buffer[7] = 0x04;
buffer[8] = _dhcpDhcpServerIp[0];
buffer[9] = _dhcpDhcpServerIp[1];
buffer[10] = _dhcpDhcpServerIp[2];
buffer[11] = _dhcpDhcpServerIp[3];
//put data in W5100 transmit buffer
_dhcpUdpSocket.write(buffer, 12);
}
buffer[0] = dhcpParamRequest;
buffer[1] = 0x06;
buffer[2] = subnetMask;
buffer[3] = routersOnSubnet;
buffer[4] = dns;
buffer[5] = domainName;
buffer[6] = dhcpT1value;
buffer[7] = dhcpT2value;
buffer[8] = endOption;
//put data in W5100 transmit buffer
_dhcpUdpSocket.write(buffer, 9);
_dhcpUdpSocket.endPacket();
}
uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId)
{
uint8_t type = 0;
uint8_t opt_len = 0;
unsigned long startTime = millis();
while(_dhcpUdpSocket.parsePacket() <= 0)
{
if((millis() - startTime) > responseTimeout)
{
return 255;
}
delay(50);
}
// start reading in the packet
RIP_MSG_FIXED fixedMsg;
_dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED));
if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT)
{
transactionId = ntohl(fixedMsg.xid);
if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId))
{
// Need to read the rest of the packet here regardless
_dhcpUdpSocket.flush();
return 0;
}
memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4);
// Skip to the option part
// Doing this a byte at a time so we don't have to put a big buffer
// on the stack (as we don't have lots of memory lying around)
for (int i =0; i < (240 - sizeof(RIP_MSG_FIXED)); i++)
{
_dhcpUdpSocket.read(); // we don't care about the returned byte
}
while (_dhcpUdpSocket.available() > 0)
{
switch (_dhcpUdpSocket.read())
{
case endOption :
break;
case padOption :
break;
case dhcpMessageType :
opt_len = _dhcpUdpSocket.read();
type = _dhcpUdpSocket.read();
break;
case subnetMask :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read(_dhcpSubnetMask, 4);
break;
case routersOnSubnet :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read(_dhcpGatewayIp, 4);
break;
case dns :
opt_len = _dhcpUdpSocket.read();
_dhcpUdpSocket.read(_dhcpDnsServerIp, 4);
break;
case dhcpServerIdentifier :
opt_len = _dhcpUdpSocket.read();
if( *((uint32_t*)_dhcpDhcpServerIp) == 0 ||
IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() )
{
_dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp));
}
else
{
// Skip over the rest of this option
while (opt_len--)
{
_dhcpUdpSocket.read();
}
}
break;
case dhcpIPaddrLeaseTime :
default :
opt_len = _dhcpUdpSocket.read();
// Skip over the rest of this option
while (opt_len--)
{
_dhcpUdpSocket.read();
}
break;
}
}
}
// Need to skip to end of the packet regardless here
_dhcpUdpSocket.flush();
return type;
}
IPAddress DhcpClass::getLocalIp()
{
return IPAddress(_dhcpLocalIp);
}
IPAddress DhcpClass::getSubnetMask()
{
return IPAddress(_dhcpSubnetMask);
}
IPAddress DhcpClass::getGatewayIp()
{
return IPAddress(_dhcpGatewayIp);
}
IPAddress DhcpClass::getDhcpServerIp()
{
return IPAddress(_dhcpDhcpServerIp);
}
IPAddress DhcpClass::getDnsServerIp()
{
return IPAddress(_dhcpDnsServerIp);
}

158
libraries/Ethernet/Dhcp.h Executable file
View File

@ -0,0 +1,158 @@
// DHCP Library v0.3 - April 25, 2009
// Author: Jordan Terrell - blog.jordanterrell.com
#ifndef Dhcp_h
#define Dhcp_h
#include "Udp.h"
/* DHCP state machine. */
#define STATE_DHCP_START 0
#define STATE_DHCP_DISCOVER 1
#define STATE_DHCP_REQUEST 2
#define STATE_DHCP_LEASED 3
#define STATE_DHCP_REREQUEST 4
#define STATE_DHCP_RELEASE 5
#define DHCP_FLAGSBROADCAST 0x8000
/* UDP port numbers for DHCP */
#define DHCP_SERVER_PORT 67 /* from server to client */
#define DHCP_CLIENT_PORT 68 /* from client to server */
/* DHCP message OP code */
#define DHCP_BOOTREQUEST 1
#define DHCP_BOOTREPLY 2
/* DHCP message type */
#define DHCP_DISCOVER 1
#define DHCP_OFFER 2
#define DHCP_REQUEST 3
#define DHCP_DECLINE 4
#define DHCP_ACK 5
#define DHCP_NAK 6
#define DHCP_RELEASE 7
#define DHCP_INFORM 8
#define DHCP_HTYPE10MB 1
#define DHCP_HTYPE100MB 2
#define DHCP_HLENETHERNET 6
#define DHCP_HOPS 0
#define DHCP_SECS 0
#define MAGIC_COOKIE 0x63825363
#define MAX_DHCP_OPT 16
#define HOST_NAME "WIZnet"
enum
{
padOption = 0,
subnetMask = 1,
timerOffset = 2,
routersOnSubnet = 3,
/* timeServer = 4,
nameServer = 5,*/
dns = 6,
/*logServer = 7,
cookieServer = 8,
lprServer = 9,
impressServer = 10,
resourceLocationServer = 11,*/
hostName = 12,
/*bootFileSize = 13,
meritDumpFile = 14,*/
domainName = 15,
/*swapServer = 16,
rootPath = 17,
extentionsPath = 18,
IPforwarding = 19,
nonLocalSourceRouting = 20,
policyFilter = 21,
maxDgramReasmSize = 22,
defaultIPTTL = 23,
pathMTUagingTimeout = 24,
pathMTUplateauTable = 25,
ifMTU = 26,
allSubnetsLocal = 27,
broadcastAddr = 28,
performMaskDiscovery = 29,
maskSupplier = 30,
performRouterDiscovery = 31,
routerSolicitationAddr = 32,
staticRoute = 33,
trailerEncapsulation = 34,
arpCacheTimeout = 35,
ethernetEncapsulation = 36,
tcpDefaultTTL = 37,
tcpKeepaliveInterval = 38,
tcpKeepaliveGarbage = 39,
nisDomainName = 40,
nisServers = 41,
ntpServers = 42,
vendorSpecificInfo = 43,
netBIOSnameServer = 44,
netBIOSdgramDistServer = 45,
netBIOSnodeType = 46,
netBIOSscope = 47,
xFontServer = 48,
xDisplayManager = 49,*/
dhcpRequestedIPaddr = 50,
dhcpIPaddrLeaseTime = 51,
/*dhcpOptionOverload = 52,*/
dhcpMessageType = 53,
dhcpServerIdentifier = 54,
dhcpParamRequest = 55,
/*dhcpMsg = 56,
dhcpMaxMsgSize = 57,*/
dhcpT1value = 58,
dhcpT2value = 59,
/*dhcpClassIdentifier = 60,*/
dhcpClientIdentifier = 61,
endOption = 255
};
typedef struct _RIP_MSG_FIXED
{
uint8_t op;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
uint32_t xid;
uint16_t secs;
uint16_t flags;
uint8_t ciaddr[4];
uint8_t yiaddr[4];
uint8_t siaddr[4];
uint8_t giaddr[4];
uint8_t chaddr[6];
}RIP_MSG_FIXED;
class DhcpClass {
private:
uint32_t _dhcpInitialTransactionId;
uint32_t _dhcpTransactionId;
uint8_t _dhcpMacAddr[6];
uint8_t _dhcpLocalIp[4];
uint8_t _dhcpSubnetMask[4];
uint8_t _dhcpGatewayIp[4];
uint8_t _dhcpDhcpServerIp[4];
uint8_t _dhcpDnsServerIp[4];
UDP _dhcpUdpSocket;
void presend_DHCP();
void send_DHCP_MESSAGE(uint8_t, uint16_t);
uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId);
public:
IPAddress getLocalIp();
IPAddress getSubnetMask();
IPAddress getGatewayIp();
IPAddress getDhcpServerIp();
IPAddress getDnsServerIp();
int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
};
#endif

View File

@ -1,5 +1,6 @@
#include "w5100.h"
#include "Ethernet.h"
#include "Dhcp.h"
// XXX: don't make assumptions about the value of MAX_SOCK_NUM.
uint8_t EthernetClass::_state[MAX_SOCK_NUM] = {
@ -7,30 +8,78 @@ uint8_t EthernetClass::_state[MAX_SOCK_NUM] = {
uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = {
0, 0, 0, 0 };
void EthernetClass::begin(uint8_t *mac, uint8_t *ip)
int EthernetClass::begin(uint8_t *mac_address)
{
uint8_t gateway[4];
gateway[0] = ip[0];
gateway[1] = ip[1];
gateway[2] = ip[2];
DhcpClass dhcp;
// Initialise the basic info
W5100.init();
W5100.setMACAddress(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);
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();
}
return ret;
}
void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip)
{
// Assume the gateway will be the machine on the same network as the local IP
// but with last octet being '1'
IPAddress gateway = local_ip;
gateway[3] = 1;
begin(mac, ip, gateway);
begin(mac_address, local_ip, gateway);
}
void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway)
void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway)
{
uint8_t subnet[] = {
255, 255, 255, 0 };
begin(mac, ip, gateway, subnet);
IPAddress subnet(255, 255, 255, 0);
begin(mac_address, local_ip, gateway, subnet);
}
void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway, uint8_t *subnet)
void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress gateway, IPAddress subnet)
{
W5100.init();
W5100.setMACAddress(mac);
W5100.setIPAddress(ip);
W5100.setGatewayIp(gateway);
W5100.setSubnetMask(subnet);
W5100.setIPAddress(local_ip._address);
W5100.setGatewayIp(gateway._address);
W5100.setSubnetMask(subnet._address);
}
IPAddress EthernetClass::localIP()
{
IPAddress ret;
W5100.getIPAddress(ret.raw_address());
return ret;
}
IPAddress EthernetClass::subnetMask()
{
IPAddress ret;
W5100.getSubnetMask(ret.raw_address());
return ret;
}
IPAddress EthernetClass::gatewayIP()
{
IPAddress ret;
W5100.getGatewayIp(ret.raw_address());
return ret;
}
IPAddress EthernetClass::dnsServerIP()
{
return _dnsServerAddress;
}
EthernetClass Ethernet;

View File

@ -3,6 +3,7 @@
#include <inttypes.h>
//#include "w5100.h"
#include "IPAddress.h"
#include "Client.h"
#include "Server.h"
@ -10,12 +11,23 @@
class EthernetClass {
private:
IPAddress _dnsServerAddress;
public:
static uint8_t _state[MAX_SOCK_NUM];
static uint16_t _server_port[MAX_SOCK_NUM];
void begin(uint8_t *, uint8_t *);
void begin(uint8_t *, uint8_t *, uint8_t *);
void begin(uint8_t *, uint8_t *, uint8_t *, uint8_t *);
// 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
int begin(uint8_t *mac_address);
void begin(uint8_t *mac_address, IPAddress local_ip);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway);
void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway, IPAddress subnet);
IPAddress localIP();
IPAddress subnetMask();
IPAddress gatewayIP();
IPAddress dnsServerIP();
friend class Client;
friend class Server;
};

View File

@ -0,0 +1,44 @@
#include <Arduino.h>
#include <IPAddress.h>
IPAddress::IPAddress()
{
memset(_address, 0, sizeof(_address));
}
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
{
_address[0] = first_octet;
_address[1] = second_octet;
_address[2] = third_octet;
_address[3] = fourth_octet;
}
IPAddress::IPAddress(uint32_t address)
{
memcpy(_address, &address, sizeof(_address));
}
IPAddress::IPAddress(const uint8_t *address)
{
memcpy(_address, address, sizeof(_address));
}
IPAddress& IPAddress::operator=(const uint8_t *address)
{
memcpy(_address, address, sizeof(_address));
return *this;
}
IPAddress& IPAddress::operator=(uint32_t address)
{
memcpy(_address, (const uint8_t *)&address, sizeof(_address));
return *this;
}
bool IPAddress::operator==(const uint8_t* addr)
{
return memcmp(addr, _address, sizeof(_address)) == 0;
}

View File

@ -0,0 +1,68 @@
/*
*
* MIT License:
* Copyright (c) 2011 Adrian McEwen
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* adrianm@mcqn.com 1/1/2011
*/
#ifndef IPAddress_h
#define IPAddress_h
// A class to make it easier to handle and pass around IP addresses
class IPAddress {
private:
uint8_t _address[4]; // IPv4 address
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t* raw_address() { return _address; };
public:
// Constructors
IPAddress();
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
IPAddress(uint32_t address);
IPAddress(const uint8_t *address);
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
// to a four-byte uint8_t array is expected
operator uint32_t() { return *((uint32_t*)_address); };
bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
bool operator==(const uint8_t* addr);
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const { return _address[index]; };
uint8_t& operator[](int index) { return _address[index]; };
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
IPAddress& operator=(const uint8_t *address);
IPAddress& operator=(uint32_t address);
friend class EthernetClass;
friend class UDP;
friend class Client;
friend class Server;
friend class DhcpClass;
};
#endif

View File

@ -56,97 +56,12 @@ uint8_t UDP::begin(uint16_t port) {
return 1;
}
/* Send packet contained in buf of length len to peer at specified ip, and port */
/* Use this function to transmit binary data that might contain 0x00 bytes*/
/* This function returns sent data size for success else -1. */
uint16_t UDP::sendPacket(uint8_t * buf, uint16_t len, uint8_t * ip, uint16_t port){
return sendto(_sock,(const uint8_t *)buf,len,ip,port);
}
/* Send zero-terminated string str as packet to peer at specified ip, and port */
/* This function returns sent data size for success else -1. */
uint16_t UDP::sendPacket(const char str[], uint8_t * ip, uint16_t port){
// compute strlen
const char *s;
for(s = str; *s; ++s);
uint16_t len = (s-str);
// send packet
return sendto(_sock,(const uint8_t *)str,len,ip,port);
}
/* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes.
* returned value includes 8 byte UDP header!*/
int UDP::available() {
return W5100.getRXReceivedSize(_sock);
}
/* Read a received packet into buffer buf (which is of maximum length len); */
/* store calling ip and port as well. Call available() to make sure data is ready first. */
/* NOTE: I don't believe len is ever checked in implementation of recvfrom(),*/
/* so it's easy to overflow buffer. so we check and truncate. */
/* returns number of bytes read, or negative number of bytes we would have needed if we truncated */
int UDP::readPacket(uint8_t * buf, uint16_t bufLen, uint8_t *ip, uint16_t *port) {
int packetLen = available()-8; //skip UDP header;
if(packetLen < 0 ) return 0; // no real data here
if(packetLen > (int)bufLen) {
//packet is too large - truncate
//HACK - hand-parse the UDP packet using TCP recv method
uint8_t tmpBuf[8];
int i;
//read 8 header bytes and get IP and port from it
recv(_sock,tmpBuf,8);
ip[0] = tmpBuf[0];
ip[1] = tmpBuf[1];
ip[2] = tmpBuf[2];
ip[3] = tmpBuf[3];
*port = tmpBuf[4];
*port = (*port << 8) + tmpBuf[5];
//now copy first (bufLen) bytes into buf
for(i=0;i<(int)bufLen;i++) {
recv(_sock,tmpBuf,1);
buf[i]=tmpBuf[0];
}
//and just read the rest byte by byte and throw it away
while(available()) {
recv(_sock,tmpBuf,1);
}
return (-1*packetLen);
//ALTERNATIVE: requires stdlib - takes a bunch of space
/*//create new buffer and read everything into it
uint8_t * tmpBuf = (uint8_t *)malloc(packetLen);
recvfrom(_sock,tmpBuf,packetLen,ip,port);
if(!tmpBuf) return 0; //couldn't allocate
// copy first bufLen bytes
for(unsigned int i=0; i<bufLen; i++) {
buf[i]=tmpBuf[i];
}
//free temp buffer
free(tmpBuf);
*/
}
return recvfrom(_sock,buf,bufLen,ip,port);
}
/* Read a received packet, throw away peer's ip and port. See note above. */
int UDP::readPacket(uint8_t * buf, uint16_t len) {
uint8_t ip[4];
uint16_t port[1];
return recvfrom(_sock,buf,len,ip,port);
}
int UDP::readPacket(char * buf, uint16_t bufLen, uint8_t *ip, uint16_t &port) {
uint16_t myPort;
uint16_t ret = readPacket( (byte*)buf, bufLen, ip, &myPort);
port = myPort;
return ret;
}
/* Release any resources being used by this UDP instance */
void UDP::stop()
{
@ -159,3 +74,93 @@ void UDP::stop()
_sock = MAX_SOCK_NUM;
}
int UDP::beginPacket(IPAddress ip, uint16_t port)
{
_offset = 0;
return startUDP(_sock, ip.raw_address(), port);
}
int UDP::endPacket()
{
return sendUDP(_sock);
}
void UDP::write(uint8_t byte)
{
write(&byte, 1);
}
void UDP::write(const char *str)
{
size_t len = strlen(str);
write((const uint8_t *)str, len);
}
void UDP::write(const uint8_t *buffer, size_t size)
{
uint16_t bytes_written = bufferData(_sock, _offset, buffer, size);
_offset += bytes_written;
}
int UDP::parsePacket()
{
//HACK - hand-parse the UDP packet using TCP recv method
uint8_t tmpBuf[8];
int ret =0;
//read 8 header bytes and get IP and port from it
ret = recv(_sock,tmpBuf,8);
if (ret > 0)
{
_remoteIP = tmpBuf;
_remotePort = tmpBuf[4];
_remotePort = (_remotePort << 8) + tmpBuf[5];
// When we get here, any remaining bytes are the data
ret = available();
}
return ret;
}
int UDP::read()
{
uint8_t byte;
if (recv(_sock, &byte, 1) > 0)
{
// We read things without any problems
return byte;
}
// If we get here, there's no data available
return -1;
}
int UDP::read(unsigned char* buffer, size_t len)
{
/* In the readPacket that copes with truncating packets, the buffer was
filled with this code. Not sure why it loops round reading out a byte
at a time.
int i;
for(i=0;i<(int)bufLen;i++) {
recv(_sock,tmpBuf,1);
buf[i]=tmpBuf[0];
}
*/
return recv(_sock, buffer, len);
}
int UDP::peek()
{
uint8_t b;
// Unlike recv, peek doesn't check to see if there's any data available, so we must
if (!available())
return -1;
::peek(_sock, &b);
return b;
}
void UDP::flush()
{
while (available())
{
read();
}
}

View File

@ -37,28 +37,60 @@
#ifndef udp_h
#define udp_h
#include <Stream.h>
#include <IPAddress.h>
#define UDP_TX_PACKET_MAX_SIZE 24
class UDP {
class UDP : public Stream {
private:
uint8_t _sock; // socket ID for Wiz5100
uint16_t _port; // local port to listen on
IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed
uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed
uint16_t _offset; // offset into the packet being sent
public:
UDP();
UDP(); // Constructor
uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
int available(); // has data been received?
void stop(); // Finish with the UDP socket
// C-style buffer-oriented functions
uint16_t sendPacket(uint8_t *, uint16_t, uint8_t *, uint16_t); //send a packet to specified peer
uint16_t sendPacket(const char[], uint8_t *, uint16_t); //send a string as a packet to specified peer
int readPacket(uint8_t *, uint16_t); // read a received packet
int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port
// readPacket that fills a character string buffer
int readPacket(char *, uint16_t, uint8_t *, uint16_t &);
// Sending UDP packets
// Start building up a packet to send to the remote host specific in ip and port
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
int beginPacket(IPAddress ip, uint16_t port);
// Finish off this packet and send it
// Returns 1 if the packet was sent successfully, 0 if there was an error
int endPacket();
// Write a single byte into the packet
virtual void write(uint8_t);
// Write a string of characters into the packet
virtual void write(const char *str);
// Write size bytes from buffer into the packet
virtual void write(const uint8_t *buffer, size_t size);
// Finish with the UDP socket
void stop();
// Start processing the next available incoming packet
// Returns the size of the packet in bytes, or 0 if no packets are available
int parsePacket();
// Number of bytes remaining in the current packet
virtual int available();
// Read a single byte from the current packet
virtual int read();
// Read up to len bytes from the current packet and place them into buffer
// Returns the number of bytes read, or 0 if none are available
virtual int read(unsigned char* buffer, size_t len);
// Read up to len characters from the current packet and place them into buffer
// Returns the number of characters read, or 0 if none are available
virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); };
// Return the next byte from the current packet without moving on to the next byte
virtual int peek();
virtual void flush(); // Finish reading the current packet
// Return the IP address of the host who sent the current incoming packet
IPAddress remoteIP() { return _remoteIP; };
// Return the port of the host who sent the current incoming packet
uint16_t remotePort() { return _remotePort; };
};
#endif

View File

@ -31,12 +31,9 @@
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,168,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
IPAddress ip(192,168,1,20);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255, 255, 255, 0);
// Initialize the Ethernet server library

View File

@ -24,9 +24,9 @@
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 177 };
byte gateway[] = { 192,168,1, 1 };
byte subnet[] = { 255, 255, 0, 0 };
IPAddress ip(192,168,1, 177);
IPAddress gateway(192,168,1, 1);
IPAddress subnet(255, 255, 0, 0);
// telnet defaults to port 23
Server server(23);
@ -60,4 +60,4 @@ void loop() {
// echo the bytes to the server as well:
Serial.print(thisChar);
}
}
}

View File

@ -23,20 +23,13 @@
#include <Ethernet.h>
// assign a MAC address for the ethernet controller.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
// fill in your address here:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,169,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
// The address of the server you want to connect to (pachube.com):
byte server[] = {
209,40,205,190 };
IPAddress server(209,40,205,190);
// initialize the library instance:
Client client(server, 80);
@ -46,9 +39,15 @@ boolean lastConnected = false; // state of the connection last time through
const int postingInterval = 10000; //delay between updates to Pachube.com
void setup() {
// start the ethernet connection and serial port:
Ethernet.begin(mac, ip);
// start serial port:
Serial.begin(9600);
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
for(;;)
;
}
// give the ethernet module time to boot up:
delay(1000);
}

View File

@ -29,16 +29,12 @@
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
byte ip[] = {
192,169,1,20 };
byte gateway[] = {
192,168,1,1};
byte subnet[] = {
255, 255, 255, 0 };
IPAddress ip(192,169,1,20);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255, 255, 255, 0);
// The address of the server you want to connect to (pachube.com):
byte server[] = {
209,40,205,190 };
IPAddress server(209,40,205,190);
// initialize the library instance:
Client client(server, 80);

View File

@ -24,12 +24,10 @@
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
IPAddress ip(192,168,1,177);
// Enter the IP address of the server you're connecting to:
byte server[] = {
1,1,1,1 };
IPAddress server(1,1,1,1);
// Initialize the Ethernet client library
// with the IP address and port of the server

View File

@ -22,15 +22,10 @@
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
IPAddress ip(192, 168, 1, 177);
unsigned int localPort = 8888; // local port to listen on
// the next two variables are set when a packet is received
byte remoteIp[4]; // holds received packet's originating IP
unsigned int remotePort; // holds received packet's originating port
// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged"; // a string to send back
@ -48,19 +43,33 @@ void setup() {
void loop() {
// if there's data available, read a packet
int packetSize = Udp.available(); // note that this includes the UDP header
int packetSize = Udp.parsePacket();
if(packetSize)
{
packetSize = packetSize - 8; // subtract the 8 byte header
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remote = Udp.remoteIP();
for (int i =0; i < 4; i++)
{
Serial.print(remote[i], DEC);
if (i < 3)
{
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(Udp.remotePort());
// read the packet into packetBufffer and get the senders IP addr and port number
Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort);
// read the packet into packetBufffer
Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
Serial.println("Contents:");
Serial.println(packetBuffer);
Udp.sendPacket( ReplyBuffer, remoteIp, remotePort);
// send a reply, to the IP address and port that sent us the packet we received
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
}
delay(10);
}

View File

@ -20,18 +20,14 @@
#include <Ethernet.h>
#include <Udp.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
unsigned int localPort = 8888; // local port to listen for UDP packets
byte timeServer[] = {
192, 43, 244, 18}; // time.nist.gov NTP server
IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
@ -42,11 +38,16 @@ UDP Udp;
void setup()
{
// start Ethernet and UDP
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
// start Ethernet and UDP
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
for(;;)
;
}
Udp.begin(localPort);
}
void loop()
@ -55,8 +56,9 @@ void loop()
// wait to see if a reply is available
delay(1000);
if ( Udp.available() ) {
Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
if ( Udp.parsePacket() ) {
// We've received a packet, read the data from it
Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
@ -100,7 +102,7 @@ void loop()
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
unsigned long sendNTPpacket(IPAddress& address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
@ -118,7 +120,9 @@ unsigned long sendNTPpacket(byte *address)
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE, address, 123); //NTP requests are to port 123
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer,NTP_PACKET_SIZE);
Udp.endPacket();
}

View File

@ -15,11 +15,10 @@
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1,177 };
byte server[] = { 173,194,33,104 }; // Google
IPAddress server(173,194,33,104); // Google
// Initialize the Ethernet client library
// with the IP address and port of the server
@ -27,10 +26,15 @@ byte server[] = { 173,194,33,104 }; // Google
Client client(server, 80);
void setup() {
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
for(;;)
;
}
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");

View File

@ -21,7 +21,7 @@
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 177 };
IPAddress ip(192,168,1, 177);
// Initialize the Ethernet server library
// with the IP address and port you want to use
@ -79,4 +79,4 @@ void loop()
// close the connection:
client.stop();
}
}
}

View File

@ -9,6 +9,7 @@
Ethernet KEYWORD1
Client KEYWORD1
Server KEYWORD1
IPAddress KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -19,10 +20,16 @@ connect KEYWORD2
write KEYWORD2
available KEYWORD2
read KEYWORD2
peek KEYWORD2
flush KEYWORD2
stop KEYWORD2
connected KEYWORD2
begin KEYWORD2
beginPacket KEYWORD2
endPacket KEYWORD2
parsePacket KEYWORD2
remoteIP KEYWORD2
remotePort KEYWORD2
#######################################
# Constants (LITERAL1)

13
libraries/Ethernet/util.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef UTIL_H
#define UTIL_H
#define htons(x) ( (x)<<8 | ((x)>>8)&0xFF )
#define ntohs(x) htons(x)
#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
((x)<< 8 & 0x00FF0000UL) | \
((x)>> 8 & 0x0000FF00UL) | \
((x)>>24 & 0x000000FFUL) )
#define ntohl(x) htonl(x)
#endif

View File

@ -344,3 +344,58 @@ uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len)
return ret;
}
uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len)
{
uint16_t ret =0;
if (len > W5100.getTXFreeSize(s))
{
ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size.
}
else
{
ret = len;
}
W5100.send_data_processing_offset(s, offset, buf, ret);
return ret;
}
int startUDP(SOCKET s, uint8_t* addr, uint16_t port)
{
if
(
((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
((port == 0x00))
)
{
return 0;
}
else
{
W5100.writeSnDIPR(s, addr);
W5100.writeSnDPORT(s, port);
return 1;
}
}
int sendUDP(SOCKET s)
{
W5100.execCmdSn(s, Sock_SEND);
/* +2008.01 bj */
while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK )
{
if (W5100.readSnIR(s) & SnIR::TIMEOUT)
{
/* +2008.01 [bj]: clear interrupt */
W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
return 0;
}
}
/* +2008.01 bj */
W5100.writeSnIR(s, SnIR::SEND_OK);
/* Sent ok */
return 1;
}

View File

@ -16,5 +16,26 @@ extern uint16_t recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr,
extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len);
// Functions to allow buffered UDP send (i.e. where the UDP datagram is built up over a
// number of calls before being sent
/*
@brief This function 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 1 if the datagram was successfully set up, or 0 if there was an error
*/
extern int startUDP(SOCKET s, uint8_t* addr, uint16_t port);
/*
@brief This function copies 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
*/
uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len);
/*
@brief Send a UDP datagram built up from a sequence of startUDP followed by one or more
calls to bufferData.
@return 1 if the datagram was successfully sent, or 0 if there was an error
*/
int sendUDP(SOCKET s);
#endif
/* _SOCKET_H_ */

View File

@ -65,10 +65,16 @@ uint16_t W5100Class::getRXReceivedSize(SOCKET s)
}
void W5100Class::send_data_processing(SOCKET s, uint8_t *data, uint16_t len)
void W5100Class::send_data_processing(SOCKET s, const uint8_t *data, uint16_t len)
{
// This is same as having no offset in a call to send_data_processing_offset
send_data_processing_offset(s, 0, data, len);
}
void W5100Class::send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len)
{
uint16_t ptr = readSnTX_WR(s);
ptr += data_offset;
uint16_t offset = ptr & SMASK;
uint16_t dstAddr = offset + SBASE[s];
@ -132,7 +138,7 @@ uint8_t W5100Class::write(uint16_t _addr, uint8_t _data)
return 1;
}
uint16_t W5100Class::write(uint16_t _addr, uint8_t *_buf, uint16_t _len)
uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len)
{
for (int i=0; i<_len; i++)
{

View File

@ -146,7 +146,19 @@ public:
* This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer
* register. User should read upper byte first and lower byte later to get proper value.
*/
void send_data_processing(SOCKET s, uint8_t *data, uint16_t len);
void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len);
/**
* @brief A copy of send_data_processing that uses the provided ptr for the
* write offset. Only needed for the "streaming" UDP API, where
* a single UDP packet is built up over a number of calls to
* send_data_processing_ptr, because TX_WR doesn't seem to get updated
* correctly in those scenarios
* @param ptr value to use in place of TX_WR. If 0, then the value is read
* in from TX_WR
* @return New value for ptr, to be used in the next call
*/
// FIXME Update documentation
void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len);
/**
* @brief This function is being called by recv() also.
@ -182,7 +194,7 @@ public:
// ---------------
private:
static uint8_t write(uint16_t _addr, uint8_t _data);
static uint16_t write(uint16_t addr, uint8_t *buf, uint16_t len);
static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len);
static uint8_t read(uint16_t addr);
static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);