diff --git a/README.adoc b/README.adoc index 4dfea8da..7af51481 100644 --- a/README.adoc +++ b/README.adoc @@ -1,3 +1,15 @@ +Customized library based on the original one. + +- Added correct registry definitions for W5500 + +- Corrected minor problems + +- added automatic spi settings for Industruino ethernet module + +- Included ICMP ping functions from BlakeFoster ICMPPing library + + + = Ethernet Library for Arduino = With the Arduino Ethernet Shield, this library allows an Arduino board to connect to the internet. diff --git a/src/Ethernet.h b/src/Ethernet.h index 376e6c59..ee8cda20 100644 --- a/src/Ethernet.h +++ b/src/Ethernet.h @@ -108,6 +108,7 @@ class EthernetClass { friend class EthernetClient; friend class EthernetServer; friend class EthernetUDP; + friend class EthernetICMP; private: // Opens a socket(TCP or UDP or IP_RAW mode) static uint8_t socketBegin(uint8_t protocol, uint16_t port); diff --git a/src/EthernetICMP.cpp b/src/EthernetICMP.cpp new file mode 100644 index 00000000..6e2d8fd2 --- /dev/null +++ b/src/EthernetICMP.cpp @@ -0,0 +1,325 @@ +/* + * Icmp.cpp: Library to send/receive ICMP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.cpp + * Drop Icmp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + * + * MIT License: + * Copyright (c) 2008 Bjoern Hartmann + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * bjoern@cs.stanford.edu 12/30/2008 + */ + +/* + * Copyright (c) 2010 by Blake Foster + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + + +#include +//#include "Ethernet.h" +//#include "utility/w5100.h" +#include "EthernetICMP.h" + +#ifdef ICMPPING_INSERT_YIELDS +#define ICMPPING_DOYIELD() delay(2) +#else +#define ICMPPING_DOYIELD() +#endif + +inline uint16_t _makeUint16(const uint8_t& highOrder, const uint8_t& lowOrder) +{ + // make a 16-bit unsigned integer given the low order and high order bytes. + // lowOrder first because the Arduino is little endian. + uint8_t value [] = {lowOrder, highOrder}; + return *(uint16_t *)&value; +} + +uint16_t _checksum(const EthernetICMPEcho& echo) +{ + // calculate the checksum of an ICMPEcho with all fields but icmpHeader.checksum populated + unsigned long sum = 0; + + // add the header, bytes reversed since we're using little-endian arithmetic. + sum += _makeUint16(echo.icmpHeader.type, echo.icmpHeader.code); + + // add id and sequence + sum += echo.id + echo.seq; + + // add time, one half at a time. + uint16_t const * time = (uint16_t const *)&echo.time; + sum += *time + *(time + 1); + + // add the payload + for (uint8_t const * b = echo.payload; b < echo.payload + sizeof(echo.payload); b+=2) + { + sum += _makeUint16(*b, *(b + 1)); + } + + // ones complement of ones complement sum + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return ~sum; +} + +EthernetICMPEcho::EthernetICMPEcho(uint8_t type, uint16_t _id, uint16_t _seq, uint8_t * _payload) +: seq(_seq), id(_id), time(millis()) +{ + memcpy(payload, _payload, REQ_DATASIZE); + icmpHeader.type = type; + icmpHeader.code = 0; + icmpHeader.checksum = _checksum(*this); +} + +EthernetICMPEcho::EthernetICMPEcho() +: seq(0), id(0), time(0) +{ + memset(payload, 0, sizeof(payload)); + icmpHeader.code = 0; + icmpHeader.type = 0; + icmpHeader.checksum = 0; +} + +void EthernetICMPEcho::serialize(uint8_t * binData) const +{ + *(binData++) = icmpHeader.type; + *(binData++) = icmpHeader.code; + + *(uint16_t *)binData = htons(icmpHeader.checksum); binData += 2; + *(uint16_t *)binData = htons(id); binData += 2; + *(uint16_t *)binData = htons(seq); binData += 2; + *(icmp_time_t *) binData = htonl(time); binData += 4; + + memcpy(binData, payload, sizeof(payload)); +} + +void EthernetICMPEcho::deserialize(uint8_t const * binData) +{ + icmpHeader.type = *(binData++); + icmpHeader.code = *(binData++); + + icmpHeader.checksum = ntohs(*(uint16_t *)binData); binData += 2; + id = ntohs(*(uint16_t *)binData); binData += 2; + seq = ntohs(*(uint16_t *)binData); binData += 2; + + if (icmpHeader.type != TIME_EXCEEDED) + { + time = ntohl(*(icmp_time_t *)binData); binData += 4; + } + + memcpy(payload, binData, sizeof(payload)); +} + + +uint16_t EthernetICMPPing::ping_timeout = PING_TIMEOUT; + +EthernetICMPPing::EthernetICMPPing(SOCKET socket, uint8_t id) : +#ifdef ICMPPING_ASYNCH_ENABLE + _curSeq(0), _numRetries(0), _asyncstart(0), _asyncstatus(BAD_RESPONSE), +#endif + _id(id), _nextSeq(0), _socket(socket), _attempt(0) +{ + memset(_payload, 0x1A, REQ_DATASIZE); +} + + +void EthernetICMPPing::setPayload(uint8_t * payload) +{ + memcpy(_payload, payload, REQ_DATASIZE); +} + +void EthernetICMPPing::openSocket() +{ + + W5100.execCmdSn(_socket, Sock_CLOSE); + W5100.writeSnIR(_socket, 0xFF); + W5100.writeSnMR(_socket, SnMR::IPRAW); + W5100.writeSnPROTO(_socket, IPPROTO::ICMP); + W5100.writeSnPORT(_socket, 0); + W5100.execCmdSn(_socket, Sock_OPEN); +} + +void EthernetICMPPing::closeSocket() +{ + W5100.execCmdSn(_socket, Sock_CLOSE); + W5100.writeSnIR(_socket, 0xFF); +} + + +void EthernetICMPPing::operator()(const IPAddress& addr, int nRetries, EthernetICMPEchoReply& result) +{ + openSocket(); + + EthernetICMPEcho echoReq(ICMP_ECHOREQ, _id, _nextSeq++, _payload); + + for (_attempt=0; _attempt ping_timeout) + { + + // yep, we've timed out... + if (_attempt < _numRetries) + { + // still, this wasn't our last attempt, let's try again + if (asyncSend(result)) + { + // another send has succeeded + // we'll wait for that now... + return false; + } + + // this send has failed. too bad, + // we are done. + + // Close RAW socket to allow device being pinged again + closeSocket(); + + return true; + } + + // we timed out and have no more attempts left... + // hello? is anybody out there? + // guess not: + result.status = NO_RESPONSE; + + // Close RAW socket to allow device being pinged again + closeSocket(); + + return true; + } + + // have yet to time out, will wait some more: + return false; // results still not in + +} + +#endif /* ICMPPING_ASYNCH_ENABLE */ diff --git a/src/EthernetICMP.h b/src/EthernetICMP.h new file mode 100644 index 00000000..fd2893f5 --- /dev/null +++ b/src/EthernetICMP.h @@ -0,0 +1,333 @@ +/* + * Icmp.cpp: Library to send/receive ICMP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.cpp + * + * MIT License: + * Copyright (c) 2008 Bjoern Hartmann + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * bjoern@cs.stanford.edu 12/30/2008 + */ + +#ifndef EthernetICMP_h +#define EthernetICMP_h + +#include +#include "utility/w5100.h" + + + +/* + * Copyright (c) 2010 by Blake Foster + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + + +#define REQ_DATASIZE 64 +#define ICMP_ECHOREPLY 0 +#define ICMP_ECHOREQ 8 +#define ICMP_ECHOREP 0 +#define TIME_EXCEEDED 11 +#define PING_TIMEOUT 1000 + + +// ICMPPING_ASYNCH_ENABLE -- define this to enable asynch operations +// #define ICMPPING_ASYNCH_ENABLE + +// ICMPPING_INSERT_YIELDS -- some platforms, such as ESP8266, like +// (read: need) to do background work so control must be yielded +// back to the main system periodically when you are doing something +// that takes a good while. +// Define (uncomment the following line) on these platforms, which +// will call a short delay() at critical junctures. +// #define ICMPPING_INSERT_YIELDS + +typedef unsigned long icmp_time_t; + +class EthernetICMPHeader; +class EthernetICMPPing; + +typedef enum Status +{ + /* + Indicates whether a ping succeeded or failed due to one of various error + conditions. These correspond to error conditions that occur in this + library, not anything defined in the ICMP protocol. + */ + SUCCESS = 0, + SEND_TIMEOUT = 1, // Timed out sending the request + NO_RESPONSE = 2, // Died waiting for a response + BAD_RESPONSE = 3, // we got back the wrong type + ASYNC_SENT = 4 +} Status; + + +struct EthernetICMPHeader +{ + /* + Header for an ICMP packet. Does not include the IP header. + */ + uint8_t type; + uint8_t code; + uint16_t checksum; +}; + + +struct EthernetICMPEcho +{ + /* + Contents of an ICMP echo packet, including the ICMP header. Does not + include the IP header. + */ + + /* + This constructor sets all fields and calculates the checksum. It is used + to create ICMP packet data when we send a request. + @param type: ICMP_ECHOREQ or ICMP_ECHOREP. + @param _id: Some arbitrary id. Usually set once per process. + @param _seq: The sequence number. Usually started at zero and incremented + once per request. + @param payload: An arbitrary chunk of data that we expect to get back in + the response. + */ + EthernetICMPEcho(uint8_t type, uint16_t _id, uint16_t _seq, uint8_t * _payload); + + /* + This constructor leaves everything zero. This is used when we receive a + response, since we nuke whatever is here already when we copy the packet + data out of the W5100. + */ + EthernetICMPEcho(); + + EthernetICMPHeader icmpHeader; + uint16_t id; + uint16_t seq; + icmp_time_t time; + uint8_t payload [REQ_DATASIZE]; + + /* + Serialize the header as a byte array, in big endian format. + */ + void serialize(byte * binData) const; + /* + Serialize the header as a byte array, in big endian format. + */ + void deserialize(byte const * binData); +}; + + +struct EthernetICMPEchoReply +{ + /* + Struct returned by ICMPPing(). + @param data: The packet data, including the ICMP header. + @param ttl: Time to live + @param status: SUCCESS if the ping succeeded. One of various error codes + if it failed. + @param addr: The ip address that we received the response from. Something + is borked if this doesn't match the IP address we pinged. + */ + EthernetICMPEcho data; + uint8_t ttl; + Status status; + IPAddress addr; +}; + + +class EthernetICMPPing +{ + /* + Function-object for sending ICMP ping requests. + */ + +public: + /* + Construct an ICMP ping object. + @param socket: The socket number in the W5100. + @param id: The id to put in the ping packets. Can be pretty much any + arbitrary number. + */ + EthernetICMPPing(SOCKET s, uint8_t id); + + + /* + Control the ping timeout (ms). Defaults to PING_TIMEOUT (1000ms) but can + be set using setTimeout(MS). + @param timeout_ms: Timeout for ping replies, in milliseconds. + @note: this value is static -- i.e. system-wide for all ICMPPing objects. + */ + static void setTimeout(uint16_t setTo) { ping_timeout = setTo;} + + /* + Fetch the current setting for ping timeouts (in ms). + @return: timeout for all ICMPPing requests, in milliseconds. + */ + static uint16_t timeout() { return ping_timeout;} + + + /* + Pings the given IP address. + @param addr: IP address to ping, as an array of four octets. + @param nRetries: Number of times to rety before giving up. + @return: An ICMPEchoReply containing the response. The status field in + the return value indicates whether the echo request succeeded or + failed. If the request failed, the status indicates the reason for + failure on the last retry. + */ + EthernetICMPEchoReply operator()(const IPAddress&, int nRetries); + + /* + This overloaded version of the () operator takes a (hopefully blank) + ICMPEchoReply as parameter instead of constructing one internally and + then copying it on return. This creates a very small improvement in + efficiency at the cost of making your code uglier. + @param addr: IP address to ping, as an array of four octets. + @param nRetries: Number of times to rety before giving up. + @param result: ICMPEchoReply that will hold the result. + */ + void operator()(const IPAddress& addr, int nRetries, EthernetICMPEchoReply& result); + + + + /* + Use setPayload to set custom data for all ICMP packets + by passing it an array of [REQ_DATASIZE]. E.g. + uint8_t myPayload[REQ_DATASIZE] = { ... whatever ...}; + ICMPPing ping(pingSocket, (uint16_t)random(0, 255)); + ping.setPayload(myPayload); + // ... as usual ... + + @param payload: pointer to start of REQ_DATASIZE array of bytes to use as payload + + */ + void setPayload(uint8_t * payload); + +#ifdef ICMPPING_ASYNCH_ENABLE + /* + Asynchronous ping methods -- only enabled if ICMPPING_ASYNCH_ENABLE is defined, above. + + These methods are used to start a ping request, go do something else, and + come back later to check if the results are in. A complete example is in the + examples directory but the gist of it is E.g. + + + // say we're in some function, to simplify things... + IPAddress pingAddr(74,125,26,147); // ip address to ping + + EthernetICMPPing ping(0, (uint16_t)random(0, 255)); + EthernetICMPEchoReply theResult; + + if (! asyncStart(pingAddr, 3, theResult)) + { + // well, this didn't start off on the right foot + Serial.print("Echo request send failed; "); + Serial.println((int)theResult.status); + + // + return; // forget about this + } + + // ok, ping has started... + while (! ping.asyncComplete(theResult)) { + + // whatever needs handling while we wait on results + doSomeStuff(); + doSomeOtherStuff(); + delay(30); + + } + + // we get here means we either got a response, or timed out... + if (theResult.status == SUCCESS) + { + // yay... do something. + } else { + // boooo... do something else. + } + + return; + + + */ + + + /* + asyncStart -- begins a new ping request, asynchronously. Parameters are the + same as for regular ping, but the method returns false on error. + + @param addr: IP address to ping, as an array of four octets. + @param nRetries: Number of times to rety before giving up. + @param result: ICMPEchoReply that will hold a status == ASYNC_SENT on success. + @return: true on async request sent, false otherwise. + @author: Pat Deegan, http://psychogenic.com + */ + bool asyncStart(const IPAddress& addr, int nRetries, EthernetICMPEchoReply& result); + + + /* + asyncComplete -- check if the asynchronous ping is done. + This can be either because of a successful outcome (reply received) + or because of an error/timeout. + + @param result: ICMPEchoReply that will hold the result. + @return: true if the result ICMPEchoReply contains the status/other data, + false if we're still waiting for it to complete. + @author: Pat Deegan, http://psychogenic.com + */ + bool asyncComplete(EthernetICMPEchoReply& result); +#endif + +private: + + // holds the timeout, in ms, for all objects of this class. + static uint16_t ping_timeout; + + void openSocket(); + void closeSocket(); + + Status sendEchoRequest(const IPAddress& addr, const EthernetICMPEcho& echoReq); + void receiveEchoReply(const EthernetICMPEcho& echoReq, const IPAddress& addr, EthernetICMPEchoReply& echoReply); + + + +#ifdef ICMPPING_ASYNCH_ENABLE + // extra internal state/methods used when asynchronous pings + // are enabled. + bool asyncSend(EthernetICMPEchoReply& result); + uint8_t _curSeq; + uint8_t _numRetries; + icmp_time_t _asyncstart; + Status _asyncstatus; + IPAddress _addr; +#endif + uint8_t _id; + uint8_t _nextSeq; + SOCKET _socket; + uint8_t _attempt; + + uint8_t _payload[REQ_DATASIZE]; +}; + +#pragma pack(1) + +#endif diff --git a/src/socket.cpp b/src/socket.cpp index f059dc92..46cdc067 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -21,6 +21,13 @@ #include #include "Ethernet.h" #include "utility/w5100.h" +#include "EthernetIcmp.h" + +#ifdef ICMPPING_INSERT_YIELDS +#define ICMPPING_DOYIELD() delay(2) +#else +#define ICMPPING_DOYIELD() +#endif #if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32) extern void yield(void); @@ -537,3 +544,110 @@ bool EthernetClass::socketSendUDP(uint8_t s) return true; } +void EthernetICMPPing::receiveEchoReply(const EthernetICMPEcho& echoReq, const IPAddress& addr, EthernetICMPEchoReply& echoReply) +{ + icmp_time_t start = millis(); + while (millis() - start < ping_timeout) + { + + if (getSnRX_RSR(_socket) < 1) + { + // take a break, maybe let platform do + // some background work (like on ESP8266) + ICMPPING_DOYIELD(); + continue; + } + + // ah! we did receive something... check it out. + + uint8_t ipHeader[6]; + uint8_t buffer = W5100.readSnRX_RD(_socket); + read_data(_socket, (uint16_t) buffer, ipHeader, sizeof(ipHeader)); + buffer += sizeof(ipHeader); + for (int i = 0; i < 4; ++i) + echoReply.addr[i] = ipHeader[i]; + uint8_t dataLen = ipHeader[4]; + dataLen = (dataLen << 8) + ipHeader[5]; + + uint8_t serialized[sizeof(EthernetICMPEcho)]; + if (dataLen > sizeof(EthernetICMPEcho)) + dataLen = sizeof(EthernetICMPEcho); + read_data(_socket, (uint16_t) buffer, serialized, dataLen); + echoReply.data.deserialize(serialized); + + buffer += dataLen; + W5100.writeSnRX_RD(_socket, buffer); + W5100.execCmdSn(_socket, Sock_RECV); + + echoReply.ttl = W5100.readSnTTL(_socket); + + // Since there aren't any ports in ICMP, we need to manually inspect the response + // to see if it originated from the request we sent out. + switch (echoReply.data.icmpHeader.type) { + case ICMP_ECHOREP: { + if (echoReply.data.id == echoReq.id + && echoReply.data.seq == echoReq.seq) { + echoReply.status = SUCCESS; + return; + } + break; + } + case TIME_EXCEEDED: { + uint8_t * sourceIpHeader = echoReply.data.payload; + unsigned int ipHeaderSize = (sourceIpHeader[0] & 0x0F) * 4u; + uint8_t * sourceIcmpHeader = echoReply.data.payload + ipHeaderSize; + + // The destination ip address in the originating packet's IP header. + IPAddress sourceDestAddress(sourceIpHeader + ipHeaderSize - 4); + + if (!(sourceDestAddress == addr)) + continue; + + uint16_t sourceId = ntohs(*(uint16_t * )(sourceIcmpHeader + 4)); + uint16_t sourceSeq = ntohs(*(uint16_t * )(sourceIcmpHeader + 6)); + + if (sourceId == echoReq.id && sourceSeq == echoReq.seq) { + echoReply.status = BAD_RESPONSE; + return; + } + break; + } + } + + + } + echoReply.status = NO_RESPONSE; +} + +Status EthernetICMPPing::sendEchoRequest(const IPAddress& addr, const EthernetICMPEcho& echoReq) +{ + // I wish there were a better way of doing this, but if we use the uint32_t + // cast operator, we're forced to (1) cast away the constness, and (2) deal + // with an endianness nightmare. + uint8_t addri [] = {addr[0], addr[1], addr[2], addr[3]}; + W5100.writeSnDIPR(_socket, addri); + W5100.writeSnTTL(_socket, 255); + // The port isn't used, becuause ICMP is a network-layer protocol. So we + // write zero. This probably isn't actually necessary. + W5100.writeSnDPORT(_socket, 0); + + uint8_t serialized [sizeof(EthernetICMPEcho)]; + echoReq.serialize(serialized); + + //W5100.send_data_processing(_socket, serialized, sizeof(EthernetICMPEcho)); + write_data(_socket, 0, serialized, sizeof(EthernetICMPEcho)); + W5100.execCmdSn(_socket, Sock_SEND); + + while ((W5100.readSnIR(_socket) & SnIR::SEND_OK) != SnIR::SEND_OK) + { + if (W5100.readSnIR(_socket) & SnIR::TIMEOUT) + { + W5100.writeSnIR(_socket, (SnIR::SEND_OK | SnIR::TIMEOUT)); + return SEND_TIMEOUT; + } + + ICMPPING_DOYIELD(); + } + W5100.writeSnIR(_socket, SnIR::SEND_OK); + return SUCCESS; +} \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 00000000..0dfed7b5 --- /dev/null +++ b/src/util.h @@ -0,0 +1,14 @@ +#ifndef UTIL_H +#define UTIL_H + +//#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) ) +#define htons(x) ( (((x)&0xFF)<<8) | (((x)&0xFF00)>>8) ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +#endif \ No newline at end of file diff --git a/src/utility/w5100.h b/src/utility/w5100.h index 099556a6..f5aa26d5 100644 --- a/src/utility/w5100.h +++ b/src/utility/w5100.h @@ -16,6 +16,7 @@ #include #include +#include // Safe for all chips #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0) @@ -49,6 +50,14 @@ #endif +// Industruino can't go faster than 4 MHz +#if defined(ARDUINO_SAMD_INDUSTRUINO_D21G) +#undef SPI_ETHERNET_SETTINGS +#define SPI_ETHERNET_SETTINGS SPISettings(4000000, MSBFIRST, SPI_MODE0) +#endif + + + typedef uint8_t SOCKET; class SnMR { @@ -140,8 +149,8 @@ class W5100Class { inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); } inline void getIPAddress(uint8_t * addr) { readSIPR(addr); } - inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); } - inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); } + inline void setRetransmissionTime(uint16_t timeout) { if (chip == 55) writeRTR_W5500(timeout); else writeRTR_W5100(timeout); } + inline void setRetransmissionCount(uint8_t retry) { if (chip == 55) writeRCR_W5500(retry); else writeRCR_W5100(retry); } static void execCmdSn(SOCKET s, SockCMD _cmd); @@ -190,26 +199,39 @@ class W5100Class { static W5100Linkstatus getLinkStatus(); public: - __GP_REGISTER8 (MR, 0x0000); // Mode - __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address - __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address - __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address - __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address - __GP_REGISTER8 (IR, 0x0015); // Interrupt - __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask - __GP_REGISTER16(RTR, 0x0017); // Timeout address - __GP_REGISTER8 (RCR, 0x0019); // Retry count - __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size (W5100 only) - __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size (W5100 only) - __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode - __GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer - __GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number - __GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode (W5100 only) - __GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode (W5100 only) - __GP_REGISTER8 (VERSIONR_W5200,0x001F); // Chip Version Register (W5200 only) - __GP_REGISTER8 (VERSIONR_W5500,0x0039); // Chip Version Register (W5500 only) - __GP_REGISTER8 (PSTATUS_W5200, 0x0035); // PHY Status - __GP_REGISTER8 (PHYCFGR_W5500, 0x002E); // PHY Configuration register, default: 10111xxx + __GP_REGISTER8 (MR, 0x0000); // Mode + __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address + __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address + __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address + __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address + __GP_REGISTER16(INTLEVEL, 0x0013); // Interrupt Low Level Timer + __GP_REGISTER8 (IR, 0x0015); // Interrupt + __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask + __GP_REGISTER8 (SIR, 0x0017); // Socket Interrupt (W5500 only) + __GP_REGISTER16(RTR_W5100, 0x0017); // Timeout address + __GP_REGISTER8 (SIMR, 0x0018); // Socket Interrupt Mask + __GP_REGISTER16(RTR_W5500, 0x0019); // Timeout address (W5500 only) + __GP_REGISTER8 (RCR_W5100, 0x0019); // Retry count + __GP_REGISTER8 (RCR_W5500, 0x001B); // Retry count (W5500 only) + __GP_REGISTER8 (RMSR, 0x001A); // Receive memory size (W5100 only) + __GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size (W5100 only) + __GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode + __GP_REGISTER8 (PTIMER_W5100, 0x0028); // PPP LCP Request Timer + __GP_REGISTER8 (PTIMER_W5500, 0x001C); // PPP LCP Request Timer (W5500 only) + __GP_REGISTER8 (PMAGIC_W5100, 0x0029); // PPP LCP Magic Number + __GP_REGISTER8 (PMAGIC_W5500, 0x001D); // PPP LCP Magic Number (W5500 only) + __GP_REGISTER_N(UIPR_W5100, 0x002A, 4); // Unreachable IP address in UDP mode (W5100 only) + __GP_REGISTER_N(UIPR_W5500, 0x0028, 4); // Unreachable IP address in UDP mode (W5500 only) + __GP_REGISTER16(UPORT_W5100, 0x002E); // Unreachable Port address in UDP mode (W5100 only) + __GP_REGISTER16(UPORT_W5500, 0x002C); // Unreachable Port address in UDP mode (W5500 only) + __GP_REGISTER8 (VERSIONR_W5200,0x001F); // Chip Version Register (W5200 only) + __GP_REGISTER8 (VERSIONR_W5500,0x0039); // Chip Version Register (W5500 only) + __GP_REGISTER8 (PSTATUS_W5200, 0x0035); // PHY Status + __GP_REGISTER8 (PHYCFGR_W5500, 0x002E); // PHY Configuration register, default: 10111xxx + __GP_REGISTER_N(PHAR, 0x001E, 6); // PPP Destination MAC address + __GP_REGISTER16(PSID, 0x0024); // PPP Session ID + __GP_REGISTER16(PMRU, 0x0026); // PPP Maximum Segment Size + #undef __GP_REGISTER8 @@ -289,6 +311,9 @@ class W5100Class { __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) + __SOCKET_REGISTER8(Sn_IMR, 0x002C) // Interrupt Mask (W5500 only) + __SOCKET_REGISTER16(SnFRAG, 0x002D) // Fragment Offset in IP header (W5500 only) + __SOCKET_REGISTER8(Sn_KPALVTR, 0x002F) // Keep alive timer (W5500 only) #undef __SOCKET_REGISTER8 #undef __SOCKET_REGISTER16 @@ -451,18 +476,28 @@ extern W5100Class W5100; #endif + #ifndef UTIL_H #define UTIL_H #ifndef htons +// The host order of the Arduino platform is little endian. +// Sometimes it is desired to convert to big endian (or +// network order) -#define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) ) +// Host to Network short +#define htons(x) ( (((x)&0xFF)<<8) | (((x)>>8)&0xFF) ) + +// Network to Host short #define ntohs(x) htons(x) +// Host to Network long #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ ((x)<< 8 & 0x00FF0000UL) | \ ((x)>> 8 & 0x0000FF00UL) | \ ((x)>>24 & 0x000000FFUL) ) + +// Network to Host long #define ntohl(x) htonl(x) #endif // !defined(htons)