├── .gitignore ├── README.md ├── contributors.txt └── icmp_ping ├── ICMPPing.cpp ├── ICMPPing.h ├── examples ├── Ping │ └── Ping.ino └── PingAsync │ └── PingAsync.ino ├── keywords.txt └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 by Blake Foster 2 | 3 | This file is free software; you can redistribute it and/or modify 4 | it under the terms of either the GNU General Public License version 2 5 | or the GNU Lesser General Public License version 2.1, both as 6 | published by the Free Software Foundation. 7 | 8 | 9 | Arduino-Ping 10 | ============ 11 | 12 | ICMP ping library for the Arduino 13 | 14 | To use, copy the icmp_ping directory into the libraries directory of your Arduino folder. 15 | 16 | e.g. cp ~/Arduino-Ping/icmp_ping /usr/share/arduino/libraries/icmp_ping 17 | 18 | Then restart the Arduino software if necessary, and icmp_ping should be available under the libraries dropdown. 19 | 20 | See the included sketch for example usage. 21 | See the header (ICMP.h) for API documentation. 22 | 23 | The master branch currently requires at least version 1.5.5 beta of the Arduino software. For older versions of the 24 | Arduino software, use version 2.1. After cloning: 25 | 26 | cd Arduino-Ping 27 | git checkout version2.1 28 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | Blake Foster 2 | Pat Deegan 3 | -------------------------------------------------------------------------------- /icmp_ping/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 | W5100.execCmdSn(_socket, Sock_CLOSE); 125 | W5100.writeSnIR(_socket, 0xFF); 126 | W5100.writeSnMR(_socket, SnMR::IPRAW); 127 | W5100.writeSnPROTO(_socket, IPPROTO::ICMP); 128 | W5100.writeSnPORT(_socket, 0); 129 | W5100.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 | W5100.read_data(_socket, (uint16_t) buffer, serialized, dataLen); 230 | echoReply.data.deserialize(serialized); 231 | 232 | buffer += dataLen; 233 | W5100.writeSnRX_RD(_socket, buffer); 234 | W5100.execCmdSn(_socket, Sock_RECV); 235 | 236 | echoReply.ttl = W5100.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 (W5100.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 | -------------------------------------------------------------------------------- /icmp_ping/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 | -------------------------------------------------------------------------------- /icmp_ping/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 | Circuit: 8 | * Ethernet shield attached to pins 10, 11, 12, 13 9 | 10 | created 30 Sep 2010 11 | by Blake Foster 12 | 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // max address for ethernet shield 20 | byte ip[] = {192,168,2,177}; // ip address for ethernet shield 21 | IPAddress pingAddr(74,125,26,147); // ip address to ping 22 | 23 | SOCKET pingSocket = 0; 24 | 25 | char buffer [256]; 26 | ICMPPing ping(pingSocket, (uint16_t)random(0, 255)); 27 | 28 | void setup() 29 | { 30 | // start Ethernet 31 | Ethernet.begin(mac, ip); 32 | Serial.begin(9600); 33 | } 34 | 35 | void loop() 36 | { 37 | ICMPEchoReply echoReply = ping(pingAddr, 4); 38 | if (echoReply.status == SUCCESS) 39 | { 40 | sprintf(buffer, 41 | "Reply[%d] from: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d", 42 | echoReply.data.seq, 43 | echoReply.addr[0], 44 | echoReply.addr[1], 45 | echoReply.addr[2], 46 | echoReply.addr[3], 47 | REQ_DATASIZE, 48 | millis() - echoReply.data.time, 49 | echoReply.ttl); 50 | } 51 | else 52 | { 53 | sprintf(buffer, "Echo request failed; %d", echoReply.status); 54 | } 55 | Serial.println(buffer); 56 | delay(500); 57 | } 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /icmp_ping/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 | * Circuit: 16 | * Ethernet shield attached to pins 10, 11, 12, 13 17 | * OR other wiznet 5100-based board (tested on the WIZ811MJ, for example), 18 | * suitably connected. 19 | * 20 | * Created on: Dec 12, 2015 21 | * Author: Pat Deegan 22 | * Part of the ICMPPing Project 23 | * Copyright (C) 2015 Pat Deegan, http://psychogenic.com 24 | * 25 | * This file is free software; you can redistribute it and/or modify it under the terms 26 | * of either the GNU General Public License version 2 or the GNU Lesser General Public 27 | * License version 2.1, both as published by the Free Software Foundation. 28 | */ 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | /* ************ Configuration ************** */ 35 | 36 | 37 | // NOTE: Use *COMMAS* (,) between IP values, as we're 38 | // using these to init the constructors 39 | 40 | // PING_REMOTE_IP -- remote host to ping (see NOTE above) 41 | // so, e.g., 162.220.162.142 becomes 162, 220, 162, 142 42 | #define PING_REMOTE_IP 162, 220, 162, 142 43 | 44 | 45 | // TEST_USING_STATIC_IP -- (see NOTE above) 46 | // leave undefined (commented out) 47 | // to use DHCP instead. 48 | // #define TEST_USING_STATIC_IP 192, 168, 2, 177 49 | 50 | 51 | 52 | 53 | // PING_REQUEST_TIMEOUT_MS -- timeout in ms. between 1 and 65000 or so 54 | // save values: 1000 to 5000, say. 55 | #define PING_REQUEST_TIMEOUT_MS 2500 56 | 57 | #define LOCAL_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 58 | 59 | #ifndef ICMPPING_ASYNCH_ENABLE 60 | #error "Asynchronous functions only available if ICMPPING_ASYNCH_ENABLE is defined -- see ICMPPing.h" 61 | #endif 62 | 63 | 64 | 65 | byte mac[] = {LOCAL_MAC_ADDRESS}; // max address for ethernet shield 66 | 67 | #ifdef TEST_USING_STATIC_IP 68 | byte ip[] = {TEST_USING_STATIC_IP}; // ip address for ethernet shield 69 | #endif 70 | 71 | IPAddress pingAddr(PING_REMOTE_IP); // ip address to ping 72 | 73 | SOCKET pingSocket = 0; 74 | 75 | char buffer [256]; 76 | ICMPPing ping(pingSocket, 1); 77 | 78 | void dieWithMessage(const char * msg) 79 | { 80 | 81 | for (;;) 82 | { 83 | Serial.println(msg); 84 | delay(500); 85 | } 86 | } 87 | 88 | void setup() 89 | { 90 | // start Ethernet 91 | Serial.begin(115200); 92 | 93 | Serial.println("PingAsync booted..."); 94 | Serial.print("Configuring ethernet with "); 95 | 96 | #ifdef TEST_USING_STATIC_IP 97 | 98 | Serial.println("static ip"); 99 | Ethernet.begin(mac, ip); 100 | #else 101 | 102 | Serial.print("DHCP..."); 103 | if (! Ethernet.begin(mac) ) 104 | { 105 | 106 | Serial.println("FAILURE"); 107 | dieWithMessage("Couldn't init ethernet using DHCP?!"); 108 | } 109 | 110 | Serial.println("Success!"); 111 | #endif 112 | 113 | // increase the default time-out, if needed, assuming a bad 114 | // connection or whatever. 115 | ICMPPing::setTimeout(PING_REQUEST_TIMEOUT_MS); 116 | 117 | } 118 | 119 | 120 | // lastPingSucceeded -- just a flag so we don't drown in 121 | // output when things are going wrong... 122 | bool lastPingSucceeded = false; 123 | 124 | // someCriticalStuffThatCantWait is just a toy function 125 | // to represent the *reason* you want to use asynchronous 126 | // calls rather than wait around for pings to come home. 127 | // Here, we just print out some chars... 128 | void someCriticalStuffThatCantWait() 129 | { 130 | for (int i = 0; i < 10; i++) 131 | { 132 | if (lastPingSucceeded) { 133 | Serial.print('.'); 134 | } 135 | } 136 | Serial.print('_'); 137 | } 138 | 139 | void loop() 140 | { 141 | 142 | lastPingSucceeded = false; 143 | ICMPEchoReply echoResult; // we'll get the status here 144 | Serial.println("Starting async ping."); 145 | 146 | // asynchStart will return false if we couldn't 147 | // even send a ping, though you could also check the 148 | // echoResult.status with should == 149 | if (! ping.asyncStart(pingAddr, 3, echoResult)) 150 | { 151 | Serial.print("Couldn't even send our ping request?? Status: "); 152 | Serial.println((int)echoResult.status); 153 | delay(500); 154 | return; 155 | 156 | } 157 | 158 | // ok the ping started out well... 159 | Serial.println("Ping sent "); 160 | while (! ping.asyncComplete(echoResult)) 161 | { 162 | // we have critical stuff we wish to handle 163 | // while we wait for ping to come back 164 | someCriticalStuffThatCantWait(); 165 | } 166 | 167 | // async is done! let's see how it worked out... 168 | if (echoResult.status != SUCCESS) 169 | { 170 | // failure... but whyyyy? 171 | sprintf(buffer, "Echo request failed; %d", echoResult.status); 172 | } else { 173 | // huzzah 174 | lastPingSucceeded = true; 175 | sprintf(buffer, 176 | "Reply[%d] from: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d", 177 | echoResult.data.seq, 178 | echoResult.addr[0], 179 | echoResult.addr[1], 180 | echoResult.addr[2], 181 | echoResult.addr[3], 182 | REQ_DATASIZE, 183 | millis() - echoResult.data.time, 184 | echoResult.ttl); 185 | } 186 | 187 | Serial.println(buffer); 188 | delay(500); 189 | } 190 | 191 | -------------------------------------------------------------------------------- /icmp_ping/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 | -------------------------------------------------------------------------------- /icmp_ping/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 | --------------------------------------------------------------------------------