diff --git a/libraries/Ethernet/IPAddress.cpp b/libraries/Ethernet/IPAddress.cpp new file mode 100644 index 000000000..389329de0 --- /dev/null +++ b/libraries/Ethernet/IPAddress.cpp @@ -0,0 +1,39 @@ + +#include +#include + +IPAddress::IPAddress() +{ + memset(_address, 0, sizeof(_address)); +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + _address[0] = first_octet; + _address[1] = second_octet; + _address[2] = third_octet; + _address[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) +{ + memcpy(_address, &address, sizeof(_address)); +} + +IPAddress::IPAddress(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); +} + +IPAddress& IPAddress::operator=(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) +{ + memcpy(_address, (const uint8_t *)&address, sizeof(_address)); + return *this; +} + diff --git a/libraries/Ethernet/IPAddress.h b/libraries/Ethernet/IPAddress.h new file mode 100644 index 000000000..586385320 --- /dev/null +++ b/libraries/Ethernet/IPAddress.h @@ -0,0 +1,55 @@ +/* + * + * MIT License: + * Copyright (c) 2011 Adrian McEwen + * 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. + * + * adrianm@mcqn.com 1/1/2011 + */ + +#ifndef IPAddress_h +#define IPAddress_h + +// A class to make it easier to handle and pass around IP addresses + +class IPAddress { +private: + uint8_t _address[4]; // IPv4 address + +public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint8_t*() { return _address; }; + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { return _address[index]; }; + uint8_t& operator[](int index) { return _address[index]; }; + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); +}; + +#endif diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index 07c454df2..6ad8aca7e 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -56,97 +56,12 @@ uint8_t UDP::begin(uint16_t port) { return 1; } -/* Send packet contained in buf of length len to peer at specified ip, and port */ -/* Use this function to transmit binary data that might contain 0x00 bytes*/ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(uint8_t * buf, uint16_t len, uint8_t * ip, uint16_t port){ - return sendto(_sock,(const uint8_t *)buf,len,ip,port); -} - -/* Send zero-terminated string str as packet to peer at specified ip, and port */ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(const char str[], uint8_t * ip, uint16_t port){ - // compute strlen - const char *s; - for(s = str; *s; ++s); - uint16_t len = (s-str); - // send packet - return sendto(_sock,(const uint8_t *)str,len,ip,port); -} /* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes. * returned value includes 8 byte UDP header!*/ int UDP::available() { return W5100.getRXReceivedSize(_sock); } - -/* Read a received packet into buffer buf (which is of maximum length len); */ -/* store calling ip and port as well. Call available() to make sure data is ready first. */ -/* NOTE: I don't believe len is ever checked in implementation of recvfrom(),*/ -/* so it's easy to overflow buffer. so we check and truncate. */ -/* returns number of bytes read, or negative number of bytes we would have needed if we truncated */ -int UDP::readPacket(uint8_t * buf, uint16_t bufLen, uint8_t *ip, uint16_t *port) { - int packetLen = available()-8; //skip UDP header; - if(packetLen < 0 ) return 0; // no real data here - if(packetLen > (int)bufLen) { - //packet is too large - truncate - //HACK - hand-parse the UDP packet using TCP recv method - uint8_t tmpBuf[8]; - int i; - //read 8 header bytes and get IP and port from it - recv(_sock,tmpBuf,8); - ip[0] = tmpBuf[0]; - ip[1] = tmpBuf[1]; - ip[2] = tmpBuf[2]; - ip[3] = tmpBuf[3]; - *port = tmpBuf[4]; - *port = (*port << 8) + tmpBuf[5]; - - //now copy first (bufLen) bytes into buf - for(i=0;i<(int)bufLen;i++) { - recv(_sock,tmpBuf,1); - buf[i]=tmpBuf[0]; - } - - //and just read the rest byte by byte and throw it away - while(available()) { - recv(_sock,tmpBuf,1); - } - - return (-1*packetLen); - - //ALTERNATIVE: requires stdlib - takes a bunch of space - /*//create new buffer and read everything into it - uint8_t * tmpBuf = (uint8_t *)malloc(packetLen); - recvfrom(_sock,tmpBuf,packetLen,ip,port); - if(!tmpBuf) return 0; //couldn't allocate - // copy first bufLen bytes - for(unsigned int i=0; i 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + // When we get here, any remaining bytes are the data + ret = available(); + } + return ret; +} + +int UDP::read() +{ + uint8_t byte; + if (recv(_sock, &byte, 1) > 0) + { + // We read things without any problems + return byte; + } + // If we get here, there's no data available + return -1; +} + +int UDP::read(unsigned char* buffer, size_t len) +{ + /* In the readPacket that copes with truncating packets, the buffer was + filled with this code. Not sure why it loops round reading out a byte + at a time. + int i; + for(i=0;i<(int)bufLen;i++) { + recv(_sock,tmpBuf,1); + buf[i]=tmpBuf[0]; + } + */ + return recv(_sock, buffer, len); +} + +int UDP::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must + if (!available()) + return -1; + ::peek(_sock, &b); + return b; +} + +void UDP::flush() +{ + while (available()) + { + read(); + } +} + diff --git a/libraries/Ethernet/Udp.h b/libraries/Ethernet/Udp.h index c801ee277..3970a9662 100644 --- a/libraries/Ethernet/Udp.h +++ b/libraries/Ethernet/Udp.h @@ -37,28 +37,57 @@ #ifndef udp_h #define udp_h +#include +#include + #define UDP_TX_PACKET_MAX_SIZE 24 -class UDP { +class UDP : public Stream { private: uint8_t _sock; // socket ID for Wiz5100 uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent public: - UDP(); + UDP(); // Constructor uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - int available(); // has data been received? + void stop(); // Finish with the UDP socket - // C-style buffer-oriented functions - uint16_t sendPacket(uint8_t *, uint16_t, uint8_t *, uint16_t); //send a packet to specified peer - uint16_t sendPacket(const char[], uint8_t *, uint16_t); //send a string as a packet to specified peer - int readPacket(uint8_t *, uint16_t); // read a received packet - int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port - // readPacket that fills a character string buffer - int readPacket(char *, uint16_t, uint8_t *, uint16_t &); + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + int beginPacket(IPAddress ip, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPacket(); + // Write a single byte into the packet + virtual void write(uint8_t); + // Write a string of characters into the packet + virtual void write(const char *str); + // Write size bytes from buffer into the packet + virtual void write(const uint8_t *buffer, size_t size); - // Finish with the UDP socket - void stop(); + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len); + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + uint16_t remotePort() { return _remotePort; }; }; #endif diff --git a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde index f27290f54..e23410139 100644 --- a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde +++ b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde @@ -22,15 +22,10 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen on -// the next two variables are set when a packet is received -byte remoteIp[4]; // holds received packet's originating IP -unsigned int remotePort; // holds received packet's originating port - // buffers for receiving and sending data char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, char ReplyBuffer[] = "acknowledged"; // a string to send back @@ -48,19 +43,33 @@ void setup() { void loop() { // if there's data available, read a packet - int packetSize = Udp.available(); // note that this includes the UDP header + int packetSize = Udp.parsePacket(); if(packetSize) { - packetSize = packetSize - 8; // subtract the 8 byte header Serial.print("Received packet of size "); Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); - // read the packet into packetBufffer and get the senders IP addr and port number - Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort); + // read the packet into packetBufffer + Udp.read((byte*)packetBuffer,UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer); - Udp.sendPacket( ReplyBuffer, remoteIp, remotePort); + // send a reply, to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write(ReplyBuffer); + Udp.endPacket(); } delay(10); } diff --git a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde index 22bc754c5..cfb5a2bc1 100644 --- a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde +++ b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde @@ -24,14 +24,12 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen for UDP packets -byte timeServer[] = { - 192, 43, 244, 18}; // time.nist.gov NTP server +IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message @@ -55,8 +53,9 @@ void loop() // wait to see if a reply is available delay(1000); - if ( Udp.available() ) { - Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer + if ( Udp.parsePacket() ) { + // We've received a packet, read the data from it + Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: @@ -118,7 +117,9 @@ unsigned long sendNTPpacket(byte *address) // all NTP fields have been given values, now // you can send a packet requesting a timestamp: - Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE, address, 123); //NTP requests are to port 123 + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer,NTP_PACKET_SIZE); + Udp.endPacket(); } diff --git a/libraries/Ethernet/keywords.txt b/libraries/Ethernet/keywords.txt index ebc579363..7fdcedf09 100644 --- a/libraries/Ethernet/keywords.txt +++ b/libraries/Ethernet/keywords.txt @@ -9,6 +9,7 @@ Ethernet KEYWORD1 Client KEYWORD1 Server KEYWORD1 +IPAddress KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -19,10 +20,16 @@ connect KEYWORD2 write KEYWORD2 available KEYWORD2 read KEYWORD2 +peek KEYWORD2 flush KEYWORD2 stop KEYWORD2 connected KEYWORD2 begin KEYWORD2 +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/libraries/Ethernet/utility/socket.cpp b/libraries/Ethernet/utility/socket.cpp index cad54a5e3..487584511 100644 --- a/libraries/Ethernet/utility/socket.cpp +++ b/libraries/Ethernet/utility/socket.cpp @@ -344,3 +344,58 @@ uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len) return ret; } +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len) +{ + uint16_t ret =0; + if (len > W5100.getTXFreeSize(s)) + { + ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size. + } + else + { + ret = len; + } + W5100.send_data_processing_offset(s, offset, buf, ret); + return ret; +} + +int startUDP(SOCKET s, uint8_t* addr, uint16_t port) +{ + if + ( + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + ((port == 0x00)) + ) + { + return 0; + } + else + { + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + return 1; + } +} + +int sendUDP(SOCKET s) +{ + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) + { + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* +2008.01 [bj]: clear interrupt */ + W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT)); + return 0; + } + } + + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); + + /* Sent ok */ + return 1; +} + diff --git a/libraries/Ethernet/utility/socket.h b/libraries/Ethernet/utility/socket.h index 48e2d877b..37ea93f3d 100755 --- a/libraries/Ethernet/utility/socket.h +++ b/libraries/Ethernet/utility/socket.h @@ -16,5 +16,26 @@ extern uint16_t recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len); +// Functions to allow buffered UDP send (i.e. where the UDP datagram is built up over a +// number of calls before being sent +/* + @brief This function sets up a UDP datagram, the data for which will be provided by one + or more calls to bufferData and then finally sent with sendUDP. + @return 1 if the datagram was successfully set up, or 0 if there was an error +*/ +extern int startUDP(SOCKET s, uint8_t* addr, uint16_t port); +/* + @brief This function copies up to len bytes of data from buf into a UDP datagram to be + sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + @return Number of bytes successfully buffered +*/ +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len); +/* + @brief Send a UDP datagram built up from a sequence of startUDP followed by one or more + calls to bufferData. + @return 1 if the datagram was successfully sent, or 0 if there was an error +*/ +int sendUDP(SOCKET s); + #endif /* _SOCKET_H_ */ diff --git a/libraries/Ethernet/utility/w5100.cpp b/libraries/Ethernet/utility/w5100.cpp index aaf3071f7..aafabaeec 100644 --- a/libraries/Ethernet/utility/w5100.cpp +++ b/libraries/Ethernet/utility/w5100.cpp @@ -65,10 +65,16 @@ uint16_t W5100Class::getRXReceivedSize(SOCKET s) } -void W5100Class::send_data_processing(SOCKET s, uint8_t *data, uint16_t len) +void W5100Class::send_data_processing(SOCKET s, const uint8_t *data, uint16_t len) +{ + // This is same as having no offset in a call to send_data_processing_offset + send_data_processing_offset(s, 0, data, len); +} + +void W5100Class::send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len) { uint16_t ptr = readSnTX_WR(s); - + ptr += data_offset; uint16_t offset = ptr & SMASK; uint16_t dstAddr = offset + SBASE[s]; @@ -132,7 +138,7 @@ uint8_t W5100Class::write(uint16_t _addr, uint8_t _data) return 1; } -uint16_t W5100Class::write(uint16_t _addr, uint8_t *_buf, uint16_t _len) +uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) { for (int i=0; i<_len; i++) { diff --git a/libraries/Ethernet/utility/w5100.h b/libraries/Ethernet/utility/w5100.h index 118e5d80d..9872c7ccb 100755 --- a/libraries/Ethernet/utility/w5100.h +++ b/libraries/Ethernet/utility/w5100.h @@ -146,7 +146,19 @@ public: * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer * register. User should read upper byte first and lower byte later to get proper value. */ - void send_data_processing(SOCKET s, uint8_t *data, uint16_t len); + void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); + /** + * @brief A copy of send_data_processing that uses the provided ptr for the + * write offset. Only needed for the "streaming" UDP API, where + * a single UDP packet is built up over a number of calls to + * send_data_processing_ptr, because TX_WR doesn't seem to get updated + * correctly in those scenarios + * @param ptr value to use in place of TX_WR. If 0, then the value is read + * in from TX_WR + * @return New value for ptr, to be used in the next call + */ +// FIXME Update documentation + void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); /** * @brief This function is being called by recv() also. @@ -182,7 +194,7 @@ public: // --------------- private: static uint8_t write(uint16_t _addr, uint8_t _data); - static uint16_t write(uint16_t addr, uint8_t *buf, uint16_t len); + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); static uint8_t read(uint16_t addr); static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);