Czy poniższy przykład działa u Ciebie dobrze? Nie rozumiem co napisałeś kolego. Z Twojego postu nie wynika jakiej bibloteki do obsługi Ethernetu używasz, nie jest też jasne jaki modułu LAN wykorzystujesz. Domyślam się, że używasz standardowej bibloteki Ethernet i modułu "arduino ethernet shield". Ja mam do czynienia z "MINI ENC28J60" z którą standardowa biblioteka nie działa, działa natomiast bibloteka z załącznika, ściągnięta ze strony: "https://github.com/jcw/ethercard/" Problem rozwiązany.
// DHCP look-up functions based on the udp client
// http://www.ietf.org/rfc/rfc2131.txt
//
// Author: Andrew Lindsay
// Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/
//
// Rewritten dhcpStateMachine by Chris van den Hooven
// as to implement dhcp-renew when lease expires (jun 2012)
//
// Various modifications and bug fixes contributed by Victor Aprea (oct 2012)
//
// Copyright: GPL V2
// See http://www.gnu.org/licenses/gpl.html
//#define DHCPDEBUG
#include " EtherCard.h "
#include " net.h "
#define gPB ether.buffer
#define DHCP_BOOTREQUEST 1
#define DHCP_BOOTRESPONSE 2
// DHCP Message Type (option 53) (ref RFC 2132)
#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
// DHCP States for access in applications (ref RFC 2131)
enum {
DHCP_STATE_INIT,
DHCP_STATE_SELECTING,
DHCP_STATE_REQUESTING,
DHCP_STATE_BOUND,
DHCP_STATE_RENEWING,
};
/*
op 1 Message op code / message type.
1 = BOOTREQUEST, 2 = BOOTREPLY
htype 1 Hardware address type, see ARP section in " Assigned
Numbers " RFC; e.g., '1' = 10mb ethernet.
hlen 1 Hardware address length (e.g. '6' for 10mb
ethernet).
hops 1 Client sets to zero, optionally used by relay agents
when booting via a relay agent.
xid 4 Transaction ID, a random number chosen by the
client, used by the client and server to associate
messages and responses between a client and a
server.
secs 2 Filled in by client, seconds elapsed since client
began address acquisition or renewal process.
flags 2 Flags (see figure 2).
ciaddr 4 Client IP address; only filled in if client is in
BOUND, RENEW or REBINDING state and can respond
to ARP requests.
yiaddr 4 'your' (client) IP address.
siaddr 4 IP address of next server to use in bootstrap;
returned in DHCPOFFER, DHCPACK by server.
giaddr 4 Relay agent IP address, used in booting via a
relay agent.
chaddr 16 Client hardware address.
sname 64 Optional server host name, null terminated string.
file 128 Boot file name, null terminated string; " generic "
name or null in DHCPDISCOVER, fully qualified
directory-path name in DHCPOFFER.
options var Optional parameters field. See the options
documents for a list of defined options.
*/
// size 236
typedef struct {
byte op, htype, hlen, hops;
uint32_t xid;
uint16_t secs, flags;
byte ciaddr[4], yiaddr[4], siaddr[4], giaddr[4];
byte chaddr[16], sname[64], file[128];
// options
} DHCPdata;
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
// timeouts im ms
#define DHCP_REQUEST_TIMEOUT 10000
#define DHCP_HOSTNAME_MAX_LEN 32
// RFC 2132 Section 3.3:
// The time value of 0xffffffff is reserved to represent " infinity " .
#define DHCP_INFINITE_LEASE 0xffffffff
static byte dhcpState = DHCP_STATE_INIT;
static char hostname[DHCP_HOSTNAME_MAX_LEN] = " Arduino-00 " ;
static uint32_t currentXid;
static uint32_t stateTimer;
static uint32_t leaseStart;
static uint32_t leaseTime;
static byte* bufPtr;
static uint8_t dhcpCustomOptionNum = 0;
static DhcpOptionCallback dhcpCustomOptionCallback = NULL;
extern uint8_t allOnes[];// = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static void addToBuf (byte b) {
*bufPtr++ = b;
}
static void addBytes (byte len, const byte* data) {
while (len-- & gt; 0)
addToBuf(*data++);
}
// Main DHCP sending function
// implemented
// state / msgtype
// INIT / DHCPDISCOVER
// SELECTING / DHCPREQUEST
// BOUND (RENEWING) / DHCPREQUEST
// ----------------------------------------------------------
// | |SELECTING |RENEWING |INIT |
// ----------------------------------------------------------
// |broad/unicast |broadcast |unicast |broadcast |
// |server-ip |MUST |MUST NOT |MUST NOT | option 54
// |requested-ip |MUST |MUST NOT |MUST NOT | option 50
// |ciaddr |zero |IP address |zero |
// ----------------------------------------------------------
// options used (both send/receive)
// 12 Host Name Option
// 50 Requested IP Address
// 51 IP Address Lease Time
// 53 DHCP message type
// 54 Server-identifier
// 55 Parameter request list
// 58 Renewal (T1) Time Value
// 61 Client-identifier
// 255 End
static void send_dhcp_message(uint8_t *requestip) {
memset(gPB, 0, UDP_DATA_P + sizeof( DHCPdata ));
EtherCard::udpPrepare(DHCP_CLIENT_PORT,
(dhcpState == DHCP_STATE_BOUND ? EtherCard::dhcpip : allOnes),
DHCP_SERVER_PORT);
// If we ever don't do this, the DHCP renewal gets sent to whatever random
// destmacaddr was used by other code. Rather than cache the MAC address of
// the DHCP server, just force a broadcast here in all cases.
EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); //force broadcast mac
// Build DHCP Packet from buf[UDP_DATA_P]
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
dhcpPtr- & gt; op = DHCP_BOOTREQUEST;
dhcpPtr- & gt; htype = 1;
dhcpPtr- & gt; hlen = 6;
dhcpPtr- & gt; xid = currentXid;
if (dhcpState == DHCP_STATE_BOUND) {
EtherCard::copyIp(dhcpPtr- & gt; ciaddr, EtherCard::myip);
}
EtherCard::copyMac(dhcpPtr- & gt; chaddr, EtherCard::mymac);
// options defined as option, length, value
bufPtr = gPB + UDP_DATA_P + sizeof( DHCPdata );
// DHCP magic cookie, followed by message type
static byte cookie[] = { 99, 130, 83, 99, 53, 1 };
addBytes(sizeof cookie, cookie);
// addToBuf(53); // DHCP_STATE_SELECTING, DHCP_STATE_REQUESTING
// addToBuf(1); // Length
addToBuf(dhcpState == DHCP_STATE_INIT ? DHCP_DISCOVER : DHCP_REQUEST);
// Client Identifier Option, this is the client mac address
addToBuf(61); // Client identifier
addToBuf(7); // Length
addToBuf(0x01); // Ethernet
addBytes(6, EtherCard::mymac);
addToBuf(12); // Host name Option
addToBuf(10);
addBytes(10, (byte*) hostname);
if (requestip != NULL) {
addToBuf(50); // Request IP address
addToBuf(4);
addBytes(4, requestip);
addToBuf(54); // DHCP Server IP address
addToBuf(4);
addBytes(4, EtherCard::dhcpip);
}
// Additional info in parameter list - minimal list for what we need
byte len = 3;
if (dhcpCustomOptionNum)
len++;
addToBuf(55); // Parameter request list
addToBuf(len); // Length
addToBuf(1); // Subnet mask
addToBuf(3); // Route/Gateway
addToBuf(6); // DNS Server
if (dhcpCustomOptionNum)
addToBuf(dhcpCustomOptionNum); // Custom option
addToBuf(255); // end option
// packet size will be under 300 bytes
EtherCard::udpTransmit((bufPtr - gPB) - UDP_DATA_P);
}
static void process_dhcp_offer(uint16_t len, uint8_t *offeredip) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
// Offered IP address is in yiaddr
EtherCard::copyIp(offeredip, dhcpPtr- & gt; yiaddr);
// Search for the
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
if (option == 54) {
EtherCard::copyIp(EtherCard::dhcpip, ptr);
break;
}
ptr += optionLen;
} while (ptr & lt; gPB + len);
}
static void process_dhcp_ack(uint16_t len) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
// Allocated IP address is in yiaddr
EtherCard::copyIp(EtherCard::myip, dhcpPtr- & gt; yiaddr);
// Scan through variable length option list identifying options we want
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
bool done = false;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
switch (option) {
case 1:
EtherCard::copyIp(EtherCard::netmask, ptr);
break;
case 3:
EtherCard::copyIp(EtherCard::gwip, ptr);
break;
case 6:
EtherCard::copyIp(EtherCard::dnsip, ptr);
break;
case 51:
case 58:
leaseTime = 0; // option 58 = Renewal Time, 51 = Lease Time
for (byte i = 0; i & lt; 4; i++)
leaseTime = (leaseTime & lt; & lt; 8) + ptr[i];
if (leaseTime != DHCP_INFINITE_LEASE) {
leaseTime *= 1000; // milliseconds
}
break;
case 255:
done = true;
break;
default: {
// Is is a custom configured option?
if (dhcpCustomOptionCallback & & option == dhcpCustomOptionNum) {
dhcpCustomOptionCallback(option, ptr, optionLen);
}
}
}
ptr += optionLen;
} while (!done & & ptr & lt; gPB + len);
}
static bool dhcp_received_message_type (uint16_t len, byte msgType) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
if (len & gt; = 70 & & gPB[UDP_SRC_PORT_L_P] == DHCP_SERVER_PORT & &
dhcpPtr- & gt; xid == currentXid ) {
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
if(option == 53 & & *ptr == msgType ) {
// DHCP Message type match found
return true;
}
ptr += optionLen;
} while (ptr & lt; gPB + len);
}
return false;
}
static char toAsciiHex(byte b) {
char c = b & 0x0f;
c += (c & lt; = 9) ? '0' : 'A'-10;
return c;
}
bool EtherCard::dhcpSetup (const char *hname, bool fromRam) {
// Use during setup, as this discards all incoming requests until it returns.
// That shouldn't be a problem, because we don't have an IP-address yet.
// Will try 60 secs to obtain DHCP-lease.
using_dhcp = true;
if(hname != NULL) {
if(fromRam) {
strncpy(hostname, hname, DHCP_HOSTNAME_MAX_LEN);
}
else {
strncpy_P(hostname, hname, DHCP_HOSTNAME_MAX_LEN);
}
}
else {
// Set a unique hostname, use Arduino-?? with last octet of mac address
hostname[8] = toAsciiHex(mymac[5] & gt; & gt; 4);
hostname[9] = toAsciiHex(mymac[5]);
}
dhcpState = DHCP_STATE_INIT;
uint16_t start = millis();
while (dhcpState != DHCP_STATE_BOUND & & uint16_t(millis()) - start & lt; 60000) {
if (isLinkUp()) DhcpStateMachine(packetReceive());
}
updateBroadcastAddress();
delaycnt = 0;
return dhcpState == DHCP_STATE_BOUND ;
}
void EtherCard::dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback)
{
dhcpCustomOptionNum = option;
dhcpCustomOptionCallback = callback;
}
void EtherCard::DhcpStateMachine (uint16_t len)
{
#ifdef DHCPDEBUG
if (dhcpState != DHCP_STATE_BOUND) {
Serial.print(millis());
Serial.print( " State: " );
}
switch (dhcpState) {
case DHCP_STATE_INIT:
Serial.println( " Init " );
break;
case DHCP_STATE_SELECTING:
Serial.println( " Selecting " );
break;
case DHCP_STATE_REQUESTING:
Serial.println( " Requesting " );
break;
case DHCP_STATE_RENEWING:
Serial.println( " Renew " );
break;
}
#endif
switch (dhcpState) {
case DHCP_STATE_BOUND:
//!@todo Due to millis() wrap-around, DHCP renewal may not work if leaseTime is larger than 49days
if (leaseTime != DHCP_INFINITE_LEASE & & millis() - leaseStart & gt; = leaseTime) {
send_dhcp_message(myip);
dhcpState = DHCP_STATE_RENEWING;
stateTimer = millis();
}
break;
case DHCP_STATE_INIT:
currentXid = millis();
memset(myip,0,4); // force ip 0.0.0.0
send_dhcp_message(NULL);
enableBroadcast(true); //Temporarily enable broadcasts
dhcpState = DHCP_STATE_SELECTING;
stateTimer = millis();
break;
case DHCP_STATE_SELECTING:
if (dhcp_received_message_type(len, DHCP_OFFER)) {
uint8_t offeredip[4];
process_dhcp_offer(len, offeredip);
send_dhcp_message(offeredip);
dhcpState = DHCP_STATE_REQUESTING;
stateTimer = millis();
} else {
if (millis() - stateTimer & gt; DHCP_REQUEST_TIMEOUT) {
dhcpState = DHCP_STATE_INIT;
}
}
break;
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
if (dhcp_received_message_type(len, DHCP_ACK)) {
disableBroadcast(true); //Disable broadcast after temporary enable
process_dhcp_ack(len);
leaseStart = millis();
if (gwip[0] != 0) setGwIp(gwip); // why is this? because it initiates an arp request
dhcpState = DHCP_STATE_BOUND;
} else {
if (millis() - stateTimer & gt; DHCP_REQUEST_TIMEOUT) {
dhcpState = DHCP_STATE_INIT;
}
}
break;
}
}
// Some common utilities needed for IP and web applications
// Author: Guido Socher
// Copyright: GPL V2
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
#include " EtherCard.h "
void EtherCard::copyIp (uint8_t *dst, const uint8_t *src) {
memcpy(dst, src, 4);
}
void EtherCard::copyMac (uint8_t *dst, const uint8_t *src) {
memcpy(dst, src, 6);
}
void EtherCard::printIp (const char* msg, const uint8_t *buf) {
Serial.print(msg);
EtherCard::printIp(buf);
Serial.println();
}
void EtherCard::printIp (const __FlashStringHelper *ifsh, const uint8_t *buf) {
Serial.print(ifsh);
EtherCard::printIp(buf);
Serial.println();
}
void EtherCard::printIp (const uint8_t *buf) {
for (uint8_t i = 0; i & lt; 4; ++i) {
Serial.print( buf[i], DEC );
if (i & lt; 3)
Serial.print('.');
}
}
// search for a string of the form key=value in
// a string that looks like q?xyz=abc & uvw=defgh HTTP/1.1\r\n
//
// The returned value is stored in strbuf. You must allocate
// enough storage for strbuf, maxlen is the size of strbuf.
// I.e the value it is declated with: strbuf[5]- & gt; maxlen=5
uint8_t EtherCard::findKeyVal (const char *str,char *strbuf, uint8_t maxlen,const char *key)
{
uint8_t found=0;
uint8_t i=0;
const char *kp;
kp=key;
while(*str & & *str!=' ' & & *str!='\n' & & found==0) {
if (*str == *kp) {
kp++;
if (*kp == '\0') {
str++;
kp=key;
if (*str == '=') {
found=1;
}
}
} else {
kp=key;
}
str++;
}
if (found==1) {
// copy the value to a buffer and terminate it with '\0'
while(*str & & *str!=' ' & & *str!='\n' & & *str!=' & ' & & i & lt; maxlen-1) {
*strbuf=*str;
i++;
str++;
strbuf++;
}
*strbuf='\0';
}
// return the length of the value
return(i);
}
// convert a single hex digit character to its integer value
unsigned char h2int(char c)
{
if (c & gt; = '0' & & c & lt; ='9') {
return((unsigned char)c - '0');
}
if (c & gt; = 'a' & & c & lt; ='f') {
return((unsigned char)c - 'a' + 10);
}
if (c & gt; = 'A' & & c & lt; ='F') {
return((unsigned char)c - 'A' + 10);
}
return(0);
}
// decode a url string e.g " hello%20joe " or " hello+joe " becomes " hello joe "
void EtherCard::urlDecode (char *urlbuf)
{
char c;
char *dst = urlbuf;
while ((c = *urlbuf) != 0) {
if (c == '+') c = ' ';
if (c == '%') {
c = *++urlbuf;
c = (h2int(c) & lt; & lt; 4) | h2int(*++urlbuf);
}
*dst++ = c;
urlbuf++;
}
*dst = '\0';
}
// convert a single character to a 2 digit hex str
// a terminating '\0' is added
void int2h(char c, char *hstr)
{
hstr[1]=(c & 0xf)+'0';
if ((c & 0xf) & gt; 9) {
hstr[1]=(c & 0xf) - 10 + 'a';
}
c=(c & gt; & gt; 4) & 0xf;
hstr[0]=c+'0';
if (c & gt; 9) {
hstr[0]=c - 10 + 'a';
}
hstr[2]='\0';
}
// there must be enough space in urlbuf. In the worst case that is
// 3 times the length of str
void EtherCard::urlEncode (char *str,char *urlbuf)
{
char c;
while ((c = *str) != 0) {
if (c == ' '||isalnum(c)) {
if (c == ' ') {
c = '+';
}
*urlbuf=c;
str++;
urlbuf++;
continue;
}
*urlbuf='%';
urlbuf++;
int2h(c,urlbuf);
urlbuf++;
urlbuf++;
str++;
}
*urlbuf='\0';
}
// parse a string and extract the IP to bytestr
uint8_t EtherCard::parseIp (uint8_t *bytestr,char *str)
{
char *sptr;
uint8_t i=0;
sptr=NULL;
while(i & lt; 4) {
bytestr[i]=0;
i++;
}
i=0;
while(*str & & i & lt; 4) {
// if a number then start
if (sptr==NULL & & isdigit(*str)) {
sptr=str;
}
if (*str == '.') {
*str ='\0';
bytestr[i]=(atoi(sptr) & 0xff);
i++;
sptr=NULL;
}
str++;
}
*str ='\0';
if (i==3) {
bytestr[i]=(atoi(sptr) & 0xff);
return(0);
}
return(1);
}
// take a byte string and convert it to a human readable display string (base is 10 for ip and 16 for mac addr), len is 4 for IP addr and 6 for mac.
void EtherCard::makeNetStr (char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base)
{
uint8_t i=0;
uint8_t j=0;
while(i & lt; len) {
itoa((int)bytestr[i], & resultstr[j],base);
// search end of str:
while(resultstr[j]) {
j++;
}
resultstr[j]=separator;
j++;
i++;
}
j--;
resultstr[j]='\0';
}
// end of webutil.c
// This code slightly follows the conventions of, but is not derived from:
// EHTERSHIELD_H library for Arduino etherShield
// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1)
// It is however derived from the enc28j60 and ip code (which is GPL v2)
// Author: Pascal Stang
// Modified by: Guido Socher
// DHCP code: Andrew Lindsay
// Hence: GPL V2
//
// 2010-05-19 & lt; jc@wippler.nl & gt;
#include & lt; EtherCard.h & gt;
#include & lt; stdarg.h & gt;
#include & lt; avr/eeprom.h & gt;
#define WRITEBUF 0
#define READBUF 1
//#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting
byte Stash::map[SCRATCH_MAP_SIZE];
Stash::Block Stash::bufs[2];
uint8_t Stash::allocBlock () {
for (uint8_t i = 0; i & lt; sizeof map; ++i)
if (map[i] != 0)
for (uint8_t j = 0; j & lt; 8; ++j)
if (bitRead(map[i], j)) {
bitClear(map[i], j);
return (i & lt; & lt; 3) + j;
}
return 0;
}
void Stash::freeBlock (uint8_t block) {
bitSet(map[block & gt; & gt; 3], block & 7);
}
uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) {
return blk == bufs[WRITEBUF].bnum ? bufs[WRITEBUF].bytes[off] :
blk == bufs[READBUF].bnum ? bufs[READBUF].bytes[off] :
ether.peekin(blk, off);
}
// block 0 is special since always occupied
void Stash::initMap (uint8_t last /*=SCRATCH_PAGE_NUM*/) {
last = SCRATCH_PAGE_NUM;
while (--last & gt; 0)
freeBlock(last);
}
// load a page/block either into the write or into the readbuffer
void Stash::load (uint8_t idx, uint8_t blk) {
if (blk != bufs[idx].bnum) {
if (idx == WRITEBUF) {
ether.copyout(bufs[idx].bnum, bufs[idx].bytes);
if (blk == bufs[READBUF].bnum)
bufs[READBUF].bnum = 255; // forget read page if same
} else if (blk == bufs[WRITEBUF].bnum) {
// special case: read page is same as write buffer
memcpy( & bufs[READBUF], & bufs[WRITEBUF], sizeof bufs[0]);
return;
}
bufs[idx].bnum = blk;
ether.copyin(bufs[idx].bnum, bufs[idx].bytes);
}
}
uint8_t Stash::freeCount () {
uint8_t count = 0;
for (uint8_t i = 0; i & lt; sizeof map; ++i)
for (uint8_t m = 0x80; m != 0; m & gt; & gt; = 1)
if (map[i] & m)
++count;
return count;
}
// create a new stash; make it the active stash; return the first block as a handle
uint8_t Stash::create () {
uint8_t blk = allocBlock();
load(WRITEBUF, blk);
bufs[WRITEBUF].head.count = 0;
bufs[WRITEBUF].head.first = bufs[0].head.last = blk;
bufs[WRITEBUF].tail = sizeof (StashHeader);
bufs[WRITEBUF].next = 0;
return open(blk); // you are now the active stash
}
// the stashheader part only contains reasonable data if we are the first block
uint8_t Stash::open (uint8_t blk) {
curr = blk;
offs = sizeof (StashHeader); // goto first byte
load(READBUF, curr);
memcpy((StashHeader*) this, bufs[READBUF].bytes, sizeof (StashHeader));
return curr;
}
// save the metadata of current block into the first block
void Stash::save () {
load(WRITEBUF, first);
memcpy(bufs[WRITEBUF].bytes, (StashHeader*) this, sizeof (StashHeader));
if (bufs[READBUF].bnum == first)
load(READBUF, 0); // invalidates original in case it was the same block
}
// follow the linked list of blocks and free every block
void Stash::release () {
while (first & gt; 0) {
freeBlock(first);
first = ether.peekin(first, 63);
}
}
void Stash::put (char c) {
load(WRITEBUF, last);
uint8_t t = bufs[WRITEBUF].tail;
bufs[WRITEBUF].bytes[t++] = c;
if (t & lt; = 62)
bufs[WRITEBUF].tail = t;
else {
bufs[WRITEBUF].next = allocBlock();
last = bufs[WRITEBUF].next;
load(WRITEBUF, last);
bufs[WRITEBUF].tail = bufs[WRITEBUF].next = 0;
++count;
}
}
char Stash::get () {
load(READBUF, curr);
if (curr == last & & offs & gt; = bufs[READBUF].tail)
return 0;
uint8_t b = bufs[READBUF].bytes[offs];
if (++offs & gt; = 63 & & curr != last) {
curr = bufs[READBUF].next;
offs = 0;
}
return b;
}
// fetchbyte(last, 62) is tail, i.e., number of characters in last block
uint16_t Stash::size () {
return 63 * count + fetchByte(last, 62) - sizeof (StashHeader);
}
static char* wtoa (uint16_t value, char* ptr) {
if (value & gt; 9)
ptr = wtoa(value / 10, ptr);
*ptr = '0' + value % 10;
*++ptr = 0;
return ptr;
}
// write information about the fmt string and the arguments into special page/block 0
// block 0 is initially marked as allocated and never returned by allocateBlock
void Stash::prepare (PGM_P fmt, ...) {
Stash::load(WRITEBUF, 0);
uint16_t* segs = Stash::bufs[WRITEBUF].words;
*segs++ = strlen_P(fmt);
#ifdef __AVR__
*segs++ = (uint16_t) fmt;
#else
*segs++ = (uint32_t) fmt;
*segs++ = (uint32_t) fmt & gt; & gt; 16;
#endif
va_list ap;
va_start(ap, fmt);
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c == '$') {
#ifdef __AVR__
uint16_t argval = va_arg(ap, uint16_t), arglen = 0;
#else
uint32_t argval = va_arg(ap, int), arglen = 0;
#endif
switch (pgm_read_byte(fmt++)) {
case 'D': {
char buf[7];
wtoa(argval, buf);
arglen = strlen(buf);
break;
}
case 'S':
arglen = strlen((const char*) argval);
break;
case 'F':
arglen = strlen_P((PGM_P) argval);
break;
case 'E': {
byte* s = (byte*) argval;
char d;
while ((d = eeprom_read_byte(s++)) != 0)
++arglen;
break;
}
case 'H': {
Stash stash (argval);
arglen = stash.size();
break;
}
}
#ifdef __AVR__
*segs++ = argval;
#else
*segs++ = argval;
*segs++ = argval & gt; & gt; 16;
#endif
Stash::bufs[WRITEBUF].words[0] += arglen - 2;
}
}
va_end(ap);
}
uint16_t Stash::length () {
Stash::load(WRITEBUF, 0);
return Stash::bufs[WRITEBUF].words[0];
}
void Stash::extract (uint16_t offset, uint16_t count, void* buf) {
Stash::load(WRITEBUF, 0);
uint16_t* segs = Stash::bufs[WRITEBUF].words;
#ifdef __AVR__
PGM_P fmt = (PGM_P) *++segs;
#else
PGM_P fmt = (PGM_P)((segs[2] & lt; & lt; 16) | segs[1]);
segs += 2;
#endif
Stash stash;
char mode = '@', tmp[7], *ptr = NULL, *out = (char*) buf;
for (uint16_t i = 0; i & lt; offset + count; ) {
char c = 0;
switch (mode) {
case '@': {
c = pgm_read_byte(fmt++);
if (c == 0)
return;
if (c != '$')
break;
#ifdef __AVR__
uint16_t arg = *++segs;
#else
uint32_t arg = *++segs;
arg |= *++segs & lt; & lt; 16;
#endif
mode = pgm_read_byte(fmt++);
switch (mode) {
case 'D':
wtoa(arg, tmp);
ptr = tmp;
break;
case 'S':
case 'F':
case 'E':
ptr = (char*) arg;
break;
case 'H':
stash.open(arg);
ptr = (char*) & stash;
break;
}
continue;
}
case 'D':
case 'S':
c = *ptr++;
break;
case 'F':
c = pgm_read_byte(ptr++);
break;
case 'E':
c = eeprom_read_byte((byte*) ptr++);
break;
case 'H':
c = ((Stash*) ptr)- & gt; get();
break;
}
if (c == 0) {
mode = '@';
continue;
}
if (i & gt; = offset)
*out++ = c;
++i;
}
}
void Stash::cleanup () {
Stash::load(WRITEBUF, 0);
uint16_t* segs = Stash::bufs[WRITEBUF].words;
#ifdef __AVR__
PGM_P fmt = (PGM_P) *++segs;
#else
PGM_P fmt = (PGM_P)((segs[2] & lt; & lt; 16) | segs[1]);
segs += 2;
#endif
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c == '$') {
#ifdef __AVR__
uint16_t arg = *++segs;
#else
uint32_t arg = *++segs;
arg |= *++segs & lt; & lt; 16;
#endif
if (pgm_read_byte(fmt++) == 'H') {
Stash stash (arg);
stash.release();
}
}
}
}
void BufferFiller::emit_p(PGM_P fmt, ...) {
va_list ap;
va_start(ap, fmt);
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c != '$') {
*ptr++ = c;
continue;
}
c = pgm_read_byte(fmt++);
switch (c) {
case 'D':
#ifdef __AVR__
wtoa(va_arg(ap, uint16_t), (char*) ptr);
#else
wtoa(va_arg(ap, int), (char*) ptr);
#endif
break;
#ifdef FLOATEMIT
case 'T':
dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr );
break;
#endif
case 'H': {
#ifdef __AVR__
char p1 = va_arg(ap, uint16_t);
#else
char p1 = va_arg(ap, int);
#endif
char p2;
p2 = (p1 & gt; & gt; 4) & 0x0F;
p1 = p1 & 0x0F;
if (p1 & gt; 9) p1 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
p1 += 0x30; // and complete
if (p2 & gt; 9) p2 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
p2 += 0x30; // and complete
*ptr++ = p2;
*ptr++ = p1;
continue;
}
case 'L':
ltoa(va_arg(ap, long), (char*) ptr, 10);
break;
case 'S':
strcpy((char*) ptr, va_arg(ap, const char*));
break;
case 'F': {
PGM_P s = va_arg(ap, PGM_P);
char d;
while ((d = pgm_read_byte(s++)) != 0)
*ptr++ = d;
continue;
}
case 'E': {
byte* s = va_arg(ap, byte*);
char d;
while ((d = eeprom_read_byte(s++)) != 0)
*ptr++ = d;
continue;
}
default:
*ptr++ = c;
continue;
}
ptr += strlen((char*) ptr);
}
va_end(ap);
}
EtherCard ether;
uint8_t EtherCard::mymac[6]; // my MAC address
uint8_t EtherCard::myip[4]; // my ip address
uint8_t EtherCard::netmask[4]; // subnet mask
uint8_t EtherCard::broadcastip[4]; // broadcast address
uint8_t EtherCard::gwip[4]; // gateway
uint8_t EtherCard::dhcpip[4]; // dhcp server
uint8_t EtherCard::dnsip[4]; // dns server
uint8_t EtherCard::hisip[4]; // ip address of remote host
uint16_t EtherCard::hisport = 80; // tcp port to browse to
bool EtherCard::using_dhcp = false;
bool EtherCard::persist_tcp_connection = false;
uint16_t EtherCard::delaycnt = 0; //request gateway ARP lookup
uint8_t EtherCard::begin (const uint16_t size,
const uint8_t* macaddr,
uint8_t csPin) {
using_dhcp = false;
#if ETHERCARD_STASH
Stash::initMap();
#endif
copyMac(mymac, macaddr);
return initialize(size, mymac, csPin);
}
bool EtherCard::staticSetup (const uint8_t* my_ip,
const uint8_t* gw_ip,
const uint8_t* dns_ip,
const uint8_t* mask) {
using_dhcp = false;
if (my_ip != 0)
copyIp(myip, my_ip);
if (gw_ip != 0)
setGwIp(gw_ip);
if (dns_ip != 0)
copyIp(dnsip, dns_ip);
if(mask != 0)
copyIp(netmask, mask);
updateBroadcastAddress();
delaycnt = 0; //request gateway ARP lookup
return true;
}
// IP, ARP, UDP and TCP functions.
// Author: Guido Socher
// Copyright: GPL V2
//
// The TCP implementation uses some size optimisations which are valid
// only if all data can be sent in one single packet. This is however
// not a big limitation for a microcontroller as you will anyhow use
// small web-pages. The web server must send the entire web page in one
// packet. The client " web browser " as implemented here can also receive
// large pages.
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
#include " EtherCard.h "
#include " net.h "
#undef word // arduino nonsense
#define gPB ether.buffer
#define PINGPATTERN 0x42
// Avoid spurious pgmspace warnings - http://forum.jeelabs.net/node/327
// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
//#undef PROGMEM
//#define PROGMEM __attribute__(( section( " .progmem.data " ) ))
//#undef PSTR
//#define PSTR(s) (__extension__({static prog_char c[] PROGMEM = (s); & c[0];}))
#define TCPCLIENT_SRC_PORT_H 11 //Source port (MSB) for TCP/IP client connections - hardcode all TCP/IP client connection from ports in range 2816-3071
static uint8_t tcpclient_src_port_l=1; // Source port (LSB) for tcp/ip client connections - increments on each TCP/IP request
static uint8_t tcp_fd; // a file descriptor, will be encoded into the port
static uint8_t tcp_client_state; //TCP connection state: 1=Send SYN, 2=SYN sent awaiting SYN+ACK, 3=Established, 4=Not used, 5=Closing, 6=Closed
static uint8_t tcp_client_port_h; // Destination port (MSB) of TCP/IP client connection
static uint8_t tcp_client_port_l; // Destination port (LSB) of TCP/IP client connection
static uint8_t (*client_tcp_result_cb)(uint8_t,uint8_t,uint16_t,uint16_t); // Pointer to callback function to handle response to current TCP/IP request
static uint16_t (*client_tcp_datafill_cb)(uint8_t); //Pointer to callback function to handle payload data in response to current TCP/IP request
static uint8_t www_fd; // ID of current http request (only one http request at a time - one of the 8 possible concurrent TCP/IP connections)
static void (*client_browser_cb)(uint8_t,uint16_t,uint16_t); // Pointer to callback function to handle result of current HTTP request
static const char *client_additionalheaderline; // Pointer to c-string additional http request header info
static const char *client_postval;
static const char *client_urlbuf; // Pointer to c-string path part of HTTP request URL
static const char *client_urlbuf_var; // Pointer to c-string filename part of HTTP request URL
static const char *client_hoststr; // Pointer to c-string hostname of current HTTP request
static void (*icmp_cb)(uint8_t *ip); // Pointer to callback function for ICMP ECHO response handler (triggers when localhost recieves ping respnse (pong))
static uint8_t destmacaddr[6]; // storing both dns server and destination mac addresses, but at different times because both are never needed at same time.
static boolean waiting_for_dns_mac = false; //might be better to use bit flags and bitmask operations for these conditions
static boolean has_dns_mac = false;
static boolean waiting_for_dest_mac = false;
static boolean has_dest_mac = false;
static uint8_t gwmacaddr[6]; // Hardware (MAC) address of gateway router
static uint8_t waitgwmac; // Bitwise flags of gateway router status - see below for states
//Define gatweay router ARP statuses
#define WGW_INITIAL_ARP 1 // First request, no answer yet
#define WGW_HAVE_GW_MAC 2 // Have gateway router MAC
#define WGW_REFRESHING 4 // Refreshing but already have gateway MAC
#define WGW_ACCEPT_ARP_REPLY 8 // Accept an ARP reply
static uint16_t info_data_len; // Length of TCP/IP payload
static uint8_t seqnum = 0xa; // My initial tcp sequence number
static uint8_t result_fd = 123; // Session id of last reply
static const char* result_ptr; // Pointer to TCP/IP data
static unsigned long SEQ; // TCP/IP sequence number
#define CLIENTMSS 550
#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P] & gt; & gt; 4)*4) // Get offset of TCP/IP payload data
const unsigned char arpreqhdr[] PROGMEM = { 0,1,8,0,6,4,0,1 }; // ARP request header
const unsigned char iphdr[] PROGMEM = { 0x45,0,0,0x82,0,0,0x40,0,0x20 }; //IP header
const unsigned char ntpreqhdr[] PROGMEM = { 0xE3,0,4,0xFA,0,1,0,0,0,1 }; //NTP request header
extern const uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Used for hardware (MAC) and IP broadcast addresses
static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) {
const uint8_t* ptr = gPB + off;
uint32_t sum = type==1 ? IP_PROTO_UDP_V+len-8 :
type==2 ? IP_PROTO_TCP_V+len-8 : 0;
while(len & gt; 1) {
sum += (uint16_t) (((uint32_t)*ptr & lt; & lt; 8)|*(ptr+1));
ptr+=2;
len-=2;
}
if (len)
sum += ((uint32_t)*ptr) & lt; & lt; 8;
while (sum & gt; & gt; 16)
sum = (uint16_t) sum + (sum & gt; & gt; 16);
uint16_t ck = ~ (uint16_t) sum;
gPB[dest] = ck & gt; & gt; 8;
gPB[dest+1] = ck;
}
static void setMACs (const uint8_t *mac) {
EtherCard::copyMac(gPB + ETH_DST_MAC, mac);
EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac);
}
static void setMACandIPs (const uint8_t *mac, const uint8_t *dst) {
setMACs(mac);
EtherCard::copyIp(gPB + IP_DST_P, dst);
EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip);
}
static uint8_t check_ip_message_is_from(const uint8_t *ip) {
return memcmp(gPB + IP_SRC_P, ip, 4) == 0;
}
static boolean is_lan(const uint8_t source[4], const uint8_t destination[4]) {
if(source[0] == 0 || destination[0] == 0) {
return false;
}
for(int i = 0; i & lt; 4; i++)
if((source[i] & EtherCard::netmask[i]) != (destination[i] & EtherCard::netmask[i])) {
return false;
}
return true;
}
static uint8_t eth_type_is_arp_and_my_ip(uint16_t len) {
return len & gt; = 41 & & gPB[ETH_TYPE_H_P] == ETHTYPE_ARP_H_V & &
gPB[ETH_TYPE_L_P] == ETHTYPE_ARP_L_V & &
memcmp(gPB + ETH_ARP_DST_IP_P, EtherCard::myip, 4) == 0;
}
static uint8_t eth_type_is_ip_and_my_ip(uint16_t len) {
return len & gt; = 42 & & gPB[ETH_TYPE_H_P] == ETHTYPE_IP_H_V & &
gPB[ETH_TYPE_L_P] == ETHTYPE_IP_L_V & &
gPB[IP_HEADER_LEN_VER_P] == 0x45 & &
(memcmp(gPB + IP_DST_P, EtherCard::myip, 4) == 0 //not my IP
|| (memcmp(gPB + IP_DST_P, EtherCard::broadcastip, 4) == 0) //not subnet broadcast
|| (memcmp(gPB + IP_DST_P, allOnes, 4) == 0)); //not global broadcasts
//!@todo Handle multicast
}
static void fill_ip_hdr_checksum() {
gPB[IP_CHECKSUM_P] = 0;
gPB[IP_CHECKSUM_P+1] = 0;
gPB[IP_FLAGS_P] = 0x40; // don't fragment
gPB[IP_FLAGS_P+1] = 0; // fragement offset
gPB[IP_TTL_P] = 64; // ttl
fill_checksum(IP_CHECKSUM_P, IP_P, IP_HEADER_LEN,0);
}
static void make_eth_ip() {
setMACs(gPB + ETH_SRC_MAC);
EtherCard::copyIp(gPB + IP_DST_P, gPB + IP_SRC_P);
EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip);
fill_ip_hdr_checksum();
}
static void step_seq(uint16_t rel_ack_num,uint8_t cp_seq) {
uint8_t i;
uint8_t tseq;
i = 4;
while(i & gt; 0) {
rel_ack_num = gPB[TCP_SEQ_H_P+i-1]+rel_ack_num;
tseq = gPB[TCP_SEQACK_H_P+i-1];
gPB[TCP_SEQACK_H_P+i-1] = rel_ack_num;
if (cp_seq)
gPB[TCP_SEQ_H_P+i-1] = tseq;
else
gPB[TCP_SEQ_H_P+i-1] = 0; // some preset value
rel_ack_num = rel_ack_num & gt; & gt; 8;
i--;
}
}
static void make_tcphead(uint16_t rel_ack_num,uint8_t cp_seq) {
uint8_t i = gPB[TCP_DST_PORT_H_P];
gPB[TCP_DST_PORT_H_P] = gPB[TCP_SRC_PORT_H_P];
gPB[TCP_SRC_PORT_H_P] = i;
uint8_t j = gPB[TCP_DST_PORT_L_P];
gPB[TCP_DST_PORT_L_P] = gPB[TCP_SRC_PORT_L_P];
gPB[TCP_SRC_PORT_L_P] = j;
step_seq(rel_ack_num,cp_seq);
gPB[TCP_CHECKSUM_H_P] = 0;
gPB[TCP_CHECKSUM_L_P] = 0;
gPB[TCP_HEADER_LEN_P] = 0x50;
}
static void make_arp_answer_from_request() {
setMACs(gPB + ETH_SRC_MAC);
gPB[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V;
gPB[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V;
EtherCard::copyMac(gPB + ETH_ARP_DST_MAC_P, gPB + ETH_ARP_SRC_MAC_P);
EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac);
EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, gPB + ETH_ARP_SRC_IP_P);
EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip);
EtherCard::packetSend(42);
}
static void make_echo_reply_from_request(uint16_t len) {
make_eth_ip();
gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V;
if (gPB[ICMP_CHECKSUM_P] & gt; (0xFF-0x08))
gPB[ICMP_CHECKSUM_P+1]++;
gPB[ICMP_CHECKSUM_P] += 0x08;
EtherCard::packetSend(len);
}
void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) {
if (datalen & gt; 220)
datalen = 220;
gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & gt; & gt; 8;
gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
make_eth_ip();
gPB[UDP_DST_PORT_H_P] = gPB[UDP_SRC_PORT_H_P];
gPB[UDP_DST_PORT_L_P] = gPB[UDP_SRC_PORT_L_P];
gPB[UDP_SRC_PORT_H_P] = port & gt; & gt; 8;
gPB[UDP_SRC_PORT_L_P] = port;
gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) & gt; & gt; 8;
gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen;
gPB[UDP_CHECKSUM_H_P] = 0;
gPB[UDP_CHECKSUM_L_P] = 0;
memcpy(gPB + UDP_DATA_P, data, datalen);
fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1);
packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen);
}
static void make_tcp_synack_from_syn() {
gPB[IP_TOTLEN_H_P] = 0;
gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4;
make_eth_ip();
gPB[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V;
make_tcphead(1,0);
gPB[TCP_SEQ_H_P+0] = 0;
gPB[TCP_SEQ_H_P+1] = 0;
gPB[TCP_SEQ_H_P+2] = seqnum;
gPB[TCP_SEQ_H_P+3] = 0;
seqnum += 3;
gPB[TCP_OPTIONS_P] = 2;
gPB[TCP_OPTIONS_P+1] = 4;
gPB[TCP_OPTIONS_P+2] = 0x05;
gPB[TCP_OPTIONS_P+3] = 0x0;
gPB[TCP_HEADER_LEN_P] = 0x60;
gPB[TCP_WIN_SIZE] = 0x5; // 1400=0x578
gPB[TCP_WIN_SIZE+1] = 0x78;
fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+4,2);
EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN);
}
uint16_t EtherCard::getTcpPayloadLength() {
int16_t i = (((int16_t)gPB[IP_TOTLEN_H_P]) & lt; & lt; 8)|gPB[IP_TOTLEN_L_P];
i -= IP_HEADER_LEN;
i -= (gPB[TCP_HEADER_LEN_P] & gt; & gt; 4)*4; // generate len in bytes;
if (i & lt; =0)
i = 0;
return (uint16_t)i;
}
static void make_tcp_ack_from_any(int16_t datlentoack,uint8_t addflags) {
gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|addflags;
if (addflags!=TCP_FLAGS_RST_V & & datlentoack==0)
datlentoack = 1;
make_tcphead(datlentoack,1); // no options
uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN;
gPB[IP_TOTLEN_H_P] = j & gt; & gt; 8;
gPB[IP_TOTLEN_L_P] = j;
make_eth_ip();
gPB[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 1280=0x500 2048=0x800 768=0x300
gPB[TCP_WIN_SIZE+1] = 0;
fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN,2);
EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN);
}
static void make_tcp_ack_with_data_noflags(uint16_t dlen) {
uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen;
gPB[IP_TOTLEN_H_P] = j & gt; & gt; 8;
gPB[IP_TOTLEN_L_P] = j;
fill_ip_hdr_checksum();
gPB[TCP_CHECKSUM_H_P] = 0;
gPB[TCP_CHECKSUM_L_P] = 0;
fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+dlen,2);
EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN);
}
void EtherCard::httpServerReply (uint16_t dlen) {
make_tcp_ack_from_any(info_data_len,0); // send ack for http get
gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V;
make_tcp_ack_with_data_noflags(dlen); // send data
}
static uint32_t getBigEndianLong(byte offs) { //get the sequence number of packets after an ack from GET
return (((unsigned long)gPB[offs]*256+gPB[offs+1])*256+gPB[offs+2])*256+gPB[offs+3];
} //thanks to mstuetz for the missing (unsigned long)
static void setSequenceNumber(uint32_t seq) {
gPB[TCP_SEQ_H_P] = (seq & 0xff000000 ) & gt; & gt; 24;
gPB[TCP_SEQ_H_P+1] = (seq & 0xff0000 ) & gt; & gt; 16;
gPB[TCP_SEQ_H_P+2] = (seq & 0xff00 ) & gt; & gt; 8;
gPB[TCP_SEQ_H_P+3] = (seq & 0xff );
}
uint32_t EtherCard::getSequenceNumber() {
return getBigEndianLong(TCP_SEQ_H_P);
}
void EtherCard::httpServerReplyAck () {
make_tcp_ack_from_any(getTcpPayloadLength(),0); // send ack for http request
SEQ = getSequenceNumber(); //get the sequence number of packets after an ack from GET
}
void EtherCard::httpServerReply_with_flags (uint16_t dlen , uint8_t flags) {
setSequenceNumber(SEQ);
gPB[TCP_FLAGS_P] = flags; // final packet
make_tcp_ack_with_data_noflags(dlen); // send data
SEQ=SEQ+dlen;
}
void EtherCard::clientIcmpRequest(const uint8_t *destip) {
if(is_lan(EtherCard::myip, destip)) {
setMACandIPs(destmacaddr, destip);
} else {
setMACandIPs(gwmacaddr, destip);
}
gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
memcpy_P(gPB + IP_P,iphdr,9);
gPB[IP_TOTLEN_L_P] = 0x54;
gPB[IP_PROTO_P] = IP_PROTO_ICMP_V;
fill_ip_hdr_checksum();
gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V;
gPB[ICMP_TYPE_P+1] = 0; // code
gPB[ICMP_CHECKSUM_H_P] = 0;
gPB[ICMP_CHECKSUM_L_P] = 0;
gPB[ICMP_IDENT_H_P] = 5; // some number
gPB[ICMP_IDENT_L_P] = EtherCard::myip[3]; // last byte of my IP
gPB[ICMP_IDENT_L_P+1] = 0; // seq number, high byte
gPB[ICMP_IDENT_L_P+2] = 1; // seq number, low byte, we send only 1 ping at a time
memset(gPB + ICMP_DATA_P, PINGPATTERN, 56);
fill_checksum(ICMP_CHECKSUM_H_P, ICMP_TYPE_P, 56+8,0);
packetSend(98);
}
void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) {
if(is_lan(myip, ntpip)) {
setMACandIPs(destmacaddr, ntpip);
} else {
setMACandIPs(gwmacaddr, ntpip);
}
gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
memcpy_P(gPB + IP_P,iphdr,9);
gPB[IP_TOTLEN_L_P] = 0x4c;
gPB[IP_PROTO_P] = IP_PROTO_UDP_V;
fill_ip_hdr_checksum();
gPB[UDP_DST_PORT_H_P] = 0;
gPB[UDP_DST_PORT_L_P] = 0x7b; // ntp = 123
gPB[UDP_SRC_PORT_H_P] = 10;
gPB[UDP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port
gPB[UDP_LEN_H_P] = 0;
gPB[UDP_LEN_L_P] = 56; // fixed len
gPB[UDP_CHECKSUM_H_P] = 0;
gPB[UDP_CHECKSUM_L_P] = 0;
memset(gPB + UDP_DATA_P, 0, 48);
memcpy_P(gPB + UDP_DATA_P,ntpreqhdr,10);
fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 48,1);
packetSend(90);
}
uint8_t EtherCard::ntpProcessAnswer (uint32_t *time,uint8_t dstport_l) {
if ((dstport_l & & gPB[UDP_DST_PORT_L_P]!=dstport_l) || gPB[UDP_LEN_H_P]!=0 ||
gPB[UDP_LEN_L_P]!=56 || gPB[UDP_SRC_PORT_L_P]!=0x7b)
return 0;
((uint8_t*) time)[3] = gPB[0x52];
((uint8_t*) time)[2] = gPB[0x53];
((uint8_t*) time)[1] = gPB[0x54];
((uint8_t*) time)[0] = gPB[0x55];
return 1;
}
void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) {
if(is_lan(myip, dip)) { // this works because both dns mac and destinations mac are stored in same variable - destmacaddr
setMACandIPs(destmacaddr, dip); // at different times. The program could have separate variable for dns mac, then here should be
} else { // checked if dip is dns ip and separately if dip is hisip and then use correct mac.
setMACandIPs(gwmacaddr, dip);
}
// see http://tldp.org/HOWTO/Multicast-HOWTO-2.html
// multicast or broadcast address, https://github.com/jcw/ethercard/issues/59
if ((dip[0] & 0xF0) == 0xE0 || *((unsigned long*) dip) == 0xFFFFFFFF || !memcmp(broadcastip,dip,4))
EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes);
gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
memcpy_P(gPB + IP_P,iphdr,9);
gPB[IP_TOTLEN_H_P] = 0;
gPB[IP_PROTO_P] = IP_PROTO_UDP_V;
gPB[UDP_DST_PORT_H_P] = (dport & gt; & gt; 8);
gPB[UDP_DST_PORT_L_P] = dport;
gPB[UDP_SRC_PORT_H_P] = (sport & gt; & gt; 8);
gPB[UDP_SRC_PORT_L_P] = sport;
gPB[UDP_LEN_H_P] = 0;
gPB[UDP_CHECKSUM_H_P] = 0;
gPB[UDP_CHECKSUM_L_P] = 0;
}
void EtherCard::udpTransmit (uint16_t datalen) {
gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & gt; & gt; 8;
gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
fill_ip_hdr_checksum();
gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) & gt; & gt; 8;
gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen;
fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1);
packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen);
}
void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport,
const uint8_t *dip, uint16_t dport) {
udpPrepare(sport, dip, dport);
if (datalen & gt; 220)
datalen = 220;
memcpy(gPB + UDP_DATA_P, data, datalen);
udpTransmit(datalen);
}
void EtherCard::sendWol (uint8_t *wolmac) {
setMACandIPs(allOnes, allOnes);
gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
memcpy_P(gPB + IP_P,iphdr,9);
gPB[IP_TOTLEN_L_P] = 0x82;
gPB[IP_PROTO_P] = IP_PROTO_UDP_V;
fill_ip_hdr_checksum();
gPB[UDP_DST_PORT_H_P] = 0;
gPB[UDP_DST_PORT_L_P] = 0x9; // wol = normally 9
gPB[UDP_SRC_PORT_H_P] = 10;
gPB[UDP_SRC_PORT_L_P] = 0x42; // source port does not matter
gPB[UDP_LEN_H_P] = 0;
gPB[UDP_LEN_L_P] = 110; // fixed len
gPB[UDP_CHECKSUM_H_P] = 0;
gPB[UDP_CHECKSUM_L_P] = 0;
copyMac(gPB + UDP_DATA_P, allOnes);
uint8_t pos = UDP_DATA_P;
for (uint8_t m = 0; m & lt; 16; ++m) {
pos += 6;
copyMac(gPB + pos, wolmac);
}
fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 102,1);
packetSend(pos + 6);
}
// make a arp request
static void client_arp_whohas(uint8_t *ip_we_search) {
setMACs(allOnes);
gPB[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V;
memcpy_P(gPB + ETH_ARP_P,arpreqhdr,8);
memset(gPB + ETH_ARP_DST_MAC_P, 0, 6);
EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac);
EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, ip_we_search);
EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip);
EtherCard::packetSend(42);
}
uint8_t EtherCard::clientWaitingGw () {
return !(waitgwmac & WGW_HAVE_GW_MAC);
}
uint8_t EtherCard::clientWaitingDns () {
if(is_lan(myip, dnsip))
return !has_dns_mac;
return !(waitgwmac & WGW_HAVE_GW_MAC);
}
static uint8_t client_store_mac(uint8_t *source_ip, uint8_t *mac) {
if (memcmp(gPB + ETH_ARP_SRC_IP_P, source_ip, 4) != 0)
return 0;
EtherCard::copyMac(mac, gPB + ETH_ARP_SRC_MAC_P);
return 1;
}
// static void client_gw_arp_refresh() {
// if (waitgwmac & WGW_HAVE_GW_MAC)
// waitgwmac |= WGW_REFRESHING;
// }
void EtherCard::setGwIp (const uint8_t *gwipaddr) {
delaycnt = 0; //request gateway ARP lookup
waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop
copyIp(gwip, gwipaddr);
}
void EtherCard::updateBroadcastAddress()
{
for(uint8_t i=0; i & lt; 4; i++)
broadcastip[i] = myip[i] | ~netmask[i];
}
static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) {
if(is_lan(EtherCard::myip, EtherCard::hisip)) {
setMACandIPs(destmacaddr, EtherCard::hisip);
} else {
setMACandIPs(gwmacaddr, EtherCard::hisip);
}
gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
memcpy_P(gPB + IP_P,iphdr,9);
gPB[IP_TOTLEN_L_P] = 44; // good for syn
gPB[IP_PROTO_P] = IP_PROTO_TCP_V;
fill_ip_hdr_checksum();
gPB[TCP_DST_PORT_H_P] = dstport_h;
gPB[TCP_DST_PORT_L_P] = dstport_l;
gPB[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H;
gPB[TCP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port
memset(gPB + TCP_SEQ_H_P, 0, 8);
gPB[TCP_SEQ_H_P+2] = seqnum;
seqnum += 3;
gPB[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60 & gt; & gt; 4) * 4
gPB[TCP_FLAGS_P] = TCP_FLAGS_SYN_V;
gPB[TCP_WIN_SIZE] = 0x3; // 1024 = 0x400 768 = 0x300, initial window
gPB[TCP_WIN_SIZE+1] = 0x0;
gPB[TCP_CHECKSUM_H_P] = 0;
gPB[TCP_CHECKSUM_L_P] = 0;
gPB[TCP_CHECKSUM_L_P+1] = 0;
gPB[TCP_CHECKSUM_L_P+2] = 0;
gPB[TCP_OPTIONS_P] = 2;
gPB[TCP_OPTIONS_P+1] = 4;
gPB[TCP_OPTIONS_P+2] = (CLIENTMSS & gt; & gt; 8);
gPB[TCP_OPTIONS_P+3] = (uint8_t) CLIENTMSS;
fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8 +TCP_HEADER_LEN_PLAIN+4,2);
// 4 is the tcp mss option:
EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4);
}
uint8_t EtherCard::clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t),
uint16_t (*datafill_cb)(uint8_t),uint16_t port) {
client_tcp_result_cb = result_cb;
client_tcp_datafill_cb = datafill_cb;
tcp_client_port_h = port & gt; & gt; 8;
tcp_client_port_l = port;
tcp_client_state = 1; // Flag to packetloop to initiate a TCP/IP session by send a syn
tcp_fd = (tcp_fd + 1) & 7;
return tcp_fd;
}
static uint16_t www_client_internal_datafill_cb(uint8_t fd) {
BufferFiller bfill = EtherCard::tcpOffset();
if (fd==www_fd) {
if (client_postval == 0) {
bfill.emit_p(PSTR( " GET $F$S HTTP/1.0\r\n "
" Host: $F\r\n "
" $F\r\n "
" \r\n " ), client_urlbuf,
client_urlbuf_var,
client_hoststr, client_additionalheaderline);
} else {
const char* ahl = client_additionalheaderline;
bfill.emit_p(PSTR( " POST $F HTTP/1.0\r\n "
" Host: $F\r\n "
" $F$S "
" Accept: */*\r\n "
" Content-Length: $D\r\n "
" Content-Type: application/x-www-form-urlencoded\r\n "
" \r\n "
" $S " ), client_urlbuf,
client_hoststr,
ahl != 0 ? ahl : PSTR( " " ),
ahl != 0 ? " \r\n " : " " ,
strlen(client_postval),
client_postval);
}
}
return bfill.position();
}
static uint8_t www_client_internal_result_cb(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) {
if (fd!=www_fd)
(*client_browser_cb)(4,0,0);
else if (statuscode==0 & & len_of_data & gt; 12 & & client_browser_cb) {
uint8_t f = strncmp( " 200 " ,(char *) & (gPB[datapos+9]),3) != 0;
(*client_browser_cb)(f, ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P] & gt; & gt; 4)*4),len_of_data);
}
return 0;
}
void EtherCard::browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)) {
browseUrl(urlbuf, urlbuf_varpart, hoststr, PSTR( " Accept: text/html " ), callback);
}
void EtherCard::browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)) {
client_urlbuf = urlbuf;
client_urlbuf_var = urlbuf_varpart;
client_hoststr = hoststr;
client_additionalheaderline = additionalheaderline;
client_postval = 0;
client_browser_cb = callback;
www_fd = clientTcpReq( & www_client_internal_result_cb, & www_client_internal_datafill_cb,hisport);
}
void EtherCard::httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)) {
client_urlbuf = urlbuf;
client_hoststr = hoststr;
client_additionalheaderline = additionalheaderline;
client_postval = postval;
client_browser_cb = callback;
www_fd = clientTcpReq( & www_client_internal_result_cb, & www_client_internal_datafill_cb,hisport);
}
static uint16_t tcp_datafill_cb(uint8_t fd) {
uint16_t len = Stash::length();
Stash::extract(0, len, EtherCard::tcpOffset());
Stash::cleanup();
EtherCard::tcpOffset()[len] = 0;
#if SERIAL
Serial.print( " REQUEST: " );
Serial.println(len);
Serial.println((char*) EtherCard::tcpOffset());
#endif
result_fd = 123; // bogus value
return len;
}
static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t datalen) {
if (status == 0) {
result_fd = fd; // a valid result has been received, remember its session id
result_ptr = (char*) ether.buffer + datapos;
// result_ptr[datalen] = 0;
}
return 1;
}
uint8_t EtherCard::tcpSend () {
www_fd = clientTcpReq( & tcp_result_cb, & tcp_datafill_cb, hisport);
return www_fd;
}
const char* EtherCard::tcpReply (uint8_t fd) {
if (result_fd != fd)
return 0;
result_fd = 123; // set to a bogus value to prevent future match
return result_ptr;
}
void EtherCard::registerPingCallback (void (*callback)(uint8_t *srcip)) {
icmp_cb = callback;
}
uint8_t EtherCard::packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost) {
return gPB[IP_PROTO_P]==IP_PROTO_ICMP_V & &
gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V & &
gPB[ICMP_DATA_P]== PINGPATTERN & &
check_ip_message_is_from(ip_monitoredhost);
}
uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) {
uint16_t pos;
if (gPB[TCP_DST_PORT_H_P] == (port & gt; & gt; 8) & &
gPB[TCP_DST_PORT_L_P] == ((uint8_t) port))
{ //Packet targetted at specified port
if (gPB[TCP_FLAGS_P] & TCP_FLAGS_SYN_V)
make_tcp_synack_from_syn(); //send SYN+ACK
else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)
{ //This is an acknowledgement to our SYN+ACK so let's start processing that payload
info_data_len = getTcpPayloadLength();
if (info_data_len & gt; 0)
{ //Got some data
pos = TCP_DATA_START; // TCP_DATA_START is a formula
//!@todo no idea what this check pos & lt; =plen-8 does; changed this to pos & lt; =plen as otw. perfectly valid tcp packets are ignored; still if anybody has any idea please leave a comment
if (pos & lt; = plen)
return pos;
}
else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V)
make_tcp_ack_from_any(0,0); //No data so close connection
}
}
return 0;
}
uint16_t EtherCard::packetLoop (uint16_t plen) {
uint16_t len;
#if ETHERCARD_DHCP
if(using_dhcp) {
ether.DhcpStateMachine(plen);
}
#endif
if (plen==0) {
//Check every 65536 (no-packet) cycles whether we need to retry ARP request for gateway
if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) & &
delaycnt==0 & & isLinkUp()) {
client_arp_whohas(gwip);
waitgwmac |= WGW_ACCEPT_ARP_REPLY;
}
delaycnt++;
#if ETHERCARD_TCPCLIENT
//Initiate TCP/IP session if pending
if (tcp_client_state==1 & & (waitgwmac & WGW_HAVE_GW_MAC)) { // send a syn
tcp_client_state = 2;
tcpclient_src_port_l++; // allocate a new port
client_syn(((tcp_fd & lt; & lt; 5) | (0x1f & tcpclient_src_port_l)),tcp_client_port_h,tcp_client_port_l);
}
#endif
//!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed.
if(is_lan(myip, dnsip) & & !has_dns_mac & & !waiting_for_dns_mac) {
client_arp_whohas(dnsip);
waiting_for_dns_mac = true;
}
//!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed.
if(is_lan(myip, hisip) & & !has_dest_mac & & !waiting_for_dest_mac) {
client_arp_whohas(hisip);
waiting_for_dest_mac = true;
}
return 0;
}
if (eth_type_is_arp_and_my_ip(plen))
{ //Service ARP request
if (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V)
make_arp_answer_from_request();
if (waitgwmac & WGW_ACCEPT_ARP_REPLY & & (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V) & & client_store_mac(gwip, gwmacaddr))
waitgwmac = WGW_HAVE_GW_MAC;
if (!has_dns_mac & & waiting_for_dns_mac & & client_store_mac(dnsip, destmacaddr)) {
has_dns_mac = true;
waiting_for_dns_mac = false;
}
if (!has_dest_mac & & waiting_for_dest_mac & & client_store_mac(hisip, destmacaddr)) {
has_dest_mac = true;
waiting_for_dest_mac = false;
}
return 0;
}
if (eth_type_is_ip_and_my_ip(plen)==0)
{ //Not IP so ignoring
//!@todo Add other protocols (and make each optional at compile time)
return 0;
}
#if ETHERCARD_ICMP
if (gPB[IP_PROTO_P]==IP_PROTO_ICMP_V & & gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V)
{ //Service ICMP echo request (ping)
if (icmp_cb)
(*icmp_cb)( & (gPB[IP_SRC_P]));
make_echo_reply_from_request(plen);
return 0;
}
#endif
#if ETHERCARD_UDPSERVER
if (ether.udpServerListening() & & gPB[IP_PROTO_P]==IP_PROTO_UDP_V)
{ //Call UDP server handler (callback) if one is defined for this packet
if(ether.udpServerHasProcessedPacket(plen))
return 0; //An UDP server handler (callback) has processed this packet
}
#endif
if (plen & lt; 54 || gPB[IP_PROTO_P]!=IP_PROTO_TCP_V )
return 0; //from here on we are only interested in TCP-packets; these are longer than 54 bytes
#if ETHERCARD_TCPCLIENT
if (gPB[TCP_DST_PORT_H_P]==TCPCLIENT_SRC_PORT_H)
{ //Source port is in range reserved (by EtherCard) for client TCP/IP connections
if (check_ip_message_is_from(hisip)==0)
return 0; //Not current TCP/IP connection (only handle one at a time)
if (gPB[TCP_FLAGS_P] & TCP_FLAGS_RST_V)
{ //TCP reset flagged
if (client_tcp_result_cb)
(*client_tcp_result_cb)((gPB[TCP_DST_PORT_L_P] & gt; & gt; 5) & 0x7,3,0,0);
tcp_client_state = 5;
return 0;
}
len = getTcpPayloadLength();
if (tcp_client_state==2)
{ //Waiting for SYN-ACK
if ((gPB[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) & & (gPB[TCP_FLAGS_P] & TCP_FLAGS_ACK_V))
{ //SYN and ACK flags set so this is an acknowledgement to our SYN
make_tcp_ack_from_any(0,0);
gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V;
if (client_tcp_datafill_cb)
len = (*client_tcp_datafill_cb)((gPB[TCP_SRC_PORT_L_P] & gt; & gt; 5) & 0x7);
else
len = 0;
tcp_client_state = 3;
make_tcp_ack_with_data_noflags(len);
}
else
{ //Expecting SYN+ACK so reset and resend SYN
tcp_client_state = 1; // retry
len++;
if (gPB[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)
len = 0;
make_tcp_ack_from_any(len,TCP_FLAGS_RST_V);
}
return 0;
}
if (tcp_client_state==3 & & len & gt; 0)
{ //TCP connection established so read data
if (client_tcp_result_cb) {
uint16_t tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula
if (tcpstart & gt; plen-8)
tcpstart = plen-8; // dummy but save
uint16_t save_len = len;
if (tcpstart+len & gt; plen)
save_len = plen-tcpstart;
(*client_tcp_result_cb)((gPB[TCP_DST_PORT_L_P] & gt; & gt; 5) & 0x7,0,tcpstart,save_len); //Call TCP handler (callback) function
if(persist_tcp_connection)
{ //Keep connection alive by sending ACK
make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V);
}
else
{ //Close connection
make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V);
tcp_client_state = 6;
}
return 0;
}
}
if (tcp_client_state != 5)
{ //
if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) {
if(tcp_client_state == 3) {
return 0; // In some instances FIN is received *before* DATA. If that is the case, we just return here and keep looking for the data packet
}
make_tcp_ack_from_any(len+1,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V);
tcp_client_state = 6; // connection terminated
} else if (len & gt; 0) {
make_tcp_ack_from_any(len,0);
}
}
return 0;
}
#endif
#if ETHERCARD_TCPSERVER
//If we are here then this is a TCP/IP packet targetted at us and not related to our client connection so accept
return accept(hisport, plen);
#endif
}
void EtherCard::persistTcpConnection(bool persist) {
persist_tcp_connection = persist;
}
// Simple UDP listening server
//
// Author: Brian Lee
//
// Copyright: GPL V2
// See http://www.gnu.org/licenses/gpl.html
#include " EtherCard.h "
#include " net.h "
#define gPB ether.buffer
#define UDPSERVER_MAXLISTENERS 8 //the maximum number of port listeners.
typedef struct {
UdpServerCallback callback;
uint16_t port;
bool listening;
} UdpServerListener;
UdpServerListener listeners[UDPSERVER_MAXLISTENERS];
byte numListeners = 0;
void EtherCard::udpServerListenOnPort(UdpServerCallback callback, uint16_t port) {
if(numListeners & lt; UDPSERVER_MAXLISTENERS)
{
listeners[numListeners] = (UdpServerListener) {
callback, port, true
};
numListeners++;
}
}
void EtherCard::udpServerPauseListenOnPort(uint16_t port) {
for(int i = 0; i & lt; numListeners; i++)
{
if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port & gt; & gt; 8) & & gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port)) {
listeners[i].listening = false;
}
}
}
void EtherCard::udpServerResumeListenOnPort(uint16_t port) {
for(int i = 0; i & lt; numListeners; i++)
{
if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port & gt; & gt; 8) & & gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port)) {
listeners[i].listening = true;
}
}
}
bool EtherCard::udpServerListening() {
return numListeners & gt; 0;
}
bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) {
bool packetProcessed = false;
for(int i = 0; i & lt; numListeners; i++)
{
if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port & gt; & gt; 8) & & gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port) & & listeners[i].listening)
{
uint16_t datalen = (uint16_t) (gPB[UDP_LEN_H_P] & lt; & lt; 8) + gPB[UDP_LEN_L_P] - UDP_HEADER_LEN;
listeners[i].callback(
listeners[i].port,
gPB + IP_SRC_P,
(gPB[UDP_SRC_PORT_H_P] & lt; & lt; 8) | gPB[UDP_SRC_PORT_L_P],
(const char *) (gPB + UDP_DATA_P),
datalen);
packetProcessed = true;
}
}
return packetProcessed;
}
// Microchip ENC28J60 Ethernet Interface Driver
// Author: Pascal Stang
// Modified by: Guido Socher
// Copyright: GPL V2
//
// This driver provides initialization and transmit/receive
// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY.
// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin
// chip, using an SPI interface to the host processor.
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
/** @file */
#ifndef ENC28J60_H
#define ENC28J60_H
// buffer boundaries applied to internal 8K ram
// the entire available packet buffer space is allocated
#define RXSTART_INIT 0x0000 // start of RX buffer, (must be zero, Rev. B4 Errata point 5)
#define RXSTOP_INIT 0x0BFF // end of RX buffer, room for 2 packets
#define TXSTART_INIT 0x0C00 // start of TX buffer, room for 1 packet
#define TXSTOP_INIT 0x11FF // end of TX buffer
#define SCRATCH_START 0x1200 // start of scratch area
#define SCRATCH_LIMIT 0x2000 // past end of area, i.e. 3 Kb
#define SCRATCH_PAGE_SHIFT 6 // addressing is in pages of 64 bytes
#define SCRATCH_PAGE_SIZE (1 & lt; & lt; SCRATCH_PAGE_SHIFT)
#define SCRATCH_PAGE_NUM ((SCRATCH_LIMIT-SCRATCH_START) & gt; & gt; SCRATCH_PAGE_SHIFT)
#define SCRATCH_MAP_SIZE (((SCRATCH_PAGE_NUM % 8) == 0) ? (SCRATCH_PAGE_NUM / 8) : (SCRATCH_PAGE_NUM/8+1))
// area in the enc memory that can be used via enc_malloc; by default 0 bytes; decrease SCRATCH_LIMIT in order
// to use this functionality
#define ENC_HEAP_START SCRATCH_LIMIT
#define ENC_HEAP_END 0x2000
/** This class provide low-level interfacing with the ENC28J60 network interface. This is used by the EtherCard class and not intended for use by (normal) end users. */
class ENC28J60 {
public:
static uint8_t buffer[]; //! & lt; Data buffer (shared by recieve and transmit)
static uint16_t bufferSize; //! & lt; Size of data buffer
static bool broadcast_enabled; //! & lt; True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions)
static bool promiscuous_enabled; //! & lt; True if promiscuous mode enabled (used to allow temporary disable of promiscuous mode)
static uint8_t* tcpOffset () { return buffer + 0x36; } //! & lt; Pointer to the start of TCP payload
/** @brief Initialise SPI interface
* @note Configures Arduino pins as input / output, etc.
*/
static void initSPI ();
/** @brief Initialise network interface
* @param size Size of data buffer
* @param macaddr Pointer to 6 byte hardware (MAC) address
* @param csPin Arduino pin used for chip select (enable network interface SPI bus). Default = 8
* @return & lt; i & gt; uint8_t & lt; /i & gt; ENC28J60 firmware version or zero on failure.
*/
static uint8_t initialize (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin = 8);
/** @brief Check if network link is connected
* @return & lt; i & gt; bool & lt; /i & gt; True if link is up
*/
static bool isLinkUp ();
/** @brief Sends data to network interface
* @param len Size of data to send
* @note Data buffer is shared by recieve and transmit functions
*/
static void packetSend (uint16_t len);
/** @brief Copy recieved packets to data buffer
* @return & lt; i & gt; uint16_t & lt; /i & gt; Size of recieved data
* @note Data buffer is shared by recieve and transmit functions
*/
static uint16_t packetReceive ();
/** @brief Copy data from ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data to
*/
static void copyout (uint8_t page, const uint8_t* data);
/** @brief Copy data to ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data from
*/
static void copyin (uint8_t page, uint8_t* data);
/** @brief Get single byte of data from ENC28J60 memory
* @param page Data page of memory
* @param off Offset of data within page
* @return Data value
*/
static uint8_t peekin (uint8_t page, uint8_t off);
/** @brief Put ENC28J60 in sleep mode
*/
static void powerDown(); // contrib by Alex M.
/** @brief Wake ENC28J60 from sleep mode
*/
static void powerUp(); // contrib by Alex M.
/** @brief Enable reception of broadcast messages
* @param temporary Set true to temporarily enable broadcast
* @note This will increase load on recieved data handling
*/
static void enableBroadcast(bool temporary = false);
/** @brief Disable reception of broadcast messages
* @param temporary Set true to only disable if temporarily enabled
* @note This will reduce load on recieved data handling
*/
static void disableBroadcast(bool temporary = false);
/** @brief Enables reception of mulitcast messages
* @note This will increase load on recieved data handling
*/
static void enableMulticast ();
/** @brief Enables reception of all messages
* @param temporary Set true to temporarily enable promiscuous
* @note This will increase load significantly on recieved data handling
* @note All messages will be accepted, even messages with destination MAC other than own
* @note Messages with invalid CRC checksum will still be rejected
*/
static void enablePromiscuous (bool temporary = false);
/** @brief Disable reception of all messages and go back to default mode
* @param temporary Set true to only disable if temporarily enabled
* @note This will reduce load on received data handling
* @note In this mode only unicast and broadcast messages will be received
*/
static void disablePromiscuous(bool temporary = false);
/** @brief Disable reception of mulitcast messages
* @note This will reduce load on recieved data handling
*/
static void disableMulticast();
/** @brief Reset and fully initialise ENC28J60
* @param csPin Arduino pin used for chip select (enable SPI bus)
* @return & lt; i & gt; uint8_t & lt; /i & gt; 0 on failure
*/
static uint8_t doBIST(uint8_t csPin = 8);
/** @brief Copies a slice from the current packet to RAM
* @param dest pointer in RAM where the data is copied to
* @param maxlength how many bytes to copy;
* @param packetOffset where within the packet to start; if less than maxlength bytes are available only the remaining bytes are copied.
* @return & lt; i & gt; uint16_t & lt; /i & gt; the number of bytes that have been read
* @note At the destination at least maxlength+1 bytes should be reserved because the copied content will be 0-terminated.
*/
static uint16_t readPacketSlice(char* dest, int16_t maxlength, int16_t packetOffset);
/** @brief reserves a block of RAM in the memory of the enc chip
* @param size number of bytes to reserve
* @return & lt; i & gt; uint16_t & lt; /i & gt; start address of the block within the enc memory. 0 if the remaining memory for malloc operation is less than size.
* @note There is no enc_free(), i.e., reserved blocks stay reserved for the duration of the program.
* @note The total memory available for malloc-operations is determined by ENC_HEAP_END-ENC_HEAP_START, defined in enc28j60.h; by default this is 0, i.e., you have to change these values in order to use enc_malloc().
*/
static uint16_t enc_malloc(uint16_t size);
/** @brief returns the amount of memory within the enc28j60 chip that is still available for malloc.
* @return & lt; i & gt; uint16_t & lt; /i & gt; the amount of memory in bytes.
*/
static uint16_t enc_freemem();
/** @brief copies a block of data from SRAM to the enc memory
@param dest destination address within enc memory
@param source source pointer to a block of SRAM in the arduino
@param num number of bytes to copy
@note There is no sanity check. Handle with care
*/
static void memcpy_to_enc(uint16_t dest, void* source, int16_t num);
/** @brief copies a block of data from the enc memory to SRAM
@param dest destination address within SRAM
@param source source address within enc memory
@param num number of bytes to copy
*/
static void memcpy_from_enc(void* dest, uint16_t source, int16_t num);
};
typedef ENC28J60 Ethernet; //! & lt; Define alias Ethernet for ENC28J60
/** Workaround for Errata 13.
* The transmission hardware may drop some packets because it thinks a late collision
* occurred (which should never happen if all cable length etc. are ok). If setting
* this to 1 these packages will be retried a fixed number of times. Costs about 150bytes
* of flash.
*/
#define ETHERCARD_RETRY_LATECOLLISIONS 0
/** Enable pipelining of packet transmissions.
* If enabled the packetSend function will not block/wait until the packet is actually
* transmitted; but instead this wait is shifted to the next time that packetSend is
* called. This gives higher performance; however in combination with
* ETHERCARD_RETRY_LATECOLLISIONS this may lead to problems because a packet whose
* transmission fails because the ENC-chip thinks that it is a late collision will
* not be retried until the next call to packetSend.
*/
#define ETHERCARD_SEND_PIPELINING 0
#endif
// This code slightly follows the conventions of, but is not derived from:
// EHTERSHIELD_H library for Arduino etherShield
// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1)
// It is however derived from the enc28j60 and ip code (which is GPL v2)
// Author: Pascal Stang
// Modified by: Guido Socher
// DHCP code: Andrew Lindsay
// Hence: GPL V2
//
// 2010-05-19 & lt; jc@wippler.nl & gt;
//
//
// PIN Connections (Using Arduino UNO):
// VCC - 3.3V
// GND - GND
// SCK - Pin 13
// SO - Pin 12
// SI - Pin 11
// CS - Pin 8
//
/** @file */
#ifndef EtherCard_h
#define EtherCard_h
#ifndef __PROG_TYPES_COMPAT__
#define __PROG_TYPES_COMPAT__
#endif
#if ARDUINO & gt; = 100
#include & lt; Arduino.h & gt; // Arduino 1.0
#define WRITE_RESULT size_t
#define WRITE_RETURN return 1;
#else
#include & lt; WProgram.h & gt; // Arduino 0022
#define WRITE_RESULT void
#define WRITE_RETURN
#endif
#include & lt; avr/pgmspace.h & gt;
#include " enc28j60.h "
#include " net.h "
/** Enable DHCP.
* Setting this to zero disables the use of DHCP; if a program uses DHCP it will
* still compile but the program will not work. Saves about 60 bytes SRAM and
* 1550 bytes flash.
*/
#define ETHERCARD_DHCP 1
/** Enable client connections.
* Setting this to zero means that the program cannot issue TCP client requests
* anymore. Compilation will still work but the request will never be
* issued. Saves 4 bytes SRAM and 550 byte flash.
*/
#define ETHERCARD_TCPCLIENT 1
/** Enable TCP server functionality.
* Setting this to zero means that the program will not accept TCP client
* requests. Saves 2 bytes SRAM and 250 bytes flash.
*/
#define ETHERCARD_TCPSERVER 1
/** Enable UDP server functionality.
* If zero UDP server is disabled. It is
* still possible to register callbacks but these will never be called. Saves
* about 40 bytes SRAM and 200 bytes flash. If building with -flto this does not
* seem to save anything; maybe the linker is then smart enough to optimize the
* call away.
*/
#define ETHERCARD_UDPSERVER 1
/** Enable automatic reply to pings.
* Setting to zero means that the program will not automatically answer to
* PINGs anymore. Also the callback that can be registered to answer incoming
* pings will not be called. Saves 2 bytes SRAM and 230 bytes flash.
*/
#define ETHERCARD_ICMP 1
/** Enable use of stash.
* Setting this to zero means that the stash mechanism cannot be used. Again
* compilation will still work but the program may behave very unexpectedly.
* Saves 30 bytes SRAM and 80 bytes flash.
*/
#define ETHERCARD_STASH 1
/** This type definition defines the structure of a UDP server event handler callback funtion */
typedef void (*UdpServerCallback)(
uint16_t dest_port, /// & lt; Port the packet was sent to
uint8_t src_ip[4], /// & lt; IP address of the sender
uint16_t src_port, /// & lt; Port the packet was sent from
const char *data, /// & lt; UDP payload data
uint16_t len); /// & lt; Length of the payload data
/** This type definition defines the structure of a DHCP Option callback funtion */
typedef void (*DhcpOptionCallback)(
uint8_t option, /// & lt; The option number
const byte* data, /// & lt; DHCP option data
uint8_t len); /// & lt; Length of the DHCP option data
/** This structure describes the structure of memory used within the ENC28J60 network interface. */
typedef struct {
uint8_t count; /// & lt; Number of allocated pages
uint8_t first; /// & lt; First allocated page
uint8_t last; /// & lt; Last allocated page
} StashHeader;
/** This class provides access to the memory within the ENC28J60 network interface. */
class Stash : public /*Stream*/ Print, private StashHeader {
uint8_t curr; //! & lt; Current page
uint8_t offs; //! & lt; Current offset in page
typedef struct {
union {
uint8_t bytes[64];
uint16_t words[32];
struct {
StashHeader head; // StashHeader is only stored in first block
uint8_t filler[59];
uint8_t tail; // only meaningful if bnum==last; number of bytes in last block
uint8_t next; // pointer to next block
};
};
uint8_t bnum;
} Block;
static uint8_t allocBlock ();
static void freeBlock (uint8_t block);
static uint8_t fetchByte (uint8_t blk, uint8_t off);
static Block bufs[2];
static uint8_t map[SCRATCH_MAP_SIZE];
public:
static void initMap (uint8_t last=SCRATCH_PAGE_NUM);
static void load (uint8_t idx, uint8_t blk);
static uint8_t freeCount ();
Stash () : curr (0) { first = 0; }
Stash (uint8_t fd) { open(fd); }
uint8_t create ();
uint8_t open (uint8_t blk);
void save ();
void release ();
void put (char c);
char get ();
uint16_t size ();
virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN }
// virtual int available() {
// if (curr != last)
// return 1;
// load(1, last);
// return offs & lt; bufs[1].tail;
// }
// virtual int read() {
// return available() ? get() : -1;
// }
// virtual int peek() {
// return available() ? bufs[1].bytes[offs] : -1;
// }
// virtual void flush() {
// curr = last;
// offs = 63;
// }
static void prepare (PGM_P fmt, ...);
static uint16_t length ();
static void extract (uint16_t offset, uint16_t count, void* buf);
static void cleanup ();
friend void dumpBlock (const char* msg, uint8_t idx); // optional
friend void dumpStash (const char* msg, void* ptr); // optional
};
/** This class populates network send and receive buffers.
*
* This class provides formatted printing into memory. Users can use it to write into send buffers.
*
* Nota: PGM_P: is a pointer to a string in program space (defined in the source code)
*
* # Format string
*
* | Format | Parameter | Output
* |--------|-------------|----------
* | $D | uint16_t | Decimal representation
* | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd)
* | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff)
* | $L | long | Decimal representation
* | $S | const char* | Copy null terminated string from main memory
* | $F | PGM_P | Copy null terminated string from program space
* | $E | byte* | Copy null terminated string from EEPROM space
* | $$ | _none_ | '$'
*
* ¤ _Available only if FLOATEMIT is defined_
*
* # Examples
* ~~~~~~~~~~~~~{.c}
* uint16_t ddd = 123;
* double ttt = 1.23;
* uint16_t hhh = 0xa4;
* long lll = 123456789;
* char * sss;
* char fff[] PROGMEM = " MyMemory " ;
*
* sss[0] = 'G';
* sss[1] = 'P';
* sss[2] = 'L';
* sss[3] = 0;
* buf.emit_p( PSTR( " ddd=$D\n " ), ddd ); // " ddd=123\n "
* buf.emit_p( PSTR( " ttt=$T\n " ), ttt ); // " ttt=1.23\n " **TO CHECK**
* buf.emit_p( PSTR( " hhh=$H\n " ), hhh ); // " hhh=a4\n "
* buf.emit_p( PSTR( " lll=$L\n " ), lll ); // " lll=123456789\n "
* buf.emit_p( PSTR( " sss=$S\n " ), sss ); // " sss=GPL\n "
* buf.emit_p( PSTR( " fff=$F\n " ), fff ); // " fff=MyMemory\n "
* ~~~~~~~~~~~~~
*
*/
class BufferFiller : public Print {
uint8_t *start; //! & lt; Pointer to start of buffer
uint8_t *ptr; //! & lt; Pointer to cursor position
public:
/** @brief Empty constructor
*/
BufferFiller () {}
/** @brief Constructor
* @param buf Pointer to the ethernet data buffer
*/
BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {}
/** @brief Add formatted text to buffer
* @param fmt Format string (see Class description)
* @param ... parameters for format string
*/
void emit_p (PGM_P fmt, ...);
/** @brief Add data to buffer from main memory
* @param s Pointer to data
* @param n Number of characters to copy
*/
void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; }
/** @brief Add data to buffer from program space string
* @param p Program space string pointer
* @param n Number of characters to copy
*/
void emit_raw_p (PGM_P p, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; }
/** @brief Get pointer to start of buffer
* @return & lt; i & gt; uint8_t* & lt; /i & gt; Pointer to start of buffer
*/
uint8_t* buffer () const { return start; }
/** @brief Get cursor position
* @return & lt; i & gt; uint16_t & lt; /i & gt; Cursor postion
*/
uint16_t position () const { return ptr - start; }
/** @brief Write one byte to buffer
* @param v Byte to add to buffer
*/
virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN }
};
/** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use.
* @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes.
*/
class EtherCard : public Ethernet {
public:
static uint8_t mymac[6]; /// & lt; MAC address
static uint8_t myip[4]; /// & lt; IP address
static uint8_t netmask[4]; /// & lt; Netmask
static uint8_t broadcastip[4]; /// & lt; Subnet broadcast address
static uint8_t gwip[4]; /// & lt; Gateway
static uint8_t dhcpip[4]; /// & lt; DHCP server IP address
static uint8_t dnsip[4]; /// & lt; DNS server IP address
static uint8_t hisip[4]; /// & lt; DNS lookup result
static uint16_t hisport; /// & lt; TCP port to connect to (default 80)
static bool using_dhcp; /// & lt; True if using DHCP
static bool persist_tcp_connection; /// & lt; False to break connections on first packet received
static uint16_t delaycnt; /// & lt; Counts number of cycles of packetLoop when no packet received - used to trigger periodic gateway ARP request
// EtherCard.cpp
/** @brief Initialise the network interface
* @param size Size of data buffer
* @param macaddr Hardware address to assign to the network interface (6 bytes)
* @param csPin Arduino pin number connected to chip select. Default = 8
* @return & lt; i & gt; uint8_t & lt; /i & gt; Firmware version or zero on failure.
*/
static uint8_t begin (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin =8);
/** @brief Configure network interface with static IP
* @param my_ip IP address (4 bytes). 0 for no change.
* @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0
* @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0
* @param mask Subnet mask (4 bytes). 0 for no change. Default = 0
* @return & lt; i & gt; bool & lt; /i & gt; Returns true on success - actually always true
*/
static bool staticSetup (const uint8_t* my_ip,
const uint8_t* gw_ip = 0,
const uint8_t* dns_ip = 0,
const uint8_t* mask = 0);
// tcpip.cpp
/** @brief Sends a UDP packet to the IP address of last processed received packet
* @param data Pointer to data payload
* @param len Size of data payload (max 220)
* @param port Source IP port
*/
static void makeUdpReply (const char *data, uint8_t len, uint16_t port);
/** @brief Parse received data
* @param plen Size of data to parse (e.g. return value of packetReceive()).
* @return & lt; i & gt; uint16_t & lt; /i & gt; Offset of TCP payload data in data buffer or zero if packet processed
* @note Data buffer is shared by receive and transmit functions
* @note Only handles ARP and IP
*/
static uint16_t packetLoop (uint16_t plen);
/** @brief Accept a TCP/IP connection
* @param port IP port to accept on - do nothing if wrong port
* @param plen Number of bytes in packet
* @return & lt; i & gt; uint16_t & lt; /i & gt; Offset within packet of TCP payload. Zero for no data.
*/
static uint16_t accept (uint16_t port, uint16_t plen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
*/
static void httpServerReply (uint16_t dlen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
* @param flags TCP flags
*/
static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags);
/** @brief Acknowledge TCP message
* @todo Is this / should this be private?
*/
static void httpServerReplyAck ();
/** @brief Set the gateway address
* @param gwipaddr Gateway address (4 bytes)
*/
static void setGwIp (const uint8_t *gwipaddr);
/** @brief Updates the broadcast address based on current IP address and subnet mask
*/
static void updateBroadcastAddress();
/** @brief Check if got gateway hardware address (ARP lookup)
* @return & lt; i & gt; unit8_t & lt; /i & gt; True if gateway found
*/
static uint8_t clientWaitingGw ();
/** @brief Check if got gateway DNS address (ARP lookup)
* @return & lt; i & gt; unit8_t & lt; /i & gt; True if DNS found
*/
static uint8_t clientWaitingDns ();
/** @brief Prepare a TCP request
* @param result_cb Pointer to callback function that handles TCP result
* @param datafill_cb Pointer to callback function that handles TCP data payload
* @param port Remote TCP/IP port to connect to
* @return & lt; i & gt; unit8_t & lt; /i & gt; ID of TCP/IP session (0-7)
* @note Return value provides id of the request to allow up to 7 concurrent requests
*/
static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t),
uint16_t (*datafill_cb)(uint8_t),uint16_t port);
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr, const char *additionalheaderline,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP post message
* @param urlbuf Pointer to c-string URL folder
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param postval Pointer to c-string HTML Post value
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void httpPost (const char *urlbuf, const char *hoststr,
const char *additionalheaderline, const char *postval,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Send NTP request
* @param ntpip IP address of NTP server
* @param srcport IP port to send from
*/
static void ntpRequest (uint8_t *ntpip,uint8_t srcport);
/** @brief Process network time protocol response
* @param time Pointer to integer to hold result
* @param dstport_l Destination port to expect response. Set to zero to accept on any port
* @return & lt; i & gt; uint8_t & lt; /i & gt; True (1) on success
*/
static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l);
/** @brief Prepare a UDP message for transmission
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport);
/** @brief Transmit UDP packet
* @param len Size of payload
*/
static void udpTransmit (uint16_t len);
/** @brief Sends a UDP packet
* @param data Pointer to data
* @param len Size of payload (maximum 220 octets / bytes)
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void sendUdp (const char *data, uint8_t len, uint16_t sport,
const uint8_t *dip, uint16_t dport);
/** @brief Resister the function to handle ping events
* @param cb Pointer to function
*/
static void registerPingCallback (void (*cb)(uint8_t*));
/** @brief Send ping
* @param destip Ponter to 4 byte destination IP address
*/
static void clientIcmpRequest (const uint8_t *destip);
/** @brief Check for ping response
* @param ip_monitoredhost Pointer to 4 byte IP address of host to check
* @return & lt; i & gt; uint8_t & lt; /i & gt; True (1) if ping response from specified host
*/
static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost);
/** @brief Send a wake on lan message
* @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to
*/
static void sendWol (uint8_t *wolmac);
// new stash-based API
/** @brief Send TCP request
*/
static uint8_t tcpSend ();
/** @brief Get TCP reply
* @return & lt; i & gt; char* & lt; /i & gt; Pointer to TCP reply payload. NULL if no data.
*/
static const char* tcpReply (uint8_t fd);
/** @brief Configure TCP connections to be persistent or not
* @param persist True to maintain TCP connection. False to finish TCP connection after first packet.
*/
static void persistTcpConnection(bool persist);
//udpserver.cpp
/** @brief Register function to handle incomint UDP events
* @param callback Function to handle event
* @param port Port to listen on
*/
static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port);
/** @brief Pause listing on UDP port
* @brief port Port to pause
*/
static void udpServerPauseListenOnPort(uint16_t port);
/** @brief Resume listing on UDP port
* @brief port Port to pause
*/
static void udpServerResumeListenOnPort(uint16_t port);
/** @brief Check if UDP server is listening on any ports
* @return & lt; i & gt; bool & lt; /i & gt; True if listening on any ports
*/
static bool udpServerListening(); //called by tcpip, in packetLoop
/** @brief Passes packet to UDP Server
* @param len Not used
* @return & lt; i & gt; bool & lt; /i & gt; True if packet processed
*/
static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop
// dhcp.cpp
/** @brief Update DHCP state
* @param len Length of received data packet
*/
static void DhcpStateMachine(uint16_t len);
/** @brief Not implemented
* @todo Implement dhcpStartTime or remove declaration
*/
static uint32_t dhcpStartTime ();
/** @brief Not implemented
* @todo Implement dhcpLeaseTime or remove declaration
*/
static uint32_t dhcpLeaseTime ();
/** @brief Not implemented
* @todo Implement dhcpLease or remove declaration
*/
static bool dhcpLease ();
/** @brief Configure network interface with DHCP
* @return & lt; i & gt; bool & lt; /i & gt; True if DHCP successful
* @note Blocks until DHCP complete or timeout after 60 seconds
*/
static bool dhcpSetup (const char *hname = NULL, bool fromRam =false);
/** @brief Register a callback for a specific DHCP option number
* @param option The option number to request from the DHCP server
* @param callback The function to be call when the option is received
*/
static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback);
// dns.cpp
/** @brief Perform DNS lookup
* @param name Host name to lookup
* @param fromRam Set true to look up cached name. Default = false
* @return & lt; i & gt; bool & lt; /i & gt; True on success.
* @note Result is stored in & lt; i & gt; hisip & lt; /i & gt; member
*/
static bool dnsLookup (const char* name, bool fromRam =false);
// webutil.cpp
/** @brief Copies an IP address
* @param dst Pointer to the 4 byte destination
* @param src Pointer to the 4 byte source
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void copyIp (uint8_t *dst, const uint8_t *src);
/** @brief Copies a hardware address
* @param dst Pointer to the 6 byte destination
* @param src Pointer to the 6 byte destination
* @note There is no check of source or destination size. Ensure both are 6 bytes
*/
static void copyMac (uint8_t *dst, const uint8_t *src);
/** @brief Output to serial port in dotted decimal IP format
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const uint8_t *buf);
/** @brief Output message and IP address to serial port in dotted decimal IP format
* @param msg Pointer to null terminated string
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const char* msg, const uint8_t *buf);
/** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format
* @param ifsh Pointer to Flash String Helper
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
* @todo What is a FlashStringHelper?
*/
static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf);
/** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc & uvw=defgh HTTP/1.1\\r\\n
* @param str Pointer to the null terminated string to search
* @param strbuf Pointer to buffer to hold null terminated result string
* @param maxlen Maximum length of result
* @param key Pointer to null terminated string holding the key to search for
* @return & lt; i & gt; unit_t & lt; /i & gt; Length of the value. 0 if not found
* @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null)
*/
static uint8_t findKeyVal(const char *str,char *strbuf,
uint8_t maxlen, const char *key);
/** @brief Decode a URL string e.g " hello%20joe " or " hello+joe " becomes " hello joe "
* @param urlbuf Pointer to the null terminated URL
* @note urlbuf is modified
*/
static void urlDecode(char *urlbuf);
/** @brief Encode a URL, replacing illegal charaters like ' '
* @param str Pointer to the null terminated string to encode
* @param urlbuf Pointer to a buffer to contain the null terminated encoded URL
* @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str
*/
static void urlEncode(char *str,char *urlbuf);
/** @brief Convert an IP address from dotted decimal formated string to 4 bytes
* @param bytestr Pointer to the 4 byte array to store IP address
* @param str Pointer to string to parse
* @return & lt; i & gt; uint8_t & lt; /i & gt; 0 on success
*/
static uint8_t parseIp(uint8_t *bytestr,char *str);
/** @brief Convert a byte array to a human readable display string
* @param resultstr Pointer to a buffer to hold the resulting null terminated string
* @param bytestr Pointer to the byte array containing the address to convert
* @param len Length of the array (4 for IP address, 6 for hardware (MAC) address)
* @param separator Delimiter character (typically '.' for IP address and ':' for hardware (MAC) address)
* @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address
*/
static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len,
char separator,uint8_t base);
/** @brief Return the sequence number of the current TCP package
*/
static uint32_t getSequenceNumber();
/** @brief Return the payload length of the current Tcp package
*/
static uint16_t getTcpPayloadLength();
};
extern EtherCard ether; //! & lt; Global presentation of EtherCard class
#endif
// DNS look-up functions based on the udp client
// Author: Guido Socher
// Copyright: GPL V2
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
#include " EtherCard.h "
#include " net.h "
#define gPB ether.buffer
static byte dnstid_l; // a counter for transaction ID
#define DNSCLIENT_SRC_PORT_H 0xE0
static void dnsRequest (const char *hostname, bool fromRam) {
++dnstid_l; // increment for next request, finally wrap
if (ether.dnsip[0] == 0)
memset(ether.dnsip, 8, 4); // use 8.8.8.8 Google DNS as default
ether.udpPrepare((DNSCLIENT_SRC_PORT_H & lt; & lt; 8) | dnstid_l, ether.dnsip, 53);
memset(gPB + UDP_DATA_P, 0, 12);
byte *p = gPB + UDP_DATA_P + 12;
char c;
do {
byte n = 0;
for(;;) {
c = fromRam ? *hostname : pgm_read_byte(hostname);
++hostname;
if (c == '.' || c == 0)
break;
p[++n] = c;
}
*p++ = n;
p += n;
} while (c != 0);
*p++ = 0; // terminate with zero, means root domain.
*p++ = 0;
*p++ = 1; // type A
*p++ = 0;
*p++ = 1; // class IN
byte i = p - gPB - UDP_DATA_P;
gPB[UDP_DATA_P] = i;
gPB[UDP_DATA_P+1] = dnstid_l;
gPB[UDP_DATA_P+2] = 1; // flags, standard recursive query
gPB[UDP_DATA_P+5] = 1; // 1 question
ether.udpTransmit(i);
}
/** @brief Check if packet is DNS response.
@param plen Size of packet
@return & lt; i & gt; bool & lt; /i & gt; True if DNS response has error. False if not DNS response or DNS response OK.
@note hisip contains IP address of requested host or 0.0.0.0 on failure
*/
static bool checkForDnsAnswer (uint16_t plen) {
byte *p = gPB + UDP_DATA_P; //start of UDP payload
if (plen & lt; 70 || gPB[UDP_SRC_PORT_L_P] != 53 || //from DNS source port
gPB[UDP_DST_PORT_H_P] != DNSCLIENT_SRC_PORT_H || //response to same port as we sent from (MSB)
gPB[UDP_DST_PORT_L_P] != dnstid_l || //response to same port as we sent from (LSB)
p[1] != dnstid_l) //message id same as we sent
return false; //not our DNS response
if((p[3] & 0x0F) != 0)
return true; //DNS response recieved with error
p += *p; // we encoded the query len into tid
for (;;) {
if (*p & 0xC0)
p += 2;
else
while (++p & lt; gPB + plen) {
if (*p == 0) {
++p;
break;
}
}
if (p + 14 & gt; gPB + plen)
break;
if (p[1] == 1 & & p[9] == 4) { // type " A " and IPv4
ether.copyIp(ether.hisip, p + 10);
break;
}
p += p[9] + 10;
}
return false; //No error
}
// use during setup, as this discards all incoming requests until it returns
bool EtherCard::dnsLookup (const char* name, bool fromRam) {
uint16_t start = millis();
while(!isLinkUp())
{
if (uint16_t(millis()) - start & gt; = 30000)
return false; //timeout waiting for link
}
while(clientWaitingDns())
{
packetLoop(packetReceive());
if (uint16_t(millis()) - start & gt; = 30000)
return false; //timeout waiting for gateway ARP
}
memset(hisip, 0, 4);
dnsRequest(name, fromRam);
start = millis();
while (hisip[0] == 0) {
if (uint16_t(millis()) - start & gt; = 30000)
return false; //timout waiting for dns response
word len = packetReceive();
if (len & gt; 0 & & packetLoop(len) == 0) //packet not handled by tcp/ip packet loop
if(checkForDnsAnswer(len))
return false; //DNS response recieved with error
}
return true;
}
// Microchip ENC28J60 Ethernet Interface Driver
// Author: Guido Socher
// Copyright: GPL V2
//
// Based on the enc28j60.c file from the AVRlib library by Pascal Stang.
// For AVRlib See http://www.procyonengineering.com/
// Used with explicit permission of Pascal Stang.
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
#if ARDUINO & gt; = 100
#include & lt; Arduino.h & gt; // Arduino 1.0
#else
#include & lt; Wprogram.h & gt; // Arduino 0022
#endif
#include " enc28j60.h "
uint16_t ENC28J60::bufferSize;
bool ENC28J60::broadcast_enabled = false;
bool ENC28J60::promiscuous_enabled = false;
// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address (bits 0-4)
// - Bank number (bits 5-6)
// - MAC/PHY indicator (bit 7)
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
// All-bank registers
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// Bank 0 registers
#define ERDPT (0x00|0x00)
#define EWRPT (0x02|0x00)
#define ETXST (0x04|0x00)
#define ETXND (0x06|0x00)
#define ERXST (0x08|0x00)
#define ERXND (0x0A|0x00)
#define ERXRDPT (0x0C|0x00)
// #define ERXWRPT (0x0E|0x00)
#define EDMAST (0x10|0x00)
#define EDMAND (0x12|0x00)
// #define EDMADST (0x14|0x00)
#define EDMACS (0x16|0x00)
// Bank 1 registers
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCS (0x10|0x20)
// #define EPMO (0x14|0x20)
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
// Bank 2 registers
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPG (0x06|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFL (0x0A|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWR (0x16|0x40|0x80)
#define MIRD (0x18|0x40|0x80)
// Bank 3 registers
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCS (0x08|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUS (0x18|0x60)
// ENC28J60 ERXFCON Register Bit Definitions
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
// ENC28J60 EBSTCON Register Bit Definitions
#define EBSTCON_PSV2 0x80
#define EBSTCON_PSV1 0x40
#define EBSTCON_PSV0 0x20
#define EBSTCON_PSEL 0x10
#define EBSTCON_TMSEL1 0x08
#define EBSTCON_TMSEL0 0x04
#define EBSTCON_TME 0x02
#define EBSTCON_BISTST 0x01
// PHY registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
// SPI operation codes
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
// max frame length which the controller will accept:
// (note: maximum ethernet frame length would be 1518)
#define MAX_FRAMELEN 1500
#define FULL_SPEED 1 // switch to full-speed SPI for bulk transfers
static byte Enc28j60Bank;
static byte selectPin;
void ENC28J60::initSPI () {
pinMode(SS, OUTPUT);
digitalWrite(SS, HIGH);
pinMode(MOSI, OUTPUT);
pinMode(SCK, OUTPUT);
pinMode(MISO, INPUT);
digitalWrite(MOSI, HIGH);
digitalWrite(MOSI, LOW);
digitalWrite(SCK, LOW);
SPCR = bit(SPE) | bit(MSTR); // 8 MHz @ 16
bitSet(SPSR, SPI2X);
}
static void enableChip () {
cli();
digitalWrite(selectPin, LOW);
}
static void disableChip () {
digitalWrite(selectPin, HIGH);
sei();
}
static void xferSPI (byte data) {
SPDR = data;
while (!(SPSR & (1 & lt; & lt; SPIF)))
;
}
static byte readOp (byte op, byte address) {
enableChip();
xferSPI(op | (address & ADDR_MASK));
xferSPI(0x00);
if (address & 0x80)
xferSPI(0x00);
byte result = SPDR;
disableChip();
return result;
}
static void writeOp (byte op, byte address, byte data) {
enableChip();
xferSPI(op | (address & ADDR_MASK));
xferSPI(data);
disableChip();
}
static void readBuf(uint16_t len, byte* data) {
uint8_t nextbyte;
enableChip();
if (len != 0) {
xferSPI(ENC28J60_READ_BUF_MEM);
SPDR = 0x00;
while (--len) {
while (!(SPSR & (1 & lt; & lt; SPIF)))
;
nextbyte = SPDR;
SPDR = 0x00;
*data++ = nextbyte;
}
while (!(SPSR & (1 & lt; & lt; SPIF)))
;
*data++ = SPDR;
}
disableChip();
}
static void writeBuf(uint16_t len, const byte* data) {
enableChip();
if (len != 0) {
xferSPI(ENC28J60_WRITE_BUF_MEM);
SPDR = *data++;
while (--len) {
uint8_t nextbyte = *data++;
while (!(SPSR & (1 & lt; & lt; SPIF)))
;
SPDR = nextbyte;
};
while (!(SPSR & (1 & lt; & lt; SPIF)))
;
}
disableChip();
}
static void SetBank (byte address) {
if ((address & BANK_MASK) != Enc28j60Bank) {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0);
Enc28j60Bank = address & BANK_MASK;
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank & gt; & gt; 5);
}
}
static byte readRegByte (byte address) {
SetBank(address);
return readOp(ENC28J60_READ_CTRL_REG, address);
}
static uint16_t readReg(byte address) {
return readRegByte(address) + (readRegByte(address+1) & lt; & lt; 8);
}
static void writeRegByte (byte address, byte data) {
SetBank(address);
writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
}
static void writeReg(byte address, uint16_t data) {
writeRegByte(address, data);
writeRegByte(address + 1, data & gt; & gt; 8);
}
static uint16_t readPhyByte (byte address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (byte address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) {
bufferSize = size;
if (bitRead(SPCR, SPE) == 0)
initSPI();
selectPin = csPin;
pinMode(selectPin, OUTPUT);
disableChip();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay(2); // errata B7/2
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY)
;
writeReg(ERXST, RXSTART_INIT);
writeReg(ERXRDPT, RXSTART_INIT);
writeReg(ERXND, RXSTOP_INIT);
writeReg(ETXST, TXSTART_INIT);
writeReg(ETXND, TXSTOP_INIT);
writeRegByte(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN);
writeReg(EPMM0, 0x303f);
writeReg(EPMCS, 0xf7f9);
writeRegByte(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
writeRegByte(MACON2, 0x00);
writeOp(ENC28J60_BIT_FIELD_SET, MACON3,
MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
writeReg(MAIPG, 0x0C12);
writeRegByte(MABBIPG, 0x12);
writeReg(MAMXFL, MAX_FRAMELEN);
writeRegByte(MAADR5, macaddr[0]);
writeRegByte(MAADR4, macaddr[1]);
writeRegByte(MAADR3, macaddr[2]);
writeRegByte(MAADR2, macaddr[3]);
writeRegByte(MAADR1, macaddr[4]);
writeRegByte(MAADR0, macaddr[5]);
writePhy(PHCON2, PHCON2_HDLDIS);
SetBank(ECON1);
writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
byte rev = readRegByte(EREVID);
// microchip forgot to step the number on the silcon when they
// released the revision B7. 6 is now rev B7. We still have
// to see what they do when they release B8. At the moment
// there is no B8 out yet
if (rev & gt; 5) ++rev;
return rev;
}
bool ENC28J60::isLinkUp() {
return (readPhyByte(PHSTAT2) & gt; & gt; 2) & 1;
}
/*
struct __attribute__((__packed__)) transmit_status_vector {
uint16_t transmitByteCount;
byte transmitCollisionCount : 4;
byte transmitCrcError : 1;
byte transmitLengthCheckError : 1;
byte transmitLengthOutRangeError : 1;
byte transmitDone : 1;
byte transmitMulticast : 1;
byte transmitBroadcast : 1;
byte transmitPacketDefer : 1;
byte transmitExcessiveDefer : 1;
byte transmitExcessiveCollision : 1;
byte transmitLateCollision : 1;
byte transmitGiant : 1;
byte transmitUnderrun : 1;
uint16_t totalTransmitted;
byte transmitControlFrame : 1;
byte transmitPauseControlFrame : 1;
byte backpressureApplied : 1;
byte transmitVLAN : 1;
byte zero : 4;
};
*/
struct transmit_status_vector {
uint8_t bytes[7];
};
#if ETHERCARD_SEND_PIPELINING
#define BREAKORCONTINUE retry=0; continue;
#else
#define BREAKORCONTINUE break;
#endif
void ENC28J60::packetSend(uint16_t len) {
byte retry = 0;
#if ETHERCARD_SEND_PIPELINING
goto resume_last_transmission;
#endif
while (1) {
// latest errata sheet: DS80349C
// always reset transmit logic (Errata Issue 12)
// the Microchip TCP/IP stack implementation used to first check
// whether TXERIF is set and only then reset the transmit logic
// but this has been changed in later versions; possibly they
// have a reason for this; they don't mention this in the errata
// sheet
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_TXIF);
// prepare new transmission
if (retry == 0) {
writeReg(EWRPT, TXSTART_INIT);
writeReg(ETXND, TXSTART_INIT+len);
writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
writeBuf(len, buffer);
}
// initiate transmission
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
#if ETHERCARD_SEND_PIPELINING
if (retry == 0) return;
#endif
resume_last_transmission:
// wait until transmission has finished; referrring to the data sheet and
// to the errata (Errata Issue 13; Example 1) you only need to wait until either
// TXIF or TXERIF gets set; however this leads to hangs; apparently Microchip
// realized this and in later implementations of their tcp/ip stack they introduced
// a counter to avoid hangs; of course they didn't update the errata sheet
uint16_t count = 0;
while ((readRegByte(EIR) & (EIR_TXIF | EIR_TXERIF)) == 0 & & ++count & lt; 1000U)
;
if (!(readRegByte(EIR) & EIR_TXERIF) & & count & lt; 1000U) {
// no error; start new transmission
BREAKORCONTINUE
}
// cancel previous transmission if stuck
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
#if ETHERCARD_RETRY_LATECOLLISIONS == 0
BREAKORCONTINUE
#endif
// Check whether the chip thinks that a late collision ocurred; the chip
// may be wrong (Errata Issue 13); therefore we retry. We could check
// LATECOL in the ESTAT register in order to find out whether the chip
// thinks a late collision ocurred but (Errata Issue 15) tells us that
// this is not working. Therefore we check TSV
transmit_status_vector tsv;
uint16_t etxnd = readReg(ETXND);
writeReg(ERDPT, etxnd+1);
readBuf(sizeof(transmit_status_vector), (byte*) & tsv);
// LATECOL is bit number 29 in TSV (starting from 0)
if (!((readRegByte(EIR) & EIR_TXERIF) & & (tsv.bytes[3] & 1 & lt; & lt; 5) /*tsv.transmitLateCollision*/) || retry & gt; 16U) {
// there was some error but no LATECOL so we do not repeat
BREAKORCONTINUE
}
retry++;
}
}
uint16_t ENC28J60::packetReceive() {
static uint16_t gNextPacketPtr = RXSTART_INIT;
static bool unreleasedPacket = false;
uint16_t len = 0;
if (unreleasedPacket) {
if (gNextPacketPtr == 0)
writeReg(ERXRDPT, RXSTOP_INIT);
else
writeReg(ERXRDPT, gNextPacketPtr - 1);
unreleasedPacket = false;
}
if (readRegByte(EPKTCNT) & gt; 0) {
writeReg(ERDPT, gNextPacketPtr);
struct {
uint16_t nextPacket;
uint16_t byteCount;
uint16_t status;
} header;
readBuf(sizeof header, (byte*) & header);
gNextPacketPtr = header.nextPacket;
len = header.byteCount - 4; //remove the CRC count
if (len & gt; bufferSize-1)
len=bufferSize-1;
if ((header.status & 0x80)==0)
len = 0;
else
readBuf(len, buffer);
buffer[len] = 0;
unreleasedPacket = true;
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
return len;
}
void ENC28J60::copyout (byte page, const byte* data) {
uint16_t destPos = SCRATCH_START + (page & lt; & lt; SCRATCH_PAGE_SHIFT);
if (destPos & lt; SCRATCH_START || destPos & gt; SCRATCH_LIMIT - SCRATCH_PAGE_SIZE)
return;
writeReg(EWRPT, destPos);
writeBuf(SCRATCH_PAGE_SIZE, data);
}
void ENC28J60::copyin (byte page, byte* data) {
uint16_t destPos = SCRATCH_START + (page & lt; & lt; SCRATCH_PAGE_SHIFT);
if (destPos & lt; SCRATCH_START || destPos & gt; SCRATCH_LIMIT - SCRATCH_PAGE_SIZE)
return;
writeReg(ERDPT, destPos);
readBuf(SCRATCH_PAGE_SIZE, data);
}
byte ENC28J60::peekin (byte page, byte off) {
byte result = 0;
uint16_t destPos = SCRATCH_START + (page & lt; & lt; SCRATCH_PAGE_SHIFT) + off;
if (SCRATCH_START & lt; = destPos & & destPos & lt; SCRATCH_LIMIT) {
writeReg(ERDPT, destPos);
readBuf(1, & result);
}
return result;
}
// Contributed by Alex M. Based on code from: http://blog.derouineau.fr
// /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/
void ENC28J60::powerDown() {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
while(readRegByte(ESTAT) & ESTAT_RXBUSY);
while(readRegByte(ECON1) & ECON1_TXRTS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}
void ENC28J60::powerUp() {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
while(!readRegByte(ESTAT) & ESTAT_CLKRDY);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
void ENC28J60::enableBroadcast (bool temporary) {
writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN);
if(!temporary)
broadcast_enabled = true;
}
void ENC28J60::disableBroadcast (bool temporary) {
if(!temporary)
broadcast_enabled = false;
if(!broadcast_enabled)
writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN);
}
void ENC28J60::enableMulticast () {
writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN);
}
void ENC28J60::disableMulticast () {
writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN);
}
void ENC28J60::enablePromiscuous (bool temporary) {
writeRegByte(ERXFCON, readRegByte(ERXFCON) & ERXFCON_CRCEN);
if(!temporary)
promiscuous_enabled = true;
}
void ENC28J60::disablePromiscuous (bool temporary) {
if(!temporary)
promiscuous_enabled = false;
if(!promiscuous_enabled) {
writeRegByte(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN);
}
}
uint8_t ENC28J60::doBIST ( byte csPin) {
#define RANDOM_FILL 0b0000
#define ADDRESS_FILL 0b0100
#define PATTERN_SHIFT 0b1000
#define RANDOM_RACE 0b1100
// init
if (bitRead(SPCR, SPE) == 0)
initSPI();
selectPin = csPin;
pinMode(selectPin, OUTPUT);
disableChip();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay(2); // errata B7/2
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ;
// now we can start the memory test
uint16_t macResult;
uint16_t bitsResult;
// clear some of the registers registers
writeRegByte(ECON1, 0);
writeReg(EDMAST, 0);
// Set up necessary pointers for the DMA to calculate over the entire memory
writeReg(EDMAND, 0x1FFFu);
writeReg(ERXND, 0x1FFFu);
// Enable Test Mode and do an Address Fill
SetBank(EBSTCON);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL);
// wait for BISTST to be reset, only after that are we actually ready to
// start the test
// this was undocumented :(
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// now start the actual reading an calculating the checksum until the end is
// reached
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
// Compare the results
// 0xF807 should always be generated in Address fill mode
if ((macResult != bitsResult) || (bitsResult != 0xF807)) {
return 0;
}
// reset test flag
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// Now start the BIST with random data test, and also keep on swapping the
// DMA/BIST memory ports.
writeRegByte(EBSTSD, 0b10101010 | millis());
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL);
// wait for BISTST to be reset, only after that are we actually ready to
// start the test
// this was undocumented :(
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// now start the actual reading an calculating the checksum until the end is
// reached
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
// The checksum should be equal
return macResult == bitsResult;
}
void ENC28J60::memcpy_to_enc(uint16_t dest, void* source, int16_t num) {
writeReg(EWRPT, dest);
writeBuf(num, (uint8_t*) source);
}
void ENC28J60::memcpy_from_enc(void* dest, uint16_t source, int16_t num) {
writeReg(ERDPT, source);
readBuf(num, (uint8_t*) dest);
}
static uint16_t endRam = ENC_HEAP_END;
uint16_t ENC28J60::enc_malloc(uint16_t size) {
if (endRam-size & gt; = ENC_HEAP_START) {
endRam -= size;
return endRam;
}
return 0;
}
uint16_t ENC28J60::enc_freemem() {
return endRam-ENC_HEAP_START;
}
uint16_t ENC28J60::readPacketSlice(char* dest, int16_t maxlength, int16_t packetOffset) {
uint16_t erxrdpt = readReg(ERXRDPT);
int16_t packetLength;
memcpy_from_enc((char*) & packetLength, (erxrdpt+3)%(RXSTOP_INIT+1), 2);
packetLength -= 4; // remove crc
int16_t bytesToCopy = packetLength - packetOffset;
if (bytesToCopy & gt; maxlength) bytesToCopy = maxlength;
if (bytesToCopy & lt; = 0) bytesToCopy = 0;
int16_t startofSlice = (erxrdpt+7+packetOffset)%(RXSTOP_INIT+1);
memcpy_from_enc(dest, startofSlice, bytesToCopy);
dest[bytesToCopy] = 0;
return bytesToCopy;
}
// Based on the net.h file from the AVRlib library by Pascal Stang.
// Author: Guido Socher
// Copyright: GPL V2
//
// For AVRlib See http://www.procyonengineering.com/
// Used with explicit permission of Pascal Stang.
//
// 2010-05-20 & lt; jc@wippler.nl & gt;
// notation: _P = position of a field
// _V = value of a field
#ifndef NET_H
#define NET_H
// ******* ETH *******
#define ETH_HEADER_LEN 14
// values of certain bytes:
#define ETHTYPE_ARP_H_V 0x08
#define ETHTYPE_ARP_L_V 0x06
#define ETHTYPE_IP_H_V 0x08
#define ETHTYPE_IP_L_V 0x00
// byte positions in the ethernet frame:
//
// Ethernet type field (2bytes):
#define ETH_TYPE_H_P 12
#define ETH_TYPE_L_P 13
//
#define ETH_DST_MAC 0
#define ETH_SRC_MAC 6
// ******* ARP *******
#define ETH_ARP_OPCODE_REPLY_H_V 0x0
#define ETH_ARP_OPCODE_REPLY_L_V 0x02
#define ETH_ARP_OPCODE_REQ_H_V 0x0
#define ETH_ARP_OPCODE_REQ_L_V 0x01
// start of arp header:
#define ETH_ARP_P 0xe
//
#define ETHTYPE_ARP_L_V 0x06
// arp.dst.ip
#define ETH_ARP_DST_IP_P 0x26
// arp.opcode
#define ETH_ARP_OPCODE_H_P 0x14
#define ETH_ARP_OPCODE_L_P 0x15
// arp.src.mac
#define ETH_ARP_SRC_MAC_P 0x16
#define ETH_ARP_SRC_IP_P 0x1c
#define ETH_ARP_DST_MAC_P 0x20
#define ETH_ARP_DST_IP_P 0x26
// ******* IP *******
#define IP_HEADER_LEN 20
// ip.src
#define IP_SRC_P 0x1a
#define IP_DST_P 0x1e
#define IP_HEADER_LEN_VER_P 0xe
#define IP_CHECKSUM_P 0x18
#define IP_TTL_P 0x16
#define IP_FLAGS_P 0x14
#define IP_P 0xe
#define IP_TOTLEN_H_P 0x10
#define IP_TOTLEN_L_P 0x11
#define IP_PROTO_P 0x17
#define IP_PROTO_ICMP_V 1
#define IP_PROTO_TCP_V 6
// 17=0x11
#define IP_PROTO_UDP_V 17
// ******* ICMP *******
#define ICMP_TYPE_ECHOREPLY_V 0
#define ICMP_TYPE_ECHOREQUEST_V 8
//
#define ICMP_TYPE_P 0x22
#define ICMP_CHECKSUM_P 0x24
#define ICMP_CHECKSUM_H_P 0x24
#define ICMP_CHECKSUM_L_P 0x25
#define ICMP_IDENT_H_P 0x26
#define ICMP_IDENT_L_P 0x27
#define ICMP_DATA_P 0x2a
// ******* UDP *******
#define UDP_HEADER_LEN 8
//
#define UDP_SRC_PORT_H_P 0x22
#define UDP_SRC_PORT_L_P 0x23
#define UDP_DST_PORT_H_P 0x24
#define UDP_DST_PORT_L_P 0x25
//
#define UDP_LEN_H_P 0x26
#define UDP_LEN_L_P 0x27
#define UDP_CHECKSUM_H_P 0x28
#define UDP_CHECKSUM_L_P 0x29
#define UDP_DATA_P 0x2a
// ******* TCP *******
#define TCP_SRC_PORT_H_P 0x22
#define TCP_SRC_PORT_L_P 0x23
#define TCP_DST_PORT_H_P 0x24
#define TCP_DST_PORT_L_P 0x25
// the tcp seq number is 4 bytes 0x26-0x29
#define TCP_SEQ_H_P 0x26
#define TCP_SEQACK_H_P 0x2a
// flags: SYN=2
#define TCP_FLAGS_P 0x2f
#define TCP_FLAGS_SYN_V 2
#define TCP_FLAGS_FIN_V 1
#define TCP_FLAGS_RST_V 4
#define TCP_FLAGS_PUSH_V 8
#define TCP_FLAGS_SYNACK_V 0x12
#define TCP_FLAGS_ACK_V 0x10
#define TCP_FLAGS_PSHACK_V 0x18
// plain len without the options:
#define TCP_HEADER_LEN_PLAIN 20
#define TCP_HEADER_LEN_P 0x2e
#define TCP_WIN_SIZE 0x30
#define TCP_CHECKSUM_H_P 0x32
#define TCP_CHECKSUM_L_P 0x33
#define TCP_OPTIONS_P 0x36
//
#endif