Connection works under ping_computer_from_arduino.ino
This commit is contained in:
parent
db993cae11
commit
863e486944
381
Arduino/libraries/ICMPPing/ICMPPing.cpp
Normal file
381
Arduino/libraries/ICMPPing/ICMPPing.cpp
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Blake Foster <blfoster@vassar.edu>
|
||||
*
|
||||
* 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 "ICMPPing.h"
|
||||
#include <util.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 ICMPEcho& 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;
|
||||
}
|
||||
|
||||
ICMPEcho::ICMPEcho(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);
|
||||
}
|
||||
|
||||
ICMPEcho::ICMPEcho()
|
||||
: seq(0), id(0), time(0)
|
||||
{
|
||||
memset(payload, 0, sizeof(payload));
|
||||
icmpHeader.code = 0;
|
||||
icmpHeader.type = 0;
|
||||
icmpHeader.checksum = 0;
|
||||
}
|
||||
|
||||
void ICMPEcho::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 ICMPEcho::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 ICMPPing::ping_timeout = PING_TIMEOUT;
|
||||
|
||||
ICMPPing::ICMPPing(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 ICMPPing::setPayload(uint8_t * payload)
|
||||
{
|
||||
memcpy(_payload, payload, REQ_DATASIZE);
|
||||
}
|
||||
|
||||
void ICMPPing::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 ICMPPing::operator()(const IPAddress& addr, int nRetries, ICMPEchoReply& result)
|
||||
{
|
||||
openSocket();
|
||||
|
||||
ICMPEcho echoReq(ICMP_ECHOREQ, _id, _nextSeq++, _payload);
|
||||
|
||||
for (_attempt=0; _attempt<nRetries; ++_attempt)
|
||||
{
|
||||
|
||||
ICMPPING_DOYIELD();
|
||||
|
||||
result.status = sendEchoRequest(addr, echoReq);
|
||||
if (result.status == SUCCESS)
|
||||
{
|
||||
byte replyAddr [4];
|
||||
ICMPPING_DOYIELD();
|
||||
receiveEchoReply(echoReq, addr, result);
|
||||
}
|
||||
if (result.status == SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
W5100.execCmdSn(_socket, Sock_CLOSE);
|
||||
W5100.writeSnIR(_socket, 0xFF);
|
||||
}
|
||||
|
||||
ICMPEchoReply ICMPPing::operator()(const IPAddress& addr, int nRetries)
|
||||
{
|
||||
ICMPEchoReply reply;
|
||||
operator()(addr, nRetries, reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
Status ICMPPing::sendEchoRequest(const IPAddress& addr, const ICMPEcho& 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, 128);
|
||||
// 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(ICMPEcho)];
|
||||
echoReq.serialize(serialized);
|
||||
|
||||
W5100.send_data_processing(_socket, serialized, sizeof(ICMPEcho));
|
||||
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;
|
||||
}
|
||||
|
||||
void ICMPPing::receiveEchoReply(const ICMPEcho& echoReq, const IPAddress& addr, ICMPEchoReply& echoReply)
|
||||
{
|
||||
icmp_time_t start = millis();
|
||||
while (millis() - start < ping_timeout)
|
||||
{
|
||||
|
||||
if (W5100.getRXReceivedSize(_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);
|
||||
W5100.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(ICMPEcho)];
|
||||
if (dataLen > sizeof(ICMPEcho))
|
||||
dataLen = sizeof(ICMPEcho);
|
||||
W5100.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ICMPPING_ASYNCH_ENABLE
|
||||
/*
|
||||
* When ICMPPING_ASYNCH_ENABLE is defined, we have access to the
|
||||
* asyncStart()/asyncComplete() methods from the API.
|
||||
*/
|
||||
bool ICMPPing::asyncSend(ICMPEchoReply& result)
|
||||
{
|
||||
ICMPEcho echoReq(ICMP_ECHOREQ, _id, _curSeq, _payload);
|
||||
|
||||
Status sendOpResult(NO_RESPONSE);
|
||||
bool sendSuccess = false;
|
||||
for (uint8_t i=_attempt; i<_numRetries; ++i)
|
||||
{
|
||||
_attempt++;
|
||||
|
||||
ICMPPING_DOYIELD();
|
||||
sendOpResult = sendEchoRequest(_addr, echoReq);
|
||||
if (sendOpResult == SUCCESS)
|
||||
{
|
||||
sendSuccess = true; // it worked
|
||||
sendOpResult = ASYNC_SENT; // we're doing this async-style, force the status
|
||||
_asyncstart = millis(); // not the start time, for timeouts
|
||||
break; // break out of this loop, 'cause we're done.
|
||||
|
||||
}
|
||||
}
|
||||
_asyncstatus = sendOpResult; // keep track of this, in case the ICMPEchoReply isn't re-used
|
||||
result.status = _asyncstatus; // set the result, in case the ICMPEchoReply is checked
|
||||
return sendSuccess; // return success of send op
|
||||
}
|
||||
bool ICMPPing::asyncStart(const IPAddress& addr, int nRetries, ICMPEchoReply& result)
|
||||
{
|
||||
openSocket();
|
||||
|
||||
// stash our state, so we can access
|
||||
// in asynchSend()/asyncComplete()
|
||||
_numRetries = nRetries;
|
||||
_attempt = 0;
|
||||
_curSeq = _nextSeq++;
|
||||
_addr = addr;
|
||||
|
||||
return asyncSend(result);
|
||||
|
||||
}
|
||||
|
||||
bool ICMPPing::asyncComplete(ICMPEchoReply& result)
|
||||
{
|
||||
|
||||
if (_asyncstatus != ASYNC_SENT)
|
||||
{
|
||||
// we either:
|
||||
// - didn't start an async request;
|
||||
// - failed to send; or
|
||||
// - are no longer waiting on this packet.
|
||||
// either way, we're done
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (W5100.getRXReceivedSize(_socket))
|
||||
{
|
||||
// ooooh, we've got a pending reply
|
||||
ICMPEcho echoReq(ICMP_ECHOREQ, _id, _curSeq, _payload);
|
||||
receiveEchoReply(echoReq, _addr, result);
|
||||
_asyncstatus = result.status; // make note of this status, whatever it is.
|
||||
return true; // whatever the result of the receiveEchoReply(), the async op is done.
|
||||
}
|
||||
|
||||
// nothing yet... check if we've timed out
|
||||
if ( (millis() - _asyncstart) > 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.
|
||||
return true;
|
||||
}
|
||||
|
||||
// we timed out and have no more attempts left...
|
||||
// hello? is anybody out there?
|
||||
// guess not:
|
||||
result.status = NO_RESPONSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// have yet to time out, will wait some more:
|
||||
return false; // results still not in
|
||||
|
||||
}
|
||||
|
||||
#endif /* ICMPPING_ASYNCH_ENABLE */
|
||||
|
||||
|
||||
|
298
Arduino/libraries/ICMPPing/ICMPPing.h
Normal file
298
Arduino/libraries/ICMPPing/ICMPPing.h
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2010 by Blake Foster <blfoster@vassar.edu>
|
||||
*
|
||||
* 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 <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <utility/w5100.h>
|
||||
|
||||
#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 ICMPHeader;
|
||||
class ICMPPing;
|
||||
|
||||
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 ICMPHeader
|
||||
{
|
||||
/*
|
||||
Header for an ICMP packet. Does not include the IP header.
|
||||
*/
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t checksum;
|
||||
};
|
||||
|
||||
|
||||
struct ICMPEcho
|
||||
{
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
ICMPEcho(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.
|
||||
*/
|
||||
ICMPEcho();
|
||||
|
||||
ICMPHeader 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 ICMPEchoReply
|
||||
{
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
ICMPEcho data;
|
||||
uint8_t ttl;
|
||||
Status status;
|
||||
IPAddress addr;
|
||||
};
|
||||
|
||||
|
||||
class ICMPPing
|
||||
{
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
ICMPPing(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.
|
||||
*/
|
||||
ICMPEchoReply 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, ICMPEchoReply& 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
|
||||
|
||||
ICMPPing ping(0, (uint16_t)random(0, 255));
|
||||
ICMPEchoReply 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, ICMPEchoReply& 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(ICMPEchoReply& result);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// holds the timeout, in ms, for all objects of this class.
|
||||
static uint16_t ping_timeout;
|
||||
|
||||
void openSocket();
|
||||
|
||||
Status sendEchoRequest(const IPAddress& addr, const ICMPEcho& echoReq);
|
||||
void receiveEchoReply(const ICMPEcho& echoReq, const IPAddress& addr, ICMPEchoReply& echoReply);
|
||||
|
||||
|
||||
|
||||
#ifdef ICMPPING_ASYNCH_ENABLE
|
||||
// extra internal state/methods used when asynchronous pings
|
||||
// are enabled.
|
||||
bool asyncSend(ICMPEchoReply& 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)
|
31
Arduino/libraries/ICMPPing/keywords.txt
Normal file
31
Arduino/libraries/ICMPPing/keywords.txt
Normal file
@ -0,0 +1,31 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Ethernet
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ICMPPing KEYWORD1
|
||||
ICMPHeader KEYWORD1
|
||||
ICMPEcho KEYWORD1
|
||||
ICMPEchoReply KEYWORD1
|
||||
Status KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
SUCCESS LITERAL1
|
||||
SEND_TIMEOUT LITERAL1
|
||||
NO_RESPONSE LITERAL1
|
||||
BAD_RESPONSE LITERAL1
|
||||
REQ_DATASIZE LITERAL1
|
||||
ICMP_ECHOREPLY LITERAL1
|
||||
ICMP_ECHOREQ LITERAL1
|
||||
ICMP_ECHOREP LITERAL1
|
||||
PING_TIMEOUT LITERAL1
|
14
Arduino/libraries/ICMPPing/util.h
Normal file
14
Arduino/libraries/ICMPPing/util.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#define htons(x) ( ((x)<< 8 & 0xFF00) | \
|
||||
((x)>> 8 & 0x00FF) )
|
||||
#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
|
@ -15,10 +15,10 @@ void resetSensor() {
|
||||
//
|
||||
// W5500 configuration
|
||||
#include <SPI.h>
|
||||
#include <Ethernet3.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
// Configure MAC address and IP:
|
||||
byte mac[] = { 0x61, 0x2C, 0xF2, 0x09, 0x73, 0xBE };
|
||||
byte mac[] = { 0x97, 0x8A, 0xC5, 0x86, 0xA4, 0xEF };
|
||||
char T[8];
|
||||
char H[8];
|
||||
char message[20];
|
||||
@ -30,8 +30,15 @@ char message[20];
|
||||
// Define CS and RST pins:
|
||||
#define W5500_CS_PIN 10 // 8 in E LEGO
|
||||
#define W5500_RST_PIN 9 //10 in E LEGO
|
||||
IPAddress serverIP(10, 44, 1, 238); // Computer
|
||||
IPAddress W5500_ip(10, 44, 1, 22); // Change the last digit
|
||||
//IPAddress serverIP(10, 44, 1, 238); // Computer
|
||||
//IPAddress W5500_ip(10, 44, 1, 22); // Change the last digit
|
||||
IPAddress serverIP(10, 44, 1, 238);
|
||||
IPAddress W5500_ip(10, 11, 1, 46);
|
||||
IPAddress gateway(10, 11, 1, 1);
|
||||
IPAddress DNS(147,142,19,254);
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
|
||||
|
||||
|
||||
// Don't change
|
||||
const int port = 5005;
|
||||
@ -78,9 +85,9 @@ void setup() {
|
||||
|
||||
// Initialize Ethernet:
|
||||
Ethernet.init(W5500_CS_PIN);
|
||||
Ethernet.begin(mac, W5500_ip);
|
||||
delay(1500);
|
||||
Ethernet.begin(mac, W5500_ip, DNS, gateway, subnet);
|
||||
|
||||
delay(1500);
|
||||
Serial.print("W5500 IP: ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
|
||||
@ -93,12 +100,13 @@ void setup() {
|
||||
Serial.println("Test Message sent");
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
// Measure temp. and humidity every 5 seconds
|
||||
// Measure temp. and humidity every 30 seconds
|
||||
delay(15000);
|
||||
float t = sht31.readTemperature();
|
||||
float h = sht31.readHumidity();
|
||||
|
@ -20,5 +20,5 @@ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
with conn:
|
||||
data = conn.recv(1024)
|
||||
if data:
|
||||
print(f"Received from {addr} at {datetime.now()}: {data}")
|
||||
print(f"Received from {addr} at {datetime.now()}: {data.decode()}")
|
||||
|
||||
|
@ -60,7 +60,7 @@ def write_data_to_influxDB(influx_client: InfluxDBClient, write_api: WriteApi, r
|
||||
def setup_logger(logName):
|
||||
|
||||
# creat logger
|
||||
log_dir = "logs"
|
||||
log_dir = "../../Data/logs"
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# setup logging
|
||||
@ -117,7 +117,7 @@ if __name__ == "__main__":
|
||||
if (message is not None and temp_or_hum == "T"):
|
||||
print (str (datetime.now () )[:19] + ": Temp is " + message)
|
||||
T = float (message)
|
||||
p1 = influxdb_client.Point ("FerDy").tag("Table", "Philipp's").field ("temp_on_desk", T)
|
||||
p1 = influxdb_client.Point ("FerDy").tag("Climate_control", "temperature").field ("temp_Eilons_desk", T)
|
||||
temp_or_hum = "a"
|
||||
influx_client, write_api = write_data_to_influxDB (influx_client, write_api, p1)
|
||||
time.sleep (update_interval/2)
|
||||
@ -125,7 +125,7 @@ if __name__ == "__main__":
|
||||
elif (message is not None and temp_or_hum == "H"):
|
||||
print (str (datetime.now () )[:19] + ": humidity is " + message)
|
||||
H = float (message)
|
||||
p1 = influxdb_client.Point ("FerDy").tag("Table", "Philipp's").field ("hum_on_desk", H)
|
||||
p1 = influxdb_client.Point ("FerDy").tag("Climate_control", "humidity").field ("hum_Eilons_desk", H)
|
||||
temp_or_hum = "a"
|
||||
influx_client, write_api = write_data_to_influxDB (influx_client, write_api, p1)
|
||||
time.sleep (update_interval/2)
|
||||
|
@ -0,0 +1,111 @@
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include "Adafruit_SHT31.h"
|
||||
|
||||
Adafruit_SHT31 sht31 = Adafruit_SHT31();
|
||||
|
||||
#define SHT31_RST_PIN 16
|
||||
|
||||
void reset_SHT31() {
|
||||
pinMode(SHT31_RST_PIN, OUTPUT);
|
||||
digitalWrite(SHT31_RST_PIN, LOW);
|
||||
delay(10);
|
||||
digitalWrite(SHT31_RST_PIN, HIGH);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
// MAC and IP configuration
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
IPAddress ip(10, 11, 1, 46);
|
||||
IPAddress gateway(10, 11, 1, 1);
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
|
||||
// Server to connect to
|
||||
IPAddress server(10, 44, 1, 238);
|
||||
const int serverPort = 5005; // Change to the port your server listens on
|
||||
|
||||
EthernetClient client;
|
||||
|
||||
char T[8];
|
||||
char H[8];
|
||||
char message[20];
|
||||
|
||||
void setup() {
|
||||
|
||||
// Initialize Ethernet
|
||||
Serial.begin(9600);
|
||||
while (!Serial); // Wait for Serial (only needed for native USB boards)
|
||||
|
||||
Ethernet.begin(mac, ip, gateway, gateway, subnet);
|
||||
delay(1000); // Give Ethernet shield time to initialize
|
||||
|
||||
Serial.println("Connecting to server");
|
||||
|
||||
if (client.connect(server, serverPort)) {
|
||||
client.write("Connection check");
|
||||
Serial.println("Connection successful");
|
||||
client.stop();
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
|
||||
// Initialize SHT31
|
||||
if (!sht31.begin(0X44)) {
|
||||
Serial.println("Couldn't find SHT31");
|
||||
while (1) delay(100);
|
||||
}
|
||||
Serial.println("SHT31 Found");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
// Measure temp. and humidity every 30 seconds
|
||||
delay(15000);
|
||||
float t = sht31.readTemperature();
|
||||
float h = sht31.readHumidity();
|
||||
|
||||
// Report temperature to Eilon's computer
|
||||
if (client.connect(server, serverPort)) {
|
||||
client.write("Temp");
|
||||
client.stop();
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
if (client.connect(server, serverPort)) {
|
||||
if (!isnan(t)) {
|
||||
dtostrf(t, 6, 2, T);
|
||||
snprintf(message, sizeof(message), "T %s", T);
|
||||
client.write(T);
|
||||
client.stop();
|
||||
}
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
// Report humidity to Eilon's computer
|
||||
delay(15000);
|
||||
if (client.connect(server, serverPort)) {
|
||||
client.write("Humidity");
|
||||
client.stop();
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
if (client.connect(server, serverPort)) {
|
||||
if (!isnan(h)) {
|
||||
dtostrf(h, 6, 2, H);
|
||||
snprintf(message, sizeof(message), "H %s", H);
|
||||
client.write(H);
|
||||
client.stop();
|
||||
Serial.println("message sent");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
2025-07-01 12:04:21,940 - INFO - Logger set up complete
|
||||
2025-07-01 12:04:21,940 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 12:05:54,673 - INFO - Logger set up complete
|
||||
2025-07-01 12:05:54,673 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 12:09:29,668 - INFO - Logger set up complete
|
||||
2025-07-01 12:09:29,668 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:42:01,247 - INFO - Logger set up complete
|
||||
2025-07-01 16:42:01,247 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:45:29,238 - INFO - Logger set up complete
|
||||
2025-07-01 16:45:29,239 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:46:28,621 - INFO - Logger set up complete
|
||||
2025-07-01 16:46:28,621 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:49:50,243 - INFO - Logger set up complete
|
||||
2025-07-01 16:49:50,243 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:50:28,107 - INFO - Logger set up complete
|
||||
2025-07-01 16:50:28,108 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 16:50:53,880 - INFO - Logger set up complete
|
||||
2025-07-01 16:50:53,881 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 17:20:51,260 - INFO - Logger set up complete
|
||||
2025-07-01 17:20:51,260 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 17:21:00,372 - INFO - Logger set up complete
|
||||
2025-07-01 17:21:00,372 - INFO - Reading the temperature on Phillip's desk
|
||||
2025-07-01 17:21:36,585 - INFO - Logger set up complete
|
||||
2025-07-01 17:21:36,585 - INFO - Reading the temperature on Phillip's desk
|
Loading…
Reference in New Issue
Block a user