├── ICMPPing.cpp ├── ICMPPing.h ├── README.md ├── examples ├── Ping │ └── Ping.ino └── PingAsync │ └── PingAsync.ino ├── keywords.txt └── util.h /ICMPPing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 by Blake Foster 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | #include "ICMPPing.h" 11 | #include 12 | 13 | #ifdef ICMPPING_INSERT_YIELDS 14 | #define ICMPPING_DOYIELD() delay(2) 15 | #else 16 | #define ICMPPING_DOYIELD() 17 | #endif 18 | 19 | 20 | inline uint16_t _makeUint16(const uint8_t& highOrder, const uint8_t& lowOrder) 21 | { 22 | // make a 16-bit unsigned integer given the low order and high order bytes. 23 | // lowOrder first because the Arduino is little endian. 24 | uint8_t value [] = {lowOrder, highOrder}; 25 | return *(uint16_t *)&value; 26 | } 27 | 28 | uint16_t _checksum(const ICMPEcho& echo) 29 | { 30 | // calculate the checksum of an ICMPEcho with all fields but icmpHeader.checksum populated 31 | unsigned long sum = 0; 32 | 33 | // add the header, bytes reversed since we're using little-endian arithmetic. 34 | sum += _makeUint16(echo.icmpHeader.type, echo.icmpHeader.code); 35 | 36 | // add id and sequence 37 | sum += echo.id + echo.seq; 38 | 39 | // add time, one half at a time. 40 | uint16_t const * time = (uint16_t const *)&echo.time; 41 | sum += *time + *(time + 1); 42 | 43 | // add the payload 44 | for (uint8_t const * b = echo.payload; b < echo.payload + sizeof(echo.payload); b+=2) 45 | { 46 | sum += _makeUint16(*b, *(b + 1)); 47 | } 48 | 49 | // ones complement of ones complement sum 50 | sum = (sum >> 16) + (sum & 0xffff); 51 | sum += (sum >> 16); 52 | return ~sum; 53 | } 54 | 55 | ICMPEcho::ICMPEcho(uint8_t type, uint16_t _id, uint16_t _seq, uint8_t * _payload) 56 | : seq(_seq), id(_id), time(millis()) 57 | { 58 | memcpy(payload, _payload, REQ_DATASIZE); 59 | icmpHeader.type = type; 60 | icmpHeader.code = 0; 61 | icmpHeader.checksum = _checksum(*this); 62 | } 63 | 64 | ICMPEcho::ICMPEcho() 65 | : seq(0), id(0), time(0) 66 | { 67 | memset(payload, 0, sizeof(payload)); 68 | icmpHeader.code = 0; 69 | icmpHeader.type = 0; 70 | icmpHeader.checksum = 0; 71 | } 72 | 73 | void ICMPEcho::serialize(uint8_t * binData) const 74 | { 75 | *(binData++) = icmpHeader.type; 76 | *(binData++) = icmpHeader.code; 77 | 78 | *(uint16_t *)binData = htons(icmpHeader.checksum); binData += 2; 79 | *(uint16_t *)binData = htons(id); binData += 2; 80 | *(uint16_t *)binData = htons(seq); binData += 2; 81 | *(icmp_time_t *) binData = htonl(time); binData += 4; 82 | 83 | memcpy(binData, payload, sizeof(payload)); 84 | } 85 | 86 | void ICMPEcho::deserialize(uint8_t const * binData) 87 | { 88 | icmpHeader.type = *(binData++); 89 | icmpHeader.code = *(binData++); 90 | 91 | icmpHeader.checksum = ntohs(*(uint16_t *)binData); binData += 2; 92 | id = ntohs(*(uint16_t *)binData); binData += 2; 93 | seq = ntohs(*(uint16_t *)binData); binData += 2; 94 | 95 | if (icmpHeader.type != TIME_EXCEEDED) 96 | { 97 | time = ntohl(*(icmp_time_t *)binData); binData += 4; 98 | } 99 | 100 | memcpy(payload, binData, sizeof(payload)); 101 | } 102 | 103 | 104 | uint16_t ICMPPing::ping_timeout = PING_TIMEOUT; 105 | 106 | ICMPPing::ICMPPing(SOCKET socket, uint8_t id) : 107 | #ifdef ICMPPING_ASYNCH_ENABLE 108 | _curSeq(0), _numRetries(0), _asyncstart(0), _asyncstatus(BAD_RESPONSE), 109 | #endif 110 | _id(id), _nextSeq(0), _socket(socket), _attempt(0) 111 | { 112 | memset(_payload, 0x1A, REQ_DATASIZE); 113 | } 114 | 115 | 116 | void ICMPPing::setPayload(uint8_t * payload) 117 | { 118 | memcpy(_payload, payload, REQ_DATASIZE); 119 | } 120 | 121 | void ICMPPing::openSocket() 122 | { 123 | 124 | w5500.execCmdSn(_socket, Sock_CLOSE); 125 | w5500.writeSnIR(_socket, 0xFF); 126 | w5500.writeSnMR(_socket, SnMR::IPRAW); 127 | w5500.writeSnPROTO(_socket, IPPROTO::ICMP); 128 | w5500.writeSnPORT(_socket, 0); 129 | w5500.execCmdSn(_socket, Sock_OPEN); 130 | } 131 | 132 | 133 | 134 | void ICMPPing::operator()(const IPAddress& addr, int nRetries, ICMPEchoReply& result) 135 | { 136 | openSocket(); 137 | 138 | ICMPEcho echoReq(ICMP_ECHOREQ, _id, _nextSeq++, _payload); 139 | 140 | for (_attempt=0; _attempt sizeof(ICMPEcho)) 228 | dataLen = sizeof(ICMPEcho); 229 | w5500.read_data(_socket, (uint16_t) buffer, serialized, dataLen); 230 | echoReply.data.deserialize(serialized); 231 | 232 | buffer += dataLen; 233 | w5500.writeSnRX_RD(_socket, buffer); 234 | w5500.execCmdSn(_socket, Sock_RECV); 235 | 236 | echoReply.ttl = w5500.readSnTTL(_socket); 237 | 238 | // Since there aren't any ports in ICMP, we need to manually inspect the response 239 | // to see if it originated from the request we sent out. 240 | switch (echoReply.data.icmpHeader.type) { 241 | case ICMP_ECHOREP: { 242 | if (echoReply.data.id == echoReq.id 243 | && echoReply.data.seq == echoReq.seq) { 244 | echoReply.status = SUCCESS; 245 | return; 246 | } 247 | break; 248 | } 249 | case TIME_EXCEEDED: { 250 | uint8_t * sourceIpHeader = echoReply.data.payload; 251 | unsigned int ipHeaderSize = (sourceIpHeader[0] & 0x0F) * 4u; 252 | uint8_t * sourceIcmpHeader = echoReply.data.payload + ipHeaderSize; 253 | 254 | // The destination ip address in the originating packet's IP header. 255 | IPAddress sourceDestAddress(sourceIpHeader + ipHeaderSize - 4); 256 | 257 | if (!(sourceDestAddress == addr)) 258 | continue; 259 | 260 | uint16_t sourceId = ntohs(*(uint16_t * )(sourceIcmpHeader + 4)); 261 | uint16_t sourceSeq = ntohs(*(uint16_t * )(sourceIcmpHeader + 6)); 262 | 263 | if (sourceId == echoReq.id && sourceSeq == echoReq.seq) { 264 | echoReply.status = BAD_RESPONSE; 265 | return; 266 | } 267 | break; 268 | } 269 | } 270 | 271 | 272 | } 273 | echoReply.status = NO_RESPONSE; 274 | } 275 | 276 | 277 | 278 | #ifdef ICMPPING_ASYNCH_ENABLE 279 | /* 280 | * When ICMPPING_ASYNCH_ENABLE is defined, we have access to the 281 | * asyncStart()/asyncComplete() methods from the API. 282 | */ 283 | bool ICMPPing::asyncSend(ICMPEchoReply& result) 284 | { 285 | ICMPEcho echoReq(ICMP_ECHOREQ, _id, _curSeq, _payload); 286 | 287 | Status sendOpResult(NO_RESPONSE); 288 | bool sendSuccess = false; 289 | for (uint8_t i=_attempt; i<_numRetries; ++i) 290 | { 291 | _attempt++; 292 | 293 | ICMPPING_DOYIELD(); 294 | sendOpResult = sendEchoRequest(_addr, echoReq); 295 | if (sendOpResult == SUCCESS) 296 | { 297 | sendSuccess = true; // it worked 298 | sendOpResult = ASYNC_SENT; // we're doing this async-style, force the status 299 | _asyncstart = millis(); // not the start time, for timeouts 300 | break; // break out of this loop, 'cause we're done. 301 | 302 | } 303 | } 304 | _asyncstatus = sendOpResult; // keep track of this, in case the ICMPEchoReply isn't re-used 305 | result.status = _asyncstatus; // set the result, in case the ICMPEchoReply is checked 306 | return sendSuccess; // return success of send op 307 | } 308 | bool ICMPPing::asyncStart(const IPAddress& addr, int nRetries, ICMPEchoReply& result) 309 | { 310 | openSocket(); 311 | 312 | // stash our state, so we can access 313 | // in asynchSend()/asyncComplete() 314 | _numRetries = nRetries; 315 | _attempt = 0; 316 | _curSeq = _nextSeq++; 317 | _addr = addr; 318 | 319 | return asyncSend(result); 320 | 321 | } 322 | 323 | bool ICMPPing::asyncComplete(ICMPEchoReply& result) 324 | { 325 | 326 | if (_asyncstatus != ASYNC_SENT) 327 | { 328 | // we either: 329 | // - didn't start an async request; 330 | // - failed to send; or 331 | // - are no longer waiting on this packet. 332 | // either way, we're done 333 | return true; 334 | } 335 | 336 | 337 | if (w5500.getRXReceivedSize(_socket)) 338 | { 339 | // ooooh, we've got a pending reply 340 | ICMPEcho echoReq(ICMP_ECHOREQ, _id, _curSeq, _payload); 341 | receiveEchoReply(echoReq, _addr, result); 342 | _asyncstatus = result.status; // make note of this status, whatever it is. 343 | return true; // whatever the result of the receiveEchoReply(), the async op is done. 344 | } 345 | 346 | // nothing yet... check if we've timed out 347 | if ( (millis() - _asyncstart) > ping_timeout) 348 | { 349 | 350 | // yep, we've timed out... 351 | if (_attempt < _numRetries) 352 | { 353 | // still, this wasn't our last attempt, let's try again 354 | if (asyncSend(result)) 355 | { 356 | // another send has succeeded 357 | // we'll wait for that now... 358 | return false; 359 | } 360 | 361 | // this send has failed. too bad, 362 | // we are done. 363 | return true; 364 | } 365 | 366 | // we timed out and have no more attempts left... 367 | // hello? is anybody out there? 368 | // guess not: 369 | result.status = NO_RESPONSE; 370 | return true; 371 | } 372 | 373 | // have yet to time out, will wait some more: 374 | return false; // results still not in 375 | 376 | } 377 | 378 | #endif /* ICMPPING_ASYNCH_ENABLE */ 379 | 380 | 381 | 382 | -------------------------------------------------------------------------------- /ICMPPing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 by Blake Foster 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define REQ_DATASIZE 64 15 | #define ICMP_ECHOREPLY 0 16 | #define ICMP_ECHOREQ 8 17 | #define ICMP_ECHOREP 0 18 | #define TIME_EXCEEDED 11 19 | #define PING_TIMEOUT 1000 20 | 21 | 22 | // ICMPPING_ASYNCH_ENABLE -- define this to enable asynch operations 23 | #define ICMPPING_ASYNCH_ENABLE 24 | 25 | // ICMPPING_INSERT_YIELDS -- some platforms, such as ESP8266, like 26 | // (read: need) to do background work so control must be yielded 27 | // back to the main system periodically when you are doing something 28 | // that takes a good while. 29 | // Define (uncomment the following line) on these platforms, which 30 | // will call a short delay() at critical junctures. 31 | // #define ICMPPING_INSERT_YIELDS 32 | 33 | typedef unsigned long icmp_time_t; 34 | 35 | class ICMPHeader; 36 | class ICMPPing; 37 | 38 | typedef enum Status 39 | { 40 | /* 41 | Indicates whether a ping succeeded or failed due to one of various error 42 | conditions. These correspond to error conditions that occur in this 43 | library, not anything defined in the ICMP protocol. 44 | */ 45 | SUCCESS = 0, 46 | SEND_TIMEOUT = 1, // Timed out sending the request 47 | NO_RESPONSE = 2, // Died waiting for a response 48 | BAD_RESPONSE = 3, // we got back the wrong type 49 | ASYNC_SENT = 4 50 | } Status; 51 | 52 | 53 | struct ICMPHeader 54 | { 55 | /* 56 | Header for an ICMP packet. Does not include the IP header. 57 | */ 58 | uint8_t type; 59 | uint8_t code; 60 | uint16_t checksum; 61 | }; 62 | 63 | 64 | struct ICMPEcho 65 | { 66 | /* 67 | Contents of an ICMP echo packet, including the ICMP header. Does not 68 | include the IP header. 69 | */ 70 | 71 | /* 72 | This constructor sets all fields and calculates the checksum. It is used 73 | to create ICMP packet data when we send a request. 74 | @param type: ICMP_ECHOREQ or ICMP_ECHOREP. 75 | @param _id: Some arbitrary id. Usually set once per process. 76 | @param _seq: The sequence number. Usually started at zero and incremented 77 | once per request. 78 | @param payload: An arbitrary chunk of data that we expect to get back in 79 | the response. 80 | */ 81 | ICMPEcho(uint8_t type, uint16_t _id, uint16_t _seq, uint8_t * _payload); 82 | 83 | /* 84 | This constructor leaves everything zero. This is used when we receive a 85 | response, since we nuke whatever is here already when we copy the packet 86 | data out of the W5100. 87 | */ 88 | ICMPEcho(); 89 | 90 | ICMPHeader icmpHeader; 91 | uint16_t id; 92 | uint16_t seq; 93 | icmp_time_t time; 94 | uint8_t payload [REQ_DATASIZE]; 95 | 96 | /* 97 | Serialize the header as a byte array, in big endian format. 98 | */ 99 | void serialize(byte * binData) const; 100 | /* 101 | Serialize the header as a byte array, in big endian format. 102 | */ 103 | void deserialize(byte const * binData); 104 | }; 105 | 106 | 107 | struct ICMPEchoReply 108 | { 109 | /* 110 | Struct returned by ICMPPing(). 111 | @param data: The packet data, including the ICMP header. 112 | @param ttl: Time to live 113 | @param status: SUCCESS if the ping succeeded. One of various error codes 114 | if it failed. 115 | @param addr: The ip address that we received the response from. Something 116 | is borked if this doesn't match the IP address we pinged. 117 | */ 118 | ICMPEcho data; 119 | uint8_t ttl; 120 | Status status; 121 | IPAddress addr; 122 | }; 123 | 124 | 125 | class ICMPPing 126 | { 127 | /* 128 | Function-object for sending ICMP ping requests. 129 | */ 130 | 131 | public: 132 | /* 133 | Construct an ICMP ping object. 134 | @param socket: The socket number in the W5100. 135 | @param id: The id to put in the ping packets. Can be pretty much any 136 | arbitrary number. 137 | */ 138 | ICMPPing(SOCKET s, uint8_t id); 139 | 140 | 141 | /* 142 | Control the ping timeout (ms). Defaults to PING_TIMEOUT (1000ms) but can 143 | be set using setTimeout(MS). 144 | @param timeout_ms: Timeout for ping replies, in milliseconds. 145 | @note: this value is static -- i.e. system-wide for all ICMPPing objects. 146 | */ 147 | static void setTimeout(uint16_t setTo) { ping_timeout = setTo;} 148 | 149 | /* 150 | Fetch the current setting for ping timeouts (in ms). 151 | @return: timeout for all ICMPPing requests, in milliseconds. 152 | */ 153 | static uint16_t timeout() { return ping_timeout;} 154 | 155 | 156 | /* 157 | Pings the given IP address. 158 | @param addr: IP address to ping, as an array of four octets. 159 | @param nRetries: Number of times to rety before giving up. 160 | @return: An ICMPEchoReply containing the response. The status field in 161 | the return value indicates whether the echo request succeeded or 162 | failed. If the request failed, the status indicates the reason for 163 | failure on the last retry. 164 | */ 165 | ICMPEchoReply operator()(const IPAddress&, int nRetries); 166 | 167 | /* 168 | This overloaded version of the () operator takes a (hopefully blank) 169 | ICMPEchoReply as parameter instead of constructing one internally and 170 | then copying it on return. This creates a very small improvement in 171 | efficiency at the cost of making your code uglier. 172 | @param addr: IP address to ping, as an array of four octets. 173 | @param nRetries: Number of times to rety before giving up. 174 | @param result: ICMPEchoReply that will hold the result. 175 | */ 176 | void operator()(const IPAddress& addr, int nRetries, ICMPEchoReply& result); 177 | 178 | 179 | 180 | /* 181 | Use setPayload to set custom data for all ICMP packets 182 | by passing it an array of [REQ_DATASIZE]. E.g. 183 | uint8_t myPayload[REQ_DATASIZE] = { ... whatever ...}; 184 | ICMPPing ping(pingSocket, (uint16_t)random(0, 255)); 185 | ping.setPayload(myPayload); 186 | // ... as usual ... 187 | 188 | @param payload: pointer to start of REQ_DATASIZE array of bytes to use as payload 189 | 190 | */ 191 | void setPayload(uint8_t * payload); 192 | 193 | #ifdef ICMPPING_ASYNCH_ENABLE 194 | /* 195 | Asynchronous ping methods -- only enabled if ICMPPING_ASYNCH_ENABLE is defined, above. 196 | 197 | These methods are used to start a ping request, go do something else, and 198 | come back later to check if the results are in. A complete example is in the 199 | examples directory but the gist of it is E.g. 200 | 201 | 202 | // say we're in some function, to simplify things... 203 | IPAddress pingAddr(74,125,26,147); // ip address to ping 204 | 205 | ICMPPing ping(0, (uint16_t)random(0, 255)); 206 | ICMPEchoReply theResult; 207 | 208 | if (! asyncStart(pingAddr, 3, theResult)) 209 | { 210 | // well, this didn't start off on the right foot 211 | Serial.print("Echo request send failed; "); 212 | Serial.println((int)theResult.status); 213 | 214 | // 215 | return; // forget about this 216 | } 217 | 218 | // ok, ping has started... 219 | while (! ping.asyncComplete(theResult)) { 220 | 221 | // whatever needs handling while we wait on results 222 | doSomeStuff(); 223 | doSomeOtherStuff(); 224 | delay(30); 225 | 226 | } 227 | 228 | // we get here means we either got a response, or timed out... 229 | if (theResult.status == SUCCESS) 230 | { 231 | // yay... do something. 232 | } else { 233 | // boooo... do something else. 234 | } 235 | 236 | return; 237 | 238 | 239 | */ 240 | 241 | 242 | /* 243 | asyncStart -- begins a new ping request, asynchronously. Parameters are the 244 | same as for regular ping, but the method returns false on error. 245 | 246 | @param addr: IP address to ping, as an array of four octets. 247 | @param nRetries: Number of times to rety before giving up. 248 | @param result: ICMPEchoReply that will hold a status == ASYNC_SENT on success. 249 | @return: true on async request sent, false otherwise. 250 | @author: Pat Deegan, http://psychogenic.com 251 | */ 252 | bool asyncStart(const IPAddress& addr, int nRetries, ICMPEchoReply& result); 253 | 254 | 255 | /* 256 | asyncComplete -- check if the asynchronous ping is done. 257 | This can be either because of a successful outcome (reply received) 258 | or because of an error/timeout. 259 | 260 | @param result: ICMPEchoReply that will hold the result. 261 | @return: true if the result ICMPEchoReply contains the status/other data, 262 | false if we're still waiting for it to complete. 263 | @author: Pat Deegan, http://psychogenic.com 264 | */ 265 | bool asyncComplete(ICMPEchoReply& result); 266 | #endif 267 | 268 | private: 269 | 270 | // holds the timeout, in ms, for all objects of this class. 271 | static uint16_t ping_timeout; 272 | 273 | void openSocket(); 274 | 275 | Status sendEchoRequest(const IPAddress& addr, const ICMPEcho& echoReq); 276 | void receiveEchoReply(const ICMPEcho& echoReq, const IPAddress& addr, ICMPEchoReply& echoReply); 277 | 278 | 279 | 280 | #ifdef ICMPPING_ASYNCH_ENABLE 281 | // extra internal state/methods used when asynchronous pings 282 | // are enabled. 283 | bool asyncSend(ICMPEchoReply& result); 284 | uint8_t _curSeq; 285 | uint8_t _numRetries; 286 | icmp_time_t _asyncstart; 287 | Status _asyncstatus; 288 | IPAddress _addr; 289 | #endif 290 | uint8_t _id; 291 | uint8_t _nextSeq; 292 | SOCKET _socket; 293 | uint8_t _attempt; 294 | 295 | uint8_t _payload[REQ_DATASIZE]; 296 | }; 297 | 298 | #pragma pack(1) 299 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-Ping-W5500 2 | W5500 version of ICMP ping library for the Arduino by Blake Foster 3 | 4 | Uses Adafruit's Ethernet 2 library for W5500 5 | -------------------------------------------------------------------------------- /examples/Ping/Ping.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Ping Example 3 | 4 | This example sends an ICMP pings every 500 milliseconds, sends the human-readable 5 | result over the serial port. 6 | 7 | Requires Adafruit Ethernet 2 library for W5500 8 | 9 | Circuit: 10 | Ethernet shield attached to hardware SPI 11 | Ethernet CS pin to pin 8 and reset pin to pin 7 12 | 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #define ETH_CS_PIN 8 20 | #define ETH_RST_PIN 7 21 | 22 | const byte mac[] = { 0x36, 0xF2, 0xCC, 0xF2, 0xE5, 0xC0 }; 23 | IPAddress pingAddr(192, 168, 0, 102); // ip address to ping 24 | 25 | SOCKET pingSocket = 0; 26 | 27 | char buffer [256]; 28 | ICMPPing ping(pingSocket, (uint16_t)random(0, 255)); 29 | 30 | void setup() 31 | { 32 | // start Ethernet 33 | Serial.begin(115200); 34 | 35 | initEthernet(); 36 | Serial.println("init success"); 37 | 38 | } 39 | 40 | void loop() 41 | { 42 | ICMPEchoReply echoReply = ping(pingAddr, 4); 43 | if (echoReply.status == SUCCESS) 44 | { 45 | sprintf(buffer, 46 | "Reply[%d] from: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d", 47 | echoReply.data.seq, 48 | echoReply.addr[0], 49 | echoReply.addr[1], 50 | echoReply.addr[2], 51 | echoReply.addr[3], 52 | REQ_DATASIZE, 53 | millis() - echoReply.data.time, 54 | echoReply.ttl); 55 | } 56 | else 57 | { 58 | sprintf(buffer, "Echo request failed; %d", echoReply.status); 59 | } 60 | Serial.println(buffer); 61 | delay(500); 62 | } 63 | 64 | 65 | bool initEthernet() { 66 | 67 | Serial.println(F("Obtaining Local IP...")); 68 | bool DHCPsuccess = false; 69 | 70 | while (!DHCPsuccess) { 71 | digitalWrite(ETH_RST_PIN, LOW); 72 | pinMode(ETH_RST_PIN, OUTPUT); 73 | delay(100); 74 | pinMode(ETH_RST_PIN, INPUT); 75 | digitalWrite(ETH_RST_PIN, HIGH); 76 | delay(200); 77 | 78 | Ethernet.init(ETH_CS_PIN); 79 | 80 | if (Ethernet.begin(mac)) { 81 | Serial.print(F("DHCP IP: ")); 82 | Serial.println(Ethernet.localIP()); 83 | 84 | DHCPsuccess = true; 85 | } else { 86 | //timed out 60 secs. 87 | Serial.println(F("Timeout.")); 88 | Serial.println(F("Check Ethernet cable.")); 89 | Serial.println(F("Retring DHCP...")); 90 | 91 | } 92 | 93 | } 94 | 95 | 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /examples/PingAsync/PingAsync.ino: -------------------------------------------------------------------------------- 1 | /* 2 | PingAsync.cpp 3 | This example uses the asynchronous methods of ICMPPing to send a ping, 4 | do "other stuff" and check back for results periodically. 5 | 6 | 7 | See the basic "Ping" example, on which this is based, for simpler 8 | (but synchronous--meaning code will be frozen while awaiting responses) 9 | usage. 10 | 11 | Setup: Configure the various defines in the Configuration section, below, 12 | to select whether to use DHCP or a static IP and to choose the remote host 13 | to ping. 14 | 15 | 16 | Requires Adafruit Ethernet 2 library for W5500 17 | 18 | Circuit: 19 | Ethernet shield attached to hardware SPI 20 | Ethernet CS pin to pin 8 and reset pin to pin 7 21 | 22 | 23 | 24 | 25 | */ 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | /* ************ Configuration ************** */ 32 | 33 | 34 | // NOTE: Use *COMMAS* (,) between IP values, as we're 35 | // using these to init the constructors 36 | 37 | 38 | 39 | #define ETH_CS_PIN 8 40 | #define ETH_RST_PIN 7 41 | 42 | const byte mac[] = { 0x36, 0xF2, 0xCC, 0xF2, 0xE5, 0xC0 }; 43 | IPAddress pingAddr(192, 168, 0, 102); // ip address to ping 44 | 45 | // PING_REQUEST_TIMEOUT_MS -- timeout in ms. between 1 and 65000 or so 46 | // save values: 1000 to 5000, say. 47 | #define PING_REQUEST_TIMEOUT_MS 2500 48 | 49 | 50 | #ifndef ICMPPING_ASYNCH_ENABLE 51 | #error "Asynchronous functions only available if ICMPPING_ASYNCH_ENABLE is defined -- see ICMPPing.h" 52 | #endif 53 | 54 | SOCKET pingSocket = 0; 55 | 56 | char buffer [256]; 57 | ICMPPing ping(pingSocket, 1); 58 | 59 | void dieWithMessage(const char * msg) 60 | { 61 | 62 | for (;;) 63 | { 64 | Serial.println(msg); 65 | delay(500); 66 | } 67 | } 68 | 69 | void setup() 70 | { 71 | // start Ethernet 72 | Serial.begin(115200); 73 | 74 | initEthernet(); 75 | Serial.println("init success"); 76 | 77 | 78 | // increase the default time-out, if needed, assuming a bad 79 | // connection or whatever. 80 | ICMPPing::setTimeout(PING_REQUEST_TIMEOUT_MS); 81 | 82 | } 83 | 84 | 85 | // lastPingSucceeded -- just a flag so we don't drown in 86 | // output when things are going wrong... 87 | bool lastPingSucceeded = false; 88 | 89 | // someCriticalStuffThatCantWait is just a toy function 90 | // to represent the *reason* you want to use asynchronous 91 | // calls rather than wait around for pings to come home. 92 | // Here, we just print out some chars... 93 | void someCriticalStuffThatCantWait() 94 | { 95 | for (int i = 0; i < 10; i++) 96 | { 97 | if (lastPingSucceeded) { 98 | Serial.print('.'); 99 | } 100 | } 101 | Serial.print('_'); 102 | } 103 | 104 | void loop() 105 | { 106 | 107 | lastPingSucceeded = false; 108 | ICMPEchoReply echoResult; // we'll get the status here 109 | Serial.println("Starting async ping."); 110 | 111 | // asynchStart will return false if we couldn't 112 | // even send a ping, though you could also check the 113 | // echoResult.status with should == 114 | if (! ping.asyncStart(pingAddr, 3, echoResult)) 115 | { 116 | Serial.print("Couldn't even send our ping request?? Status: "); 117 | Serial.println((int)echoResult.status); 118 | delay(500); 119 | return; 120 | 121 | } 122 | 123 | // ok the ping started out well... 124 | Serial.println("Ping sent "); 125 | while (! ping.asyncComplete(echoResult)) 126 | { 127 | // we have critical stuff we wish to handle 128 | // while we wait for ping to come back 129 | someCriticalStuffThatCantWait(); 130 | } 131 | 132 | // async is done! let's see how it worked out... 133 | if (echoResult.status != SUCCESS) 134 | { 135 | // failure... but whyyyy? 136 | sprintf(buffer, "Echo request failed; %d", echoResult.status); 137 | } else { 138 | // huzzah 139 | lastPingSucceeded = true; 140 | sprintf(buffer, 141 | "Reply[%d] from: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d", 142 | echoResult.data.seq, 143 | echoResult.addr[0], 144 | echoResult.addr[1], 145 | echoResult.addr[2], 146 | echoResult.addr[3], 147 | REQ_DATASIZE, 148 | millis() - echoResult.data.time, 149 | echoResult.ttl); 150 | } 151 | 152 | Serial.println(buffer); 153 | delay(500); 154 | } 155 | 156 | 157 | 158 | bool initEthernet() { 159 | 160 | Serial.println(F("Obtaining Local IP...")); 161 | bool DHCPsuccess = false; 162 | 163 | while (!DHCPsuccess) { 164 | digitalWrite(ETH_RST_PIN, LOW); 165 | pinMode(ETH_RST_PIN, OUTPUT); 166 | delay(100); 167 | pinMode(ETH_RST_PIN, INPUT); 168 | digitalWrite(ETH_RST_PIN, HIGH); 169 | delay(200); 170 | 171 | Ethernet.init(ETH_CS_PIN); 172 | 173 | if (Ethernet.begin(mac)) { 174 | Serial.print(F("DHCP IP: ")); 175 | Serial.println(Ethernet.localIP()); 176 | 177 | DHCPsuccess = true; 178 | } else { 179 | //timed out 60 secs. 180 | Serial.println(F("Timeout.")); 181 | Serial.println(F("Check Ethernet cable.")); 182 | Serial.println(F("Retring DHCP...")); 183 | 184 | } 185 | 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Ethernet 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ICMPPing KEYWORD1 10 | ICMPHeader KEYWORD1 11 | ICMPEcho KEYWORD1 12 | ICMPEchoReply KEYWORD1 13 | Status KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | ####################################### 20 | # Constants (LITERAL1) 21 | ####################################### 22 | 23 | SUCCESS LITERAL1 24 | SEND_TIMEOUT LITERAL1 25 | NO_RESPONSE LITERAL1 26 | BAD_RESPONSE LITERAL1 27 | REQ_DATASIZE LITERAL1 28 | ICMP_ECHOREPLY LITERAL1 29 | ICMP_ECHOREQ LITERAL1 30 | ICMP_ECHOREP LITERAL1 31 | PING_TIMEOUT LITERAL1 32 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #define htons(x) ( ((x)<< 8 & 0xFF00) | \ 5 | ((x)>> 8 & 0x00FF) ) 6 | #define ntohs(x) htons(x) 7 | 8 | #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ 9 | ((x)<< 8 & 0x00FF0000UL) | \ 10 | ((x)>> 8 & 0x0000FF00UL) | \ 11 | ((x)>>24 & 0x000000FFUL) ) 12 | #define ntohl(x) htonl(x) 13 | 14 | #endif 15 | --------------------------------------------------------------------------------