From c7c21d3898cb62df301806b83c8f3debbf624b06 Mon Sep 17 00:00:00 2001 From: TMRh20 Date: Fri, 16 Jan 2015 17:14:51 -0600 Subject: [PATCH] Major update v1.2b, add UDP, DNS support - Enable user configuration for UIP_ACTIVE_OPEN to save memory/prog space ( disables client functionality of uIP IP stack ) - Link UIP BUFFER SIZE directly to the RF24Network MAX_PAYLOAD_SIZE variable to simplify configuration by only setting the variable in one place - Add support for UDP - Re-organize config file - Limit TCP window size and MSS to 511 bytes. For some reason, uIP doesn't handle packets properly if this is defined at a larger value. (See TCP flow control and congestion avoidance) - Config uip logging via serial (disabled) - Add debugging for UDP - Add debugging for DNS - Consolidate uip and RF24Ethernet includes into RF24Ethernet.h - Remove the need for use_device() function. (maintained for backwards compatibility) - Proper handling of multiple connections. Data buffers allocated on a per-connection basis. - De-include all arp code when using TUN / RF24Mesh - Add slight delay before responding after a payload is received - Support for payload sizes larger than 255 bytes (max 511) - Cleanup debugging printouts - Condense uip_poll and uip_rexmit code - Fix slight mistake in logic for window re-opening - Fix logic in available() to handle changes with multiple connections - Add code for Client.flush(); and Client.peek(); --- Dns.cpp | 438 ++++++++++++++++++++++++++++++++++++++++++ Dns.h | 45 +++++ Doxyfile | 2 +- RF24Client.cpp | 142 +++++++------- RF24Client.h | 28 +-- RF24Ethernet.cpp | 49 +++-- RF24Ethernet.h | 75 ++++---- RF24Ethernet_config.h | 42 ++-- RF24Server.cpp | 2 +- RF24Udp.cpp | 430 +++++++++++++++++++++++++++++++++++++++++ RF24Udp.h | 129 +++++++++++++ uip-conf.h | 131 ++++++++----- utility/uipopt.h | 10 +- 13 files changed, 1319 insertions(+), 204 deletions(-) create mode 100644 Dns.cpp create mode 100644 Dns.h create mode 100644 RF24Udp.cpp create mode 100644 RF24Udp.h diff --git a/Dns.cpp b/Dns.cpp new file mode 100644 index 0000000..78dbb76 --- /dev/null +++ b/Dns.cpp @@ -0,0 +1,438 @@ +// Arduino DNS client for Enc28J60-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "RF24Ethernet.h" + + +#if UIP_CONF_UDP > 0 + + +#define SOCKET_NONE 255 +// Various flags and header field values for a DNS message +#define UDP_HEADER_SIZE 8 +#define DNS_HEADER_SIZE 12 +#define TTL_SIZE 4 +#define QUERY_FLAG (0) +#define RESPONSE_FLAG (1<<15) +#define QUERY_RESPONSE_MASK (1<<15) +#define OPCODE_STANDARD_QUERY (0) +#define OPCODE_INVERSE_QUERY (1<<11) +#define OPCODE_STATUS_REQUEST (2<<11) +#define OPCODE_MASK (15<<11) +#define AUTHORITATIVE_FLAG (1<<10) +#define TRUNCATION_FLAG (1<<9) +#define RECURSION_DESIRED_FLAG (1<<8) +#define RECURSION_AVAILABLE_FLAG (1<<7) +#define RESP_NO_ERROR (0) +#define RESP_FORMAT_ERROR (1) +#define RESP_SERVER_FAILURE (2) +#define RESP_NAME_ERROR (3) +#define RESP_NOT_IMPLEMENTED (4) +#define RESP_REFUSED (5) +#define RESP_MASK (15) +#define TYPE_A (0x0001) +#define CLASS_IN (0x0001) +#define LABEL_COMPRESSION_MASK (0xC0) +// Port number that DNS servers listen on +#define DNS_PORT 53 + +// Possible return codes from ProcessResponse +#define SUCCESS 1 +#define TIMED_OUT -1 +#define INVALID_SERVER -2 +#define TRUNCATED -3 +#define INVALID_RESPONSE -4 + +void DNSClient::begin(const IPAddress& aDNSServer) +{ + iDNSServer = aDNSServer; + iRequestId = 0; +} + + +int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult) +{ + // See if we've been given a valid IP address + const char* p =aIPAddrString; + while (*p && + ( (*p == '.') || (*p >= '0') || (*p <= '9') )) + { + p++; + } + + if (*p == '\0') + { + // It's looking promising, we haven't found any invalid characters + p = aIPAddrString; + int segment =0; + int segmentValue =0; + while (*p && (segment < 4)) + { + if (*p == '.') + { + // We've reached the end of a segment + if (segmentValue > 255) + { + // You can't have IP address segments that don't fit in a byte + return 0; + } + else + { + aResult[segment] = (byte)segmentValue; + segment++; + segmentValue = 0; + } + } + else + { + // Next digit + segmentValue = (segmentValue*10)+(*p - '0'); + } + p++; + } + // We've reached the end of address, but there'll still be the last + // segment to deal with + if ((segmentValue > 255) || (segment > 3)) + { + // You can't have IP address segments that don't fit in a byte, + // or more than four segments + return 0; + } + else + { + aResult[segment] = (byte)segmentValue; + return 1; + } + } + else + { + return 0; + } +} + +int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) +{ + int ret =0; + + // See if it's a numeric IP address + if (inet_aton(aHostname, aResult)) + { + // It is, our work here is done + return 1; + } + + // Check we've got a valid DNS server to use + if (iDNSServer == INADDR_NONE) + { + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS Invalid DNS Server")) ); + return INVALID_SERVER; + } + + // Find a socket to use + if (iUdp.begin(1024+(millis() & 0xF)) == 1) + { + // Try up to three times + int retries = 0; +// while ((retries < 3) && (ret <= 0)) + { + // Send DNS request + ret = iUdp.beginPacket(iDNSServer, DNS_PORT); + if (ret != 0) + { + // Now output the request data + ret = BuildRequest(aHostname); + if (ret != 0) + { + // And finally send the request + ret = iUdp.endPacket(); + if (ret != 0) + { + // Now wait for a response + int wait_retries = 0; + ret = TIMED_OUT; + while ((wait_retries < 3) && (ret == TIMED_OUT)) + { + ret = ProcessResponse(5000, aResult); + wait_retries++; + } + } + } + } + retries++; + } + + // We're done with the socket now + iUdp.stop(); + } + + return ret; +} + +uint16_t DNSClient::BuildRequest(const char* aName) +{ + // Build header + // 1 1 1 1 1 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ID | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | QDCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ANCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | NSCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ARCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // As we only support one request at a time at present, we can simplify + // some of this header + iRequestId = millis(); // generate a random ID + uint16_t twoByteBuffer; + + // FIXME We should also check that there's enough space available to write to, rather + // FIXME than assume there's enough space (as the code does at present) + iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId)); + + twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG); + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = htons(1); // One question record + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = 0; // Zero answer records + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + // and zero additional records + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + // Build question + const char* start =aName; + const char* end =start; + uint8_t len; + // Run through the name being requested + while (*end) + { + // Find out how long this section of the name is + end = start; + while (*end && (*end != '.') ) + { + end++; + } + + if (end-start > 0) + { + // Write out the size of this section + len = end-start; + iUdp.write(&len, sizeof(len)); + // And then write out the section + iUdp.write((uint8_t*)start, end-start); + } + start = end+1; + } + + // We've got to the end of the question name, so + // terminate it with a zero-length section + len = 0; + iUdp.write(&len, sizeof(len)); + // Finally the type and class of question + twoByteBuffer = htons(TYPE_A); + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = htons(CLASS_IN); // Internet class of question + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + // Success! Everything buffered okay + return 1; +} + + +uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) +{ + uint32_t startTime = millis(); + + // Wait for a response packet + while(iUdp.parsePacket() <= 0) + { + if((millis() - startTime) > aTimeout){ + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24 DNS - Request timed out")); ); + return TIMED_OUT; + } + //delay(50); + } + + // We've had a reply! + // Read the UDP header + uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header + // Check that it's a response from the right server and the right port + if ( (iDNSServer != iUdp.remoteIP()) || + (iUdp.remotePort() != DNS_PORT) ) + { + // It's not from who we expected + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS - Invalid Server: ")); ); + return INVALID_SERVER; + } + + // Read through the rest of the response + if (iUdp.available() < DNS_HEADER_SIZE) + { + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS - Truncated")); ); + return TRUNCATED; + } + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS - DNS Header Size: ")); Serial.println(DNS_HEADER_SIZE); ); + iUdp.read(header, DNS_HEADER_SIZE); + + uint16_t header_flags = htons(*((uint16_t*)&header[2])); + // Check that it's a response to this request + if ( ( iRequestId != (*((uint16_t*)&header[0])) ) || + ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) + { + // Mark the entire packet as read + iUdp.flush(); + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS - Invalid Response 1, Flushing")); ); + return INVALID_RESPONSE; + } + // Check for any errors in the response (or in our request) + // although we don't do anything to get round these + if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) + { + // Mark the entire packet as read + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS - Invalid Response 2, Flushing")); ); + iUdp.flush(); + return -5; //INVALID_RESPONSE; + } + + // And make sure we've got (at least) one answer + uint16_t answerCount = htons(*((uint16_t*)&header[6])); + if (answerCount == 0 ) + { + // Mark the entire packet as read + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS - No Answer, Flushing")); ); + iUdp.flush(); + return -6; //INVALID_RESPONSE; + } + + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS OK, skipping questions ")); Serial.println(header[5]); ); + + // Skip over any questions + for (uint16_t i =0; i < HTONS(*((uint16_t*)&header[4])); i++) +// for (uint16_t i =0; i < wtf; i++) + { + // Skip over the name + uint8_t len; + do + { + iUdp.read(&len, 1); + if (len > 0) + { + // Don't need to actually read the data out for the string, just + // advance ptr to beyond it + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS Reading ")); Serial.println(len); ); + while(len--) + { + iUdp.read(); // we don't care about the returned byte + } + } + } while (len != 0); + + // Now jump over the type and class + for (int i =0; i < 4; i++) + { + iUdp.read(); // we don't care about the returned byte + } + } + + // Now we're up to the bit we're interested in, the answer + // There might be more than one answer (although we'll just use the first + // type A answer) and some authority and additional resource records but + // we're going to ignore all of them. + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS Answer Count: ")); Serial.println(answerCount); ); + for (uint16_t i =0; i < answerCount; i++) + { + // Skip the name + uint8_t len; + do + { + iUdp.read(&len, sizeof(len)); + if ((len & LABEL_COMPRESSION_MASK) == 0) + { + // It's just a normal label + if (len > 0) + { + // And it's got a length + // Don't need to actually read the data out for the string, + // just advance ptr to beyond it + while(len--) + { + iUdp.read(); // we don't care about the returned byte + } + } + } + else + { + // This is a pointer to a somewhere else in the message for the + // rest of the name. We don't care about the name, and RFC1035 + // says that a name is either a sequence of labels ended with a + // 0 length octet or a pointer or a sequence of labels ending in + // a pointer. Either way, when we get here we're at the end of + // the name + // Skip over the pointer + iUdp.read(); // we don't care about the returned byte + // And set len so that we drop out of the name loop + len = 0; + } + } while (len != 0); + + // Check the type and class + uint16_t answerType; + uint16_t answerClass; + iUdp.read((uint8_t*)&answerType, sizeof(answerType)); + iUdp.read((uint8_t*)&answerClass, sizeof(answerClass)); + + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS Type: ")); Serial.println(HTONS(answerType),HEX); Serial.print(F("RF24DNS Class: ")); Serial.println(HTONS(answerClass),HEX); ); + // Ignore the Time-To-Live as we don't do any caching + for (int i =0; i < TTL_SIZE; i++) + { + iUdp.read(); // we don't care about the returned byte + } + + // And read out the length of this answer + // Don't need header_flags anymore, so we can reuse it here + iUdp.read((uint8_t*)&header_flags, sizeof(header_flags)); + + if ( (HTONS(answerType) == TYPE_A) && (HTONS(answerClass) == CLASS_IN) ) + //if ( (answerType == TYPE_A) && (answerClass == CLASS_IN) ) + { + if (HTONS(header_flags) != 4) + { + // It's a weird size + // Mark the entire packet as read + IF_RF24ETHERNET_DEBUG_DNS( Serial.println(F("RF24DNS Flush invalid")); ); + iUdp.flush(); + return -9;//INVALID_RESPONSE; + } + iUdp.read(aAddress.raw_address(), 4); + return SUCCESS; + } + else + { + // This isn't an answer type we're after, move onto the next one + IF_RF24ETHERNET_DEBUG_DNS( Serial.print(F("RF24DNS - Get next answer type")); Serial.println(header_flags,HEX); ); + for (uint16_t i =0; i < HTONS(header_flags); i++) + //for (uint16_t i =0; i < header_flags; i++) + { + iUdp.read(); // we don't care about the returned byte + } + } + } + + // Mark the entire packet as read + iUdp.flush(); + + // If we get here then we haven't found an answer + return -10;//INVALID_RESPONSE; +} + +#endif \ No newline at end of file diff --git a/Dns.h b/Dns.h new file mode 100644 index 0000000..2b24e8b --- /dev/null +++ b/Dns.h @@ -0,0 +1,45 @@ +// Arduino DNS client for Enc28J60-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include "RF24Ethernet.h" + +#if UIP_CONF_UDP > 0 + + +class DNSClient +{ +public: + // ctor + void begin(const IPAddress& aDNSServer); + + /** Convert a numeric IP address string into a four-byte IP address. + @param aIPAddrString IP address to convert + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int inet_aton(const char *aIPAddrString, IPAddress& aResult); + + /** Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int getHostByName(const char* aHostname, IPAddress& aResult); + +protected: + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); + + IPAddress iDNSServer; + uint16_t iRequestId; + RF24UDP iUdp; +}; + +#endif +#endif \ No newline at end of file diff --git a/Doxyfile b/Doxyfile index 32ca3ef..17e0e82 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "RF24Ethernet - TCP/IP over RF24Network" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "Beta 1.1" +PROJECT_NUMBER = 1.2b # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/RF24Client.cpp b/RF24Client.cpp index 6c33d95..059f4ce 100644 --- a/RF24Client.cpp +++ b/RF24Client.cpp @@ -1,17 +1,5 @@ -extern "C" -{ -#import "uip-conf.h" -#import "utility/uip.h" -#import "utility/uip_arp.h" -//#import "string.h" -} - #include "RF24Ethernet.h" -#include "RF24Client.h" -//#include "Dns.h" -#include "RF24Network.h" -#include "RF24Ethernet_config.h" #define UIP_TCP_PHYH_LEN UIP_LLH_LEN+UIP_IPTCPH_LEN @@ -41,16 +29,16 @@ uint8_t RF24Client::connected(){ int RF24Client::connect(IPAddress ip, uint16_t port) { - +#if UIP_ACTIVE_OPEN > 0 stop(); uip_ipaddr_t ipaddr; uip_ip_addr(ipaddr, ip); struct uip_conn* conn = uip_connect(&ipaddr, htons(port)); if (conn) { -#if UIP_CONNECT_TIMEOUT > 0 + #if UIP_CONNECT_TIMEOUT > 0 int32_t timeout = millis() + 1000 * UIP_CONNECT_TIMEOUT; -#endif + #endif while((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) { RF24EthernetClass::tick(); @@ -62,15 +50,17 @@ int RF24Client::connect(IPAddress ip, uint16_t port) return 1; } -#if UIP_CONNECT_TIMEOUT > 0 + #if UIP_CONNECT_TIMEOUT > 0 if (((int32_t)(millis() - timeout)) > 0) { conn->tcpstateflags = UIP_CLOSED; break; } -#endif + #endif } } +#endif //Active open enabled + return 0; } @@ -91,10 +81,15 @@ RF24Client::connect(const char *host, uint16_t port) ret = dns.getHostByName(host, remote_addr); if (ret == 1) { - //Serial.println("got dns"); + #if defined (ETH_DEBUG_L1) || #defined (RF24ETHERNET_DEBUG_DNS) + Serial.println(F("*** got dns ***")); + #endif return connect(remote_addr, port); } #endif + #if defined (ETH_DEBUG_L1) || #defined (RF24ETHERNET_DEBUG_DNS) + Serial.println(F("*** dns fail ***")); + #endif //Serial.println("return"); //Serial.println(ret,DEC); return ret; @@ -109,6 +104,7 @@ void RF24Client::stop() IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("before stop(), with data")); ); data->packets_in[0] = 0; + data->dataCnt = 0; if (data->state & UIP_CLIENT_REMOTECLOSED){ data->state = 0; @@ -173,6 +169,7 @@ size_t RF24Client::_write(uip_userdata_t* u, const uint8_t *buf, size_t size) { #ifdef RF24ETHERNET_DEBUG_CLIENT + Serial.println(); Serial.print(F("UIPClient.write: writePacket(")); Serial.print(u->packets_out[0]); Serial.print(F(") pos: ")); @@ -184,7 +181,8 @@ size_t RF24Client::_write(uip_userdata_t* u, const uint8_t *buf, size_t size) Serial.println(F("'")); #endif - memcpy(&RF24Ethernet.myDataOut,buf,size); + //memcpy(&RF24Ethernet.myDataOut,buf,size); + memcpy(u->myData,buf,size); u->packets_out[0] = 1; u->out_pos=size; @@ -205,6 +203,14 @@ size_t RF24Client::_write(uip_userdata_t* u, const uint8_t *buf, size_t size) /*************************************************************/ +void uip_log( char* msg ){ + //Serial.println(); + //Serial.println("** UIP LOG **"); + //Serial.println(msg); +} + +/*************************************************************/ + void serialip_appcall(void) { @@ -214,36 +220,40 @@ void serialip_appcall(void) /*******Connected**********/ if (!u && uip_connected()){ u->state |= UIP_CONNECTED; - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("UIPClient uip_connected")); ); + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(); Serial.println(F("UIPClient uip_connected")); ); u = (uip_userdata_t*) EthernetClient::_allocateData(); if (u) { uip_conn->appstate = u; IF_RF24ETHERNET_DEBUG_CLIENT( Serial.print(F("UIPClient allocated state: ")); Serial.println(u->state,BIN); ); - } - IF_RF24ETHERNET_DEBUG_CLIENT(Serial.println(F("UIPClient allocation failed")); ); + }else{ + IF_RF24ETHERNET_DEBUG_CLIENT(Serial.println(F("UIPClient allocation failed")); ); + } } /*******User Data RX**********/ if(u){ if (uip_newdata()){ - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.print(F("UIPClient uip_newdata, uip_len:")); Serial.println(uip_len); ); + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println();Serial.print(F("UIPClient uip_newdata, uip_len:")); Serial.println(uip_len); ); if (uip_len && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))){ - uip_stop(); + uip_stop(); + uint16_t dataLen = uip_datalen(); + memcpy(u->myData + u->dataCnt, uip_appdata, dataLen); + u->dataCnt += dataLen; u->connAbortTime = u->restartTime = millis(); u->state &= ~UIP_CLIENT_RESTART; u->windowOpened = false; - RF24Ethernet.dataCnt = uip_datalen(); - memcpy(&RF24Ethernet.myData,uip_appdata,RF24Ethernet.dataCnt); u->packets_in[0] = 1; - } - } + } + goto finish; + //} + } } /*******Closed/Timed-out/Aborted**********/ // If the connection has been closed, save received but unread data. if (uip_closed() || uip_timedout() || uip_aborted()) { - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("UIPClient uip_closed")); ); + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println();Serial.println(F("UIPClient uip_closed")); ); // drop outgoing packets not sent yet: u->packets_out[0] = 0; @@ -262,26 +272,27 @@ void serialip_appcall(void) /*******ACKED**********/ if (uip_acked()) { - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("UIPClient uip_acked")); ); + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(); Serial.println(F("UIPClient uip_acked")); ); u->state &= ~UIP_CLIENT_RESTART; u->windowOpened = false; u->connAbortTime = u->restartTime = millis(); u->packets_out[0] = 0; - u->out_pos=0; + u->out_pos=0; } /*******Polling**********/ - if (uip_poll() ){ - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("UIPClient uip_poll")); ); + if (uip_poll() || uip_rexmit() ){ + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(); Serial.println(F("UIPClient uip_poll")); ); if (u->packets_out[0] != 0 ) { send_len = u->out_pos; if (send_len > 0) { - RF24Ethernet.uip_hdrlen = ((uint8_t*)uip_appdata)-uip_buf; + //RF24Ethernet.uip_hdrlen = ((uint8_t*)uip_appdata)-uip_buf; uip_len = send_len; - uip_send(RF24Ethernet.myDataOut,send_len); - RF24Ethernet.packetstate |= UIPETHERNET_SENDPACKET; + //uip_send(RF24Ethernet.myDataOut,send_len); + uip_send(u->myData,send_len); + //RF24Ethernet.packetstate |= UIPETHERNET_SENDPACKET; } goto finish; @@ -289,7 +300,7 @@ void serialip_appcall(void) // Restart mechanism to keep connections going // Only call this if the TCP window has already been re-opened, the connection is being polled, but no data // has been acked - if( u->windowOpened == true && UIP_CLIENT_RESTART && millis() - u->restartTime > u->restartInterval){ + if( u->windowOpened == true && u->state & UIP_CLIENT_RESTART && millis() - u->restartTime > u->restartInterval){ if( !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))){ u->restartTime = millis(); @@ -297,7 +308,7 @@ void serialip_appcall(void) #if defined UIP_CONNECTION_TIMEOUT if(millis() - u->connAbortTime >= UIP_CONNECTION_TIMEOUT){ #if defined RF24ETHERNET_DEBUG_CLIENT || defined ETH_DEBUG_L1 - Serial.println(F("")); Serial.println(F("*********** ABORTING CONNECTION ***************")); + Serial.println(); Serial.println(F("*********** ABORTING CONNECTION ***************")); #endif u->windowOpened = false; u->state = 0; @@ -308,11 +319,12 @@ void serialip_appcall(void) }else{ #endif #if defined RF24ETHERNET_DEBUG_CLIENT || defined ETH_DEBUG_L1 + Serial.println(); Serial.print(F("UIPClient Re-Open TCP Window, time remaining before abort: ")); Serial.println((UIP_CONNECTION_TIMEOUT - (millis() - u->connAbortTime)) / 1000.00); #endif u->restartInterval+=500; - u->restartInterval=min(u->restartInterval,7000); + u->restartInterval=rf24_min(u->restartInterval,7000); uip_restart(); #if defined UIP_CONNECTION_TIMEOUT } @@ -321,27 +333,11 @@ void serialip_appcall(void) } } - /*******ReXmit**********/ - if( uip_rexmit()){ - IF_ETH_DEBUG_L1( Serial.println(F("UIPClient REXMIT")); Serial.print(F("RTX Timer ")); Serial.println(uip_conn->timer); Serial.print(F("RTX Count ")); Serial.println(uip_conn->nrtx); Serial.print(F("RTX Timeout ")); Serial.println(uip_conn->rto); ); - if (u->packets_out[0] != 0 ) { - send_len = u->out_pos; - - if (send_len > 0) { - RF24Ethernet.uip_hdrlen = ((uint8_t*)uip_appdata)-uip_buf; - uip_len = send_len; - uip_send(RF24Ethernet.myDataOut,send_len); - RF24Ethernet.packetstate |= UIPETHERNET_SENDPACKET; - } - goto finish; - } - } - /*******Close**********/ if (u->state & UIP_CLIENT_CLOSE) { - IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(F("UIPClient state UIP_CLIENT_CLOSE")); ); + IF_RF24ETHERNET_DEBUG_CLIENT( Serial.println(); Serial.println(F("UIPClient state UIP_CLIENT_CLOSE")); ); if (u->packets_out[0] == 0) { u->state = 0; @@ -355,12 +351,13 @@ void serialip_appcall(void) } } -finish_newdata: + finish_newdata: if (u->state & UIP_CLIENT_RESTART && !u->windowOpened) { if( !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))){ uip_restart(); #if defined RF24ETHERNET_DEBUG_CLIENT || defined ETH_DEBUG_L1 - Serial.print(F("UIPClient Re-Open TCP Window")); + Serial.println(); + Serial.println(F("UIPClient Re-Open TCP Window")); #endif u->windowOpened = true; u->restartInterval = UIP_WINDOW_REOPEN_DELAY; //.75 seconds @@ -371,6 +368,11 @@ void serialip_appcall(void) finish:; + //uip_send(RF24Ethernet.myDataOut,send_len); + /*if(send_len){ + uip_send(RF24Ethernet.myData,send_len); + uip_len = send_len; + }*/ } /*******************************************************/ @@ -386,6 +388,7 @@ uip_userdata_t *RF24Client::_allocateData() data->state = sock | UIP_CLIENT_CONNECTED; //memset(&data->packets_in[0],0,sizeof(uip_userdata_t)-sizeof(data->state)); data->packets_in[0]=0; + data->dataCnt = 0; return data; } } @@ -407,7 +410,9 @@ int RF24Client::available() int RF24Client::_available(uip_userdata_t *u) { int len = 0; - len = RF24Ethernet.dataCnt; + if(u->packets_in[0] == 1){ + len = u->dataCnt; + } return len; } @@ -418,12 +423,12 @@ int RF24Client::read(uint8_t *buf, size_t size) if (data->packets_in[0] == 0) { return 0; } - size = min(RF24Ethernet.dataCnt,size); - memcpy(buf,&RF24Ethernet.myData,size); - RF24Ethernet.dataCnt -= size; - memmove(RF24Ethernet.myData,RF24Ethernet.myData+size,RF24Ethernet.dataCnt); + size = rf24_min(data->dataCnt,size); + memcpy(buf,&data->myData,size); + data->dataCnt -= size; + memmove(data->myData,data->myData+size,data->dataCnt); - if(!RF24Ethernet.dataCnt) { + if(!data->dataCnt) { data->packets_in[0] = 0; @@ -466,12 +471,12 @@ int RF24Client::peek() { if (*this) { - /*if (data->packets_in[0] != NOBLOCK) + if (data->packets_in[0] == 1) { uint8_t c; - Enc28J60Network::readPacket(data->packets_in[0],0,&c,1); + memcpy(&c,&data->myData,1); return c; - }*/ + } } return -1; } @@ -482,7 +487,8 @@ void RF24Client::flush() { if (*this) { - //_flushBlocks(&data->packets_in[0]); + data->packets_in[0] = 0; + data->dataCnt = 0; } } diff --git a/RF24Client.h b/RF24Client.h index c2777fd..dbe8874 100644 --- a/RF24Client.h +++ b/RF24Client.h @@ -3,16 +3,9 @@ #ifndef RF24CLIENT_H #define RF24CLIENT_H - -#include "ethernet_comp.h" #include "Print.h" #import "Client.h" -extern "C" { - #import "utility/uip.h" -} - - #define UIP_SOCKET_DATALEN UIP_TCP_MSS //#define UIP_SOCKET_NUMPACKETS UIP_RECEIVE_WINDOW/UIP_TCP_MSS+1 #ifndef UIP_SOCKET_NUMPACKETS @@ -45,7 +38,7 @@ typedef struct { uint8_t state; uint8_t packets_in[UIP_SOCKET_NUMPACKETS]; uint8_t packets_out[UIP_SOCKET_NUMPACKETS]; - uint8_t out_pos; + uint16_t out_pos; #if UIP_CLIENT_TIMER >= 0 unsigned long timer; #endif @@ -53,6 +46,8 @@ typedef struct { uint32_t restartTime; uint32_t restartInterval; uint32_t connAbortTime; + uint8_t myData[OUTPUT_BUFFER_SIZE]; + uint16_t dataCnt = 0; } uip_userdata_t; @@ -74,6 +69,11 @@ class RF24Client : public Client { /** * Establish a connection to a given hostname and port + * @note UDP must be enabled in uip-conf.h for DNS lookups to work + * + * @note Tip: DNS lookups generally require a buffer size of 250-300 bytes or greater. + * Lookups will generally return responses with a single A record if using hostnames like + * "www.google.com" instead of "google.com" which works well with the default buffer size */ int connect(const char *host, uint16_t port); @@ -118,18 +118,18 @@ class RF24Client : public Client { /** * Indicates whether data is available to be read by the client. * Returns the number of bytes available to be read - * @note Calling client or server available() keeps the RF24Network layer running, so needs to be called regularly, + * @note Calling client or server available() keeps the IP stack and RF24Network layer running, so needs to be called regularly, * even when disconnected or delaying for extended periods. */ int available(); /** - * Not working currently + * Read a byte from the incoming buffer without advancing the point of reading */ int peek(); /** - * Not working currently + * Flush all incoming client data from the current connection/buffer */ void flush(); using Print::write; @@ -137,7 +137,7 @@ class RF24Client : public Client { operator bool(); virtual bool operator==(const EthernetClient&); virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; - + static uip_userdata_t all_data[UIP_CONNS]; private: static int handle_connection(uip_tcp_appstate_t *s); @@ -148,7 +148,7 @@ class RF24Client : public Client { static int _available(uip_userdata_t *); - static uip_userdata_t all_data[UIP_CONNS]; + static uip_userdata_t* _allocateData(); static size_t _write(uip_userdata_t *,const uint8_t *buf, size_t size); @@ -157,7 +157,7 @@ class RF24Client : public Client { //friend void uipclient_appcall(void); friend void serialip_appcall(void); - + friend void uip_log(char* msg); }; diff --git a/RF24Ethernet.cpp b/RF24Ethernet.cpp index a06f0b6..0ebbd25 100644 --- a/RF24Ethernet.cpp +++ b/RF24Ethernet.cpp @@ -20,20 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include "RF24Ethernet.h" - -extern "C" { -#include "uip-conf.h" -#include "uip.h" -#include "uip_arp.h" -#include "timer.h" -} - -#include -#include - IPAddress RF24EthernetClass::_dnsServerAddress; //DhcpClass* RF24EthernetClass::_dhcp(NULL); @@ -48,14 +36,19 @@ RF24EthernetClass::RF24EthernetClass(RF24& _radio, RF24Network& _network): radio void RF24EthernetClass::use_device() { - radio.begin(); - RF24_Channel = RF24_Channel ? RF24_Channel : 97; +// Kept for backwards compatibility only } /*******************************************************/ +/*******************************************************/ void RF24EthernetClass::setMac(uint16_t address){ + if(!network.multicastRelay){ // Radio has not been started yet + radio.begin(); + } + + uint8_t mac[6] = {0x52,0x46,0x32,0x34,0x00,0x00}; mac[4] = address; mac[5] = address >> 8; @@ -74,7 +67,9 @@ void RF24EthernetClass::setMac(uint16_t address){ void RF24EthernetClass::setChannel(uint8_t channel){ RF24_Channel = channel; - radio.setChannel(RF24_Channel); + if(network.multicastRelay){ // Radio has not been started yet + radio.setChannel(RF24_Channel); + } } /*******************************************************/ @@ -125,7 +120,9 @@ _dnsServerAddress = dns; #endif uip_init(); + #if defined (RF24_TAP) uip_arp_init(); + #endif } /*******************************************************/ @@ -182,11 +179,13 @@ return _dnsServerAddress; void RF24EthernetClass::tick() { if(RF24Ethernet.network.update() == EXTERNAL_DATA_TYPE){ + RF24Ethernet.lastRadio = millis(); RF24NetworkFrame *frame = RF24Ethernet.network.frag_ptr; uip_len = frame->message_size; memcpy(&uip_buf,frame->message_buffer,frame->message_size); - }else{ - uip_len = 0; + /*Serial.println(); + Serial.print("eth ext "); + Serial.println(uip_len);*/ } #if !defined (RF24_TAP) @@ -271,8 +270,20 @@ void RF24EthernetClass::tick() { boolean RF24EthernetClass::network_send() { RF24NetworkHeader headerOut(00,EXTERNAL_DATA_TYPE); - RF24Ethernet.network.write(headerOut,&uip_buf,uip_len); - RF24Ethernet.packetstate &= ~UIPETHERNET_SENDPACKET; + while(millis() - RF24Ethernet.lastRadio < 2){} + + bool ok = RF24Ethernet.network.write(headerOut,&uip_buf,uip_len); + #if defined ETH_DEBUG_L1 || defined ETH_DEBUG_L2 + if(!ok){ + Serial.println(); Serial.print(millis()); Serial.println(F(" *** RF24Ethernet Network Write Fail ***")); + } + #endif + #if defined ETH_DEBUG_L2 + if(ok){ + Serial.println(); Serial.print(millis()); Serial.println(F(" RF24Ethernet Network Write OK")); + } + #endif + //RF24Ethernet.packetstate &= ~UIPETHERNET_SENDPACKET; } /*******************************************************/ diff --git a/RF24Ethernet.h b/RF24Ethernet.h index 8969c52..79b92f3 100644 --- a/RF24Ethernet.h +++ b/RF24Ethernet.h @@ -1,23 +1,8 @@ /* - RF24Ethernet - Initially based on SerialIP + RF24Ethernet by TMRh20 2014-2015 - SerialIP.h - Arduino implementation of a uIP wrapper class. - Copyright (c) 2010 Adam Nielsen - All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + https://github.com/TMRh20 + */ #ifndef RF24Ethernet_h @@ -29,19 +14,28 @@ * Class declaration for RF24Ethernet */ + #include + +extern "C" { + #import "uip-conf.h" + #import "utility/uip.h" + #include "utility/timer.h" + #include "utility/uip_arp.h" +} +#include "RF24Ethernet_config.h" +#include +#include + -#include #include "ethernet_comp.h" #include "IPAddress.h" #include "RF24Client.h" -#include "RF24Server.h" -#include "RF24Ethernet_config.h" -//#include "RF24Udp.h" -extern "C" { -#include "utility/timer.h" -#include "utility/uip.h" -} +#if UIP_CONF_UDP > 0 +#include "RF24Udp.h" +#include "Dns.h" +#endif + #define UIPETHERNET_FREEPACKET 1 @@ -104,7 +98,9 @@ class RF24EthernetClass {//: public Print { RF24EthernetClass(); /** - * use_device() must be called to enable the radio with basic settings before calling any other functions + * @note Deprecated, maintained for backwards compatibility with old examples + * + * This function is no longer needed, and does nothing */ void use_device(); @@ -148,13 +144,11 @@ class RF24EthernetClass {//: public Print { void setChannel(uint8_t channel); - size_t dataCnt; - /** Indicates whether data is available. */ int available(); - uint8_t packetstate; - uint8_t uip_hdrlen; + static uint8_t packetstate; + static uint8_t uip_hdrlen; /** Returns the local IP address */ @@ -169,8 +163,8 @@ class RF24EthernetClass {//: public Print { */ IPAddress dnsServerIP(); - uint8_t myData[UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN]; - uint8_t myDataOut[OUTPUT_BUFFER_SIZE]; + //uint8_t myData[OUTPUT_BUFFER_SIZE]; + uint32_t lastRadio; private: RF24& radio; @@ -193,7 +187,7 @@ class RF24EthernetClass {//: public Print { void uip_callback(); //friend void serialip_appcall(void); - friend void uipudp_appcall(void); + //friend void uipudp_appcall(void); friend class RF24Server; friend class RF24Client; @@ -286,14 +280,21 @@ extern RF24EthernetClass RF24Ethernet; * * This provides a fairly seamless interaction, since users only need to configure standard IP forwarding and firewall rules as desired. * - * @section News News + * @section News Update News * - * Jan 4 2015 + * \version 1.2b - Jan 16 2015 + * - Add UDP support + * - Add DNS support + * - Add support for up to 512 byte buffer size + * - Reduce used memory and program space + * - Support for multiple connections with per-connection memory buffers + * + * \version 1.1b - Jan 4 2015 * - Add connection timeout to recover from hangs during failed client downloads * - Better TCP window management to prevent hangs during client downloads * - Stability improvements * - * Dec 2014 + * \version 1.0b - Dec 2014 * - Outgoing client data corruption should be fixed * * @section Config Configuration and Setup diff --git a/RF24Ethernet_config.h b/RF24Ethernet_config.h index c098cf9..dfa027a 100644 --- a/RF24Ethernet_config.h +++ b/RF24Ethernet_config.h @@ -5,39 +5,55 @@ @{ */ -/** Uncomment #define RF24ETHERNET_DEBUG_CLIENT to enable main debugging output - * - * Uncomment #define ETH_DEBUG_L1 for debugging window reopening & retransmissions - * - * Uncomment #define ETH_DEBUG_L2 for extra client state debugging - * - * - */ - - /*********** USER DEBUG CONFIGURATION *********/ +/*********** USER DEBUG CONFIGURATION *********/ //#define RF24ETHERNET_DEBUG_CLIENT //#define ETH_DEBUG_L1 //#define ETH_DEBUG_L2 +//#define RF24ETHERNET_DEBUG_UDP +//#define RF24ETHERNET_DEBUG_DNS /****************************************/ // Note: See uip-conf.h for general user configuration options - +/** + * Uncomment #define RF24ETHERNET_DEBUG_CLIENT to enable main debugging output + */ #if defined (RF24ETHERNET_DEBUG_CLIENT) #define IF_RF24ETHERNET_DEBUG_CLIENT(x) ({x;}) #else #define IF_RF24ETHERNET_DEBUG_CLIENT(x) #endif - + /** + * Uncomment #define ETH_DEBUG_L1 for debugging window reopening & retransmissions + */ #if defined (ETH_DEBUG_L1) #define IF_ETH_DEBUG_L1(x) ({x;}) #else #define IF_ETH_DEBUG_L1(x) #endif - + /** + * Uncomment #define ETH_DEBUG_L2 for extra client state debugging + */ #if defined (ETH_DEBUG_L2) #define IF_ETH_DEBUG_L2(x) ({x;}) #else #define IF_ETH_DEBUG_L2(x) #endif + /** + * Uncomment #define RF24ETHERNET_DEBUG_UDP for UDP debugging + */ + #if defined (RF24ETHERNET_DEBUG_UDP) + #define IF_RF24ETHERNET_DEBUG_UDP(x) ({x;}) + #else + #define IF_RF24ETHERNET_DEBUG_UDP(x) + #endif + /** + * Uncomment #define RF24ETHERNET_DEBUG_DNS for DNS debugging + */ + #if defined (RF24ETHERNET_DEBUG_DNS) + #define IF_RF24ETHERNET_DEBUG_DNS(x) ({x;}) + #else + #define IF_RF24ETHERNET_DEBUG_DNS(x) + #endif + /* @} */ diff --git a/RF24Server.cpp b/RF24Server.cpp index 7100bcb..c9876dd 100644 --- a/RF24Server.cpp +++ b/RF24Server.cpp @@ -20,7 +20,7 @@ #include "RF24Server.h" extern "C" { - #include "uip-conf.h" + //#include "uip-conf.h" } RF24Server::RF24Server(uint16_t port) : _port(htons(port)) diff --git a/RF24Udp.cpp b/RF24Udp.cpp new file mode 100644 index 0000000..df6eb22 --- /dev/null +++ b/RF24Udp.cpp @@ -0,0 +1,430 @@ +/* + RF24UDP.cpp - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +#include "RF24Ethernet.h" + +#if UIP_CONF_UDP > 0 + +#ifdef RF24ETHERNET_DEBUG_UDP +#include "HardwareSerial.h" +#endif + + +#if UIP_UDP +#define UIP_ARPHDRSIZE 42 +#define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) + +// Constructor +RF24UDP::RF24UDP() : + _uip_udp_conn(NULL) +{ + memset(&appdata,0,sizeof(appdata)); +} + +// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use +uint8_t +RF24UDP::begin(uint16_t port) +{ + if (!_uip_udp_conn) + { + _uip_udp_conn = uip_udp_new(NULL, 0); + } + if (_uip_udp_conn) + { + uip_udp_bind(_uip_udp_conn,htons(port)); + _uip_udp_conn->appstate = &appdata; + return 1; + } + return 0; +} + +// Finish with the UDP socket +void +RF24UDP::stop() +{ + if (_uip_udp_conn) + { + uip_udp_remove(_uip_udp_conn); + _uip_udp_conn->appstate = NULL; + _uip_udp_conn=NULL; + //Enc28J60Network::freeBlock(appdata.packet_in); + //Enc28J60Network::freeBlock(appdata.packet_next); + //Enc28J60Network::freeBlock(appdata.packet_out); + appdata.packet_in = 0; + appdata.packet_next = 0; + appdata.packet_out = 0; + + memset(&appdata,0,sizeof(appdata)); + } +} + +// 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 +RF24UDP::beginPacket(IPAddress ip, uint16_t port) +{ + RF24EthernetClass::tick(); + if (ip && port) + { + uip_ipaddr_t ripaddr; + uip_ip_addr(&ripaddr, ip); +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.print(F("RF24UDP udp beginPacket, ")); +#endif + if (_uip_udp_conn) + { + _uip_udp_conn->rport = htons(port); + uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr); + } + else + { + _uip_udp_conn = uip_udp_new(&ripaddr,htons(port)); + if (_uip_udp_conn) + { +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.print(F("RF24UDP New connection, ")); +#endif + _uip_udp_conn->appstate = &appdata; + } + else + { +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.println(F("RF24UDP Failed to allocate new connection")); +#endif + return 0; + } + } +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.print(F("rip: ")); + Serial.print(ip); + Serial.print(F(", port: ")); + Serial.println(port); +#endif + } + if (_uip_udp_conn) + { + if (appdata.packet_out == 0) + { + appdata.packet_out = 1; + appdata.out_pos = 0;//UIP_UDP_PHYH_LEN; + if (appdata.packet_out != 0) + return 1; +#ifdef RF24ETHERNET_DEBUG_UDP + else + Serial.println(F("RF24UDP Failed to allocate memory for packet")); +#endif + } +#ifdef RF24ETHERNET_DEBUG_UDP + else + Serial.println(F("RF24UDP Previous packet on that connection not sent yet")); +#endif + } + return 0; +} + +// Start building up a packet to send to the remote host specific in host and port +// Returns 1 if successful, 0 if there was a problem resolving the hostname or port +int +RF24UDP::beginPacket(const char *host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(RF24Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return beginPacket(remote_addr, port); + } else { + return ret; + } +} + +// Finish off this packet and send it +// Returns 1 if the packet was sent successfully, 0 if there was an error +int +RF24UDP::endPacket() +{ + if (_uip_udp_conn && appdata.packet_out != 0) + { + appdata.send = true; + //Enc28J60Network::resizeBlock(appdata.packet_out,0,appdata.out_pos); + IF_RF24ETHERNET_DEBUG_UDP( Serial.println(F("RF24UDP endpacket")); ); + uip_udp_periodic_conn(_uip_udp_conn); + if (uip_len > 0) + { + _send(&appdata); + return 1; + } + } + return 0; +} + +// Write a single byte into the packet +size_t +RF24UDP::write(uint8_t c) +{ + return write(&c,1); +} + +// Write size bytes from buffer into the packet +size_t +RF24UDP::write(const uint8_t *buffer, size_t size) +{ + if (appdata.packet_out != 0) + { + + IF_RF24ETHERNET_DEBUG_UDP( Serial.println("RF24UDP Write: "); Serial.println(size); for(int i=0; i 0) + { + return c; + } + return -1; +} + +// 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 +int +RF24UDP::read(unsigned char* buffer, size_t len) +{ + RF24EthernetClass::tick(); + + //Serial.print("RF24 UDP read "); + if (appdata.packet_in != 0) + { + //memaddress read = Enc28J60Network::readPacket(appdata.packet_in,0,buffer,len); + memcpy(buffer,RF24Client::all_data[0].myData + appdata.in_pos,len); + appdata.in_pos += len; + appdata.packet_in_size -= len; + + if (appdata.packet_in_size < 1) + { + //Enc28J60Network::freeBlock(appdata.packet_in); + appdata.packet_in = 0; + } + //else + //Enc28J60Network::resizeBlock(appdata.packet_in,read); + return len; + } + return 0; +} + +// Return the next byte from the current packet without moving on to the next byte +int +RF24UDP::peek() +{ + RF24EthernetClass::tick(); + if (appdata.packet_in != 0) + { + unsigned char c; + //if (Enc28J60Network::readPacket(appdata.packet_in,0,&c,1) == 1) + // return c; + } + return -1; +} + +// Finish reading the current packet +void +RF24UDP::flush() +{ + RF24EthernetClass::tick(); + //Enc28J60Network::freeBlock(appdata.packet_in); + appdata.packet_in = 0; + appdata.packet_in_size = 0; +} + +// Return the IP address of the host who sent the current incoming packet +IPAddress +RF24UDP::remoteIP() +{ + return _uip_udp_conn ? ip_addr_uip(_uip_udp_conn->ripaddr) : IPAddress(); +} + +// Return the port of the host who sent the current incoming packet +uint16_t +RF24UDP::remotePort() +{ + return _uip_udp_conn ? ntohs(_uip_udp_conn->rport) : 0; +} + +// uIP callback function + +void +uipudp_appcall(void) { + if (uip_udp_userdata_t *data = (uip_udp_userdata_t *)(uip_udp_conn->appstate)) + { + if (uip_newdata()) + { + if (data->packet_next == 0) + { + uip_udp_conn->rport = UDPBUF->srcport; + uip_ipaddr_copy(uip_udp_conn->ripaddr,UDPBUF->srcipaddr); + //data->packet_next = Enc28J60Network::allocBlock(ntohs(UDPBUF->udplen)-UIP_UDPH_LEN); + //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery + //if (data->packet_next != 0) + //{ + //discard Linklevel and IP and udp-header and any trailing bytes: + //Enc28J60Network::copyPacket(data->packet_next,0,RF24EthernetClass::in_packet,UIP_UDP_PHYH_LEN,Enc28J60Network::blockSize(data->packet_next)); + //memcpy(&RF24Ethernet.myData,uip_appdata,uip_len); + memcpy(RF24Client::all_data[0].myData,uip_appdata ,uip_len); + //memcpy(&RF24Ethernet.myData,uip_buf + (UIP_LLH_LEN - UIP_TCPIP_HLEN), ntohs(UDPBUF->udplen)-UIP_UDPH_LEN); + //data->packet_in_size += ntohs(UDPBUF->udplen)-UIP_UDPH_LEN; + data->packet_in_size += uip_len; + data->packet_in = 1; + #ifdef RF24ETHERNET_DEBUG_UDP + Serial.print(F("RF24UDP udp, uip_newdata received packet: ")); + Serial.print(data->packet_next); + Serial.print(F(", size: ")); + Serial.println(data->packet_in_size); + + for(int i=0; ipacket_in_size; i++){ + Serial.print(RF24Client::all_data[0].myData[i],HEX); + Serial.print(F" : ")); + } + Serial.println(); + //Serial.println(Enc28J60Network::blockSize(data->packet_next)); + #endif + //} + } + } + if (uip_poll() && data->send) + { + //set uip_slen (uip private) by calling uip_udp_send +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.print(F("udp, uip_poll preparing packet to send: ")); + Serial.print(data->packet_out); + Serial.print(F(", size: ")); + Serial.println(data->out_pos); + //Serial.println(Enc28J60Network::blockSize(data->packet_out)); +#endif + // RF24EthernetClass::uip_packet = data->packet_out; + //RF24EthernetClass::uip_hdrlen = UIP_UDP_PHYH_LEN; + //memcpy(uip_appdata,RF24Ethernet.myDataOut,data->out_pos); + memcpy(uip_appdata,RF24Client::all_data[0].myData,data->out_pos); + //memcpy( + uip_udp_send(data->out_pos ); + } + } +} + +void +RF24UDP::_send(uip_udp_userdata_t *data) { + #if defined (RF24_TAP) + uip_arp_out(); //add arp + #endif + if (uip_len == UIP_ARPHDRSIZE) + { + //RF24EthernetClass::uip_packet = 0; + //RF24EthernetClass::packetstate &= ~UIPETHERNET_SENDPACKET; +#ifdef RF24ETHERNET_DEBUG_UDP + Serial.println(F("udp, uip_poll results in ARP-packet")); +#endif + RF24EthernetClass::network_send(); + } + else + //arp found ethaddr for ip (otherwise packet is replaced by arp-request) + { + data->send = false; + data->packet_out = 0; + //RF24EthernetClass::packetstate |= UIPETHERNET_SENDPACKET; +#ifdef RF24ETHERNET_DEBUG_UDP +Serial.println(data->out_pos); + Serial.print(F("udp, uip_packet to send: ")); + //Serial.println(RF24EthernetClass::uip_packet); + + + for(int i=0; iout_pos; i++){ + //Serial.print((char)RF24Ethernet.myDataOut[i]); + Serial.print((char)RF24Client::all_data[0].myData[i]); + } + Serial.println(""); +#endif + //memcpy(uip_appdata,RF24Ethernet.udpDataOut,data->out_pos); + RF24NetworkHeader headerOut(00,EXTERNAL_DATA_TYPE); + RF24Ethernet.network.write(headerOut,uip_buf,data->out_pos+UIP_UDP_PHYH_LEN); + } + //RF24EthernetClass::network_send(); + +} +#endif +#endif \ No newline at end of file diff --git a/RF24Udp.h b/RF24Udp.h new file mode 100644 index 0000000..61e48ad --- /dev/null +++ b/RF24Udp.h @@ -0,0 +1,129 @@ +/* + UIPUdp.h - Arduino implementation of a uIP wrapper class. + Copyright (c) 2013 Norbert Truchsess + All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef UIPUDP_H +#define UIPUDP_H + +#include "RF24Ethernet.h" + +#if UIP_CONF_UDP > 0 + +#include + +#define UIP_UDP_MAXDATALEN 1500 +#define UIP_UDP_PHYH_LEN UIP_LLH_LEN+UIP_IPUDPH_LEN +#define UIP_UDP_MAXPACKETSIZE UIP_UDP_MAXDATALEN+UIP_UDP_PHYH_LEN + +typedef struct { + uint16_t out_pos; + uint8_t packet_next; + uint8_t packet_in; + uint8_t packet_out; + int packet_in_size; + uint16_t in_pos; + boolean send; +} uip_udp_userdata_t; + +class RF24UDP : public UDP +{ + +private: + struct uip_udp_conn *_uip_udp_conn; + + uip_udp_userdata_t appdata; + +public: + RF24UDP(); // 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 + void + stop(); // Finish with the UDP socket + + // 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); + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + int + beginPacket(const char *host, 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 + size_t + write(uint8_t); + // Write size bytes from buffer into the packet + size_t + write(const uint8_t *buffer, size_t size); + + using Print::write; + + // 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 + int + available(); + // Read a single byte from the current packet + 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 + int + read(unsigned char* buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + int + read(char* buffer, size_t len) + { + return read((unsigned char*) buffer, len); + } + ; + // Return the next byte from the current packet without moving on to the next byte + int + peek(); + void + flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + IPAddress + remoteIP(); + + // Return the port of the host who sent the current incoming packet + uint16_t + remotePort(); + +private: + + friend void uipudp_appcall(void); + + friend class RF24EthernetClass; + friend class RF24Client; + static void _send(uip_udp_userdata_t *data); + +}; + +#endif + +#endif // UDP Enabled diff --git a/uip-conf.h b/uip-conf.h index c647ab8..fb22588 100644 --- a/uip-conf.h +++ b/uip-conf.h @@ -46,6 +46,7 @@ #include +#include "RF24Network_config.h" /************* TMRh20: User Configuration *******************/ /** @defgroup UipConfiguration @@ -61,10 +62,11 @@ User Configuration Options /** * uIP buffer size. - * @warning The buffer size MUST be less than or equal to the MAX_PAYLOAD_SIZE setting in RF24Network_conf.h. + * @note For simplicity, this is automatically set to the MAX_PAYLOAD_SIZE configured in the RF24Network_conf.h file, but can be configured independently + * of RF24Network if desired. * * Notes: - * 1. Nodes can use different buffer sizes, TCP communication is limited to the smallest + * 1. Nodes can use different buffer sizes, direct TCP communication is limited to the smallest * ie: A RPi can be configured to use 1500byte TCP windows, with Arduino nodes using only 120byte TCP windows. * 2. Routing nodes handle traffic at the link-layer, so the MAX_PAYLOAD_SIZE is not important, unless they are * running RF24Ethernet. @@ -73,22 +75,12 @@ User Configuration Options * 4. The user buffers are automatically configured to (Buffer Size - Link Layer Header Size - TCP Header Size) so * using RF24Mesh will decrease payloads by 14 bytes. * 5. Larger buffer sizes increase throughput for individual nodes, but can impact other network traffic. + * 6. Max usable value is 512 */ -#define UIP_CONF_BUFFER_SIZE 120 +#define UIP_CONF_BUFFER_SIZE MAX_PAYLOAD_SIZE - /** - * - * Adjust the rate at which the IP stack performs periodic processing. - * The periodic timer will be called at a rate of 1 second divided by this value - * - * Increase this value to reduce response times and increase throughput during user interactions. - * @note: Increasing this value will increase throughput for individual nodes, but can impact other network traffic. - */ -#define UIP_TIMER_DIVISOR 5 - - - /** +/** * Optional: Uncomment to disable * * Adjust the length of time after which an open connection will be timed out. @@ -97,19 +89,6 @@ User Configuration Options */ #define UIP_CONNECTION_TIMEOUT 20000 - /** - * Optional: Used with UIP_CONNECTION_TIMEOUT - * - * If an open connection times out, the connection will be restarted. - * - * Adjust the initial delay period before restarting a connection that has already been restarted - * - * For small payloads (96-120 bytes) with a fast connection, this value can be as low as ~750ms or so. - * When increasing the uip buffer size, this value should be increased, or - * the window may be reopened while the requested data is still being received, hindering traffic flow. - */ -#define UIP_WINDOW_REOPEN_DELAY 750 - /** * SLIP/TUN - 14 for Ethernet/TAP & 0 for TUN/SLIP * @@ -119,7 +98,45 @@ User Configuration Options */ #define UIP_CONF_LLH_LEN 14 +/** + * UDP support on or off (required for DNS) + * @note DNS support typically requires larger payload sizes (250-300). It seems that DNS servers will typically respond + * with a single address if requesting an address of www.google.com vs google.com, and this will work with the default payload size + */ + +#define UIP_CONF_UDP 0 +//#define UIP_CONF_BROADCAST 0 +//#define UIP_CONF_UDP_CONNS 1 + + /***@}*/ + /** + * @name Advanced Operation + * + * For advanced configuration of RF24Ethernet + */ + /***@{*/ + + /** + * + * Adjust the rate at which the IP stack performs periodic processing. + * The periodic timer will be called at a rate of 1 second divided by this value + * + * Increase this value to reduce response times and increase throughput during user interactions. + * @note: Increasing this value will increase throughput for individual nodes, but can impact other network traffic. + */ +#define UIP_TIMER_DIVISOR 4 + +/** + * If operating solely as a server, disable the ability to open TCP connections as a client by setting to 0 + * Saves memory and program space. + */ + +#define UIP_CONF_ACTIVE_OPEN 1 +/** + * UDP checksums on or off + */ +#define UIP_CONF_UDP_CHECKSUMS 0 /** * uIP User Output buffer size @@ -128,9 +145,28 @@ User Configuration Options * length of strings that can be sent by the user, and depends on the uip buffer size * * Must be <= UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN -* +* @note Must be an odd number or the TCP/IP sequence gets out of order with payloads larger than 511 bytes +* I think this might be a bug or missing feature of the uip stack */ -#define OUTPUT_BUFFER_SIZE UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN +#if UIP_CONF_BUFFER_SIZE >= 512 + #define OUTPUT_BUFFER_SIZE 511 +#else + #define OUTPUT_BUFFER_SIZE UIP_CONF_BUFFER_SIZE - UIP_CONF_LLH_LEN - UIP_TCPIP_HLEN +#endif + + /** + * Optional: Used with UIP_CONNECTION_TIMEOUT + * + * If an open connection is not receiving data, the connection will be restarted. + * + * Adjust the initial delay period before restarting a connection that has already been restarted + * + * For small payloads (96-120 bytes) with a fast connection, this value can be as low as ~750ms or so. + * When increasing the uip buffer size, this value should be increased, or + * the window may be reopened while the requested data is still being received, hindering traffic flow. + */ +#define UIP_WINDOW_REOPEN_DELAY 750 + /* @} */ /** @} */ /******************** END USER CONFIG ***********************************/ @@ -139,15 +175,24 @@ User Configuration Options /* for TCP */ #define UIP_SOCKET_NUMPACKETS 1 - /** - * The TCP maximum segment size. + * The TCP receive window. * * This is should not be to set to more than * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. + * @note Must be an odd number or the TCP/IP sequence gets out of order with payloads larger than 511 bytes + * I think this might be a bug or missing feature of the uip stack */ -//#define UIP_CONF_RECEIVE_WINDOW 60 //This is set automatically to the max allowable size -/** + +#if UIP_CONF_BUFFER_SIZE >= 512 + #define UIP_CONF_RECEIVE_WINDOW 511 +#else + #define UIP_CONF_RECEIVE_WINDOW UIP_CONF_BUFFER_SIZE - UIP_CONF_LLH_LEN - UIP_TCPIP_HLEN //This is set automatically to the max allowable size +#endif + +#define UIP_CONF_TCP_MSS UIP_CONF_RECEIVE_WINDOW + + /** * CPU byte order. * * \hideinitializer @@ -162,21 +207,6 @@ User Configuration Options //#define UIP_CONF_LOGGING 1 #define UIP_CONF_LOGGING 0 -/** - * UDP support on or off - * - * \hideinitializer - */ -#define UIP_CONF_UDP 0 -//#define UIP_CONF_BROADCAST 0 -//#define UIP_CONF_UDP_CONNS 1 -/** - * UDP checksums on or off - * - * \hideinitializer - */ -#define UIP_CONF_UDP_CHECKSUMS 0 - /** * uIP statistics on or off * @@ -243,6 +273,9 @@ void uipudp_appcall(void); /** \hideinitializer */ #define UIP_UDP_APPCALL uipudp_appcall +#if UIP_CONF_LOGGING > 0 +void uip_log(char* msg); +#endif #endif /* __UIP_CONF_H__ */ diff --git a/utility/uipopt.h b/utility/uipopt.h index a03234d..3b7844f 100644 --- a/utility/uipopt.h +++ b/utility/uipopt.h @@ -230,7 +230,12 @@ * * \hideinitializer */ + +#ifndef UIP_CONF_ACTIVE_OPEN #define UIP_ACTIVE_OPEN 1 +#else +#define UIP_ACTIVE_OPEN UIP_CONF_ACTIVE_OPEN +#endif /** * The maximum number of simultaneously open TCP connections. @@ -285,6 +290,7 @@ #else #define UIP_RTO UIP_CONF_RTO #endif + /** * The maximum number of times a segment should be retransmitted * before the connection should be aborted. @@ -292,7 +298,7 @@ * This should not be changed. * TMRh20: Lower RTX value for shorter timeouts */ -#define UIP_MAXRTX 5 //8 +#define UIP_MAXRTX 6 //8 /** * The maximum number of times a SYN segment should be retransmitted @@ -442,7 +448,7 @@ * This function must be implemented by the module that uses uIP, and * is called by uIP whenever a log message is generated. */ -void uip_log(char *msg); +//void uip_log(char *msg); /** * The link level header length.