Commits

bjoern committed a5ebcc1

cleaned up comments, added WString lib, wrote detailed README

  • Participants
  • Parent commits 7c35b11

Comments (0)

Files changed (13)

-arduino_osc_serial.pde: send and receive OSC messages over the Arduino's Serial port. 
+This package contains two Firmwares (arduino_osc_serial and arduino_osc_udp) that enable sending and receiving of OSC messages on Arduino using either the serial port or UDP with the Arduino Ethernet shield. The protocol is described in the header of each file.
 
-arduino_osc_udp.pde: send & receive OSC messages over UDP using the Arduino Ethernet Shield.
-To compile this file, copy the Udp library in /libraries/Ethernet/ into your arduino/hardware/libraries/Ethernet directory.
+To enable UDP communication, this package also contains additions to Arduino's Ethernet library that enable sending and receiving of UDP packets (as of Arduino0012, the library only supports TCP natively).
 
+There are three different UDP libraries included:
+
+UdpBytewise mimics byte-by-byte reading and writing of packets as used in the Serial and Wire libraries of Arduino. This library has a small footprint and offers continuity with existing programming models, but it has some limitations as packet boundaries are ignored by the read function, and packets may have to get split by the write function.
+
+UdpString uses Tom Igoe/Hernando Barragan's WString library for packets. This library offer high-level abstractions that preserve packet boundaries. However, it is currently rather large and does not leave much space for the user's sketch. The package contains a redistribution of WString for this library.
+
+UdpRaw is a thin wrapper around the UDP functions offered by the socket.c file in the Ethernet library - it uses raw byte buffers to send/receive packets. This library has the smallest footprint but is most challenging to use for novices.
+
+Examples for sending/receiving packets for each of these three libraries are included in /libraries/Ethernet/examples/.
+
+Finally, there are examples to test arduino_osc communication in PureData and Python. These examples may need additional modules/extensions to run on a fresh system. Consult the comments in the files themselves.
+
+===============================================================================
+Files:
+arduino_osc_serial.pde - Firmware for sending and receiving OSC packages over the Arduino serial port
+
+arduino_osc_udp.pde - Firmware for sending and receiving OSC packages over UDP using the Arduino ethernet shield. Relies on UDP library in files /libraries/Ethernet/UdpRaw.h/.cpp
+
+./examples/pd: - PureData (pd) example patches to test arduino_osc firmwares
+arduino_osc.pd - Patch that communicates with arduino_osc_serial.pde firmware
+arduino_osc_udp.pd - Patch that communicates with arduino_osc_udp.pde firmware
+arduino_osc_udp_widgets.pd - Subpatch containing UI widgets for arduino_osc_udp.pd. Don't open.
+arduino_osc_widgets.pd - Subpatch containing UI widgets for arduino_osc.pd. Don't open.
+
+./examples/python: - Python scripts to test arduino_osc firmwares
+test_udp_latency.py - Run repeated message echoing loops to test roundtrip latency
+test_udp_throughput.py - Receive analog sensor data from Arduino to test throughput
+
+./libraries/Ethernet: - Augmentations to Arduino's Ethernet library that enable UDP communication
+UdpBytewise.cpp - Library for reading/writing UDP packets byte-by-byte (like Serial) 
+UdpBytewise.h
+UdpRaw.cpp - Library for reading/writing UDP packets using char[] buffers
+UdpRaw.h
+UdpString.cpp - Library for reading/writing UDP packets using Tom Igoe/Hernando Barragan's WString objects
+UdpString.h
+
+
+./libraries/Ethernet/examples: - Arduino firmware examples to send/receive UDP packets for each of the three library approaches
+UdpReceiveBytewise
+UdpReceiveRaw
+UdpReceiveString
+UdpSendBytewise
+UdpSendRaw
+UdpSendString
+
+./libraries/String: re-distribution of Igoe/Barragan's WString library needed by UdpString.h/.cpp
+WString.cpp
+WString.h
+===============================================================================
+bjoern@cs.stanford.edu 12/30/2008
+

libraries/Ethernet/UdpBytewise.cpp

 /*
- *  Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ * UdpBytewise.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
+ * Drop UdpBytewise.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ * TODO: should protect buffer access with critical sections
  *
  * MIT License:
  * Copyright (c) 2008 Bjoern Hartmann
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 extern "C" {
 	if(_rxSize==0 || _rxSize-_rxIndex==0) { 
 		//if local buffer is empty or depleted
 		//check wiz5100 buffer for new packet
-		_rxSize = getSn_RX_RSR(_sock); //this size is inflated by 8 byte header
+		_rxSize = getSn_RX_RSR(_sock); //note: return value is inflated by 8 byte header
 		if(_rxSize){
 			//if we have a new packet there
-			//copy it into our local buffer
+			//reset buffer index
 			_rxIndex=0;
+			//copy packet into our local buffer
 			_rxSize = recvfrom(_sock,_rxBuffer,_rxSize-8,_rxIp,&_rxPort);
 		} else {
 			//else do nothing and rxsize is still 0
 		//if buffer is not empty, return remaining # of bytes
 		return (_rxSize-_rxIndex);
 	}
-	
 }
 
 
-
-int UdpBytewiseClass::beginPacket(uint8_t *ip, unsigned int port) { // returns 1 on success, 0 if we already started a packet
+/* Start a new packet with given target ip and port 
+ * returns 1 on success, 0 if we already started a packet */
+int UdpBytewiseClass::beginPacket(uint8_t *ip, unsigned int port) {	
 	if(_txIndex==0) {
+		//ok to start new packet - copy ip and port
 		_txIp[0]=ip[0];
 		_txIp[1]=ip[1];
 		_txIp[2]=ip[2];
 		return 1;
 	}
 	else {
-		//we already started a packet
+		//we already started a packet and have data in it
 		return 0;
 	}
 }
 
 
-
-// TODO: how do we indicate that we can't add to full buffer?
-// or do we just send a full packet and start the next?
-void UdpBytewiseClass::write(uint8_t b) {// add a byte to the currently assembled packet if there is space
+/* Add a byte to the currently assembled packet if there is space
+ * TODO: how do we indicate that we can't add to full buffer?
+ */
+void UdpBytewiseClass::write(uint8_t b) {
 	if(_txIndex>= UDP_TX_PACKET_MAX_SIZE)
 		return;		
 	_txBuffer[_txIndex++] = b;
 }
 
-int UdpBytewiseClass::endPacket(){ // returns # of bytes sent on success, 0 if there's nothing to send
+/* send an assembled packet out 
+ * returns # of bytes sent on success, 0 if there's nothing to send */
+int UdpBytewiseClass::endPacket() {
 	// send the packet
 	uint16_t result = sendto(_sock,(const uint8_t *)_txBuffer,_txIndex,_txIp,_txPort);
 	// reset buffer index
 	return (int)result;
 }
 
+/* read the next byte of the last rececived packet */
 int UdpBytewiseClass::read() {
-	if(_rxIndex!=_rxSize) {
-		//if there is something to be read
-		//return the next byte
-		return _rxBuffer[_rxIndex++];
-		
+	if(_rxIndex < _rxSize) {
+		// if there is something to be read, return the next byte
+		return _rxBuffer[_rxIndex++];		
 	} else {
-		//we already sent the last byte
-		//reset our buffer
-		_rxIndex=0;
-		_rxSize=0;
+		//we already sent the last byte - nothing to do
 		return -1;
 	}
 }

libraries/Ethernet/UdpBytewise.h

 /*
- *  Udp.h: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ * UdpBytewise.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
+ * Drop UdpBytewise.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet
  *
  * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
  * 1) UDP does not guarantee the order in which assembled UDP packets are received. This
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 #ifndef UdpBytewise_h
 	
 public:
 	void begin(uint16_t);				// initialize, start listening on specified port
-	int available();								// has data been received?
+	int available();				    // has data been received?
 
 	// Single byte-oriented functions:
 	int beginPacket(uint8_t *ip, unsigned int port); // returns 1 on success, 0 if we already started a packet
 	virtual void write(uint8_t); // add a byte to the currently assembled packet (if there's space)
 	int endPacket(); // returns # of bytes sent on success, 0 if there's nothing to send
 	
-	int read(); //read a byte if available - returns -1 if end of packet
-	void getSenderIp(uint8_t*ip);
-	unsigned int getSenderPort();
+	int read(); //read a byte if available - returns -1 if no data available
+	
+	void getSenderIp(uint8_t * ip);  //get remote IP of the packet we're currently reading from
+	unsigned int getSenderPort();  //get remote port# of the packet we're currently reading from
+	
 };
 
 extern UdpBytewiseClass UdpBytewise;

libraries/Ethernet/UdpRaw.cpp

 /*
- *  Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ *  UdpRaw.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
+ *  This version only offers minimal wrapping of socket.c/socket.h
+ *  Drop UdpRaw.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
  *
  * MIT License:
  * Copyright (c) 2008 Bjoern Hartmann
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 extern "C" {

libraries/Ethernet/UdpRaw.h

 /*
- *  Udp.h: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ *  UdpRaw.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
+ *  This version only offers minimal wrapping of socket.c/socket.h
+ *  Drop UdpRaw.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
  *
  * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
  * 1) UDP does not guarantee the order in which assembled UDP packets are received. This
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 #ifndef UdpRaw_h

libraries/Ethernet/UdpString.cpp

 /*
- *  Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ * UdpString.cpp: Library to send/receive UDP packets with the Arduino ethernet shield
+ * using Tom Igoe/Hernando Barragan's WString library.
+ * Drop UdpString.h/.cpp from this distribution into the Ethernet library directory at 
+ * hardware/libraries/Ethernet/ 
+ * Then copy directory /libraries/String from this distribution to /hardware/libraries/String in
+ * your Arduino installation.
  *
  * MIT License:
  * Copyright (c) 2008 Bjoern Hartmann
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 extern "C" {

libraries/Ethernet/UdpString.h

 /*
- *  Udp.h: Library to send/receive UDP packets with the Arduino ethernet shield.
- *  Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 
+ * UdpString.h: Library to send/receive UDP packets with the Arduino ethernet shield
+ * using Tom Igoe/Hernando Barragan's WString library.
+ * Drop UdpString.h/.cpp from this distribution into the Ethernet library directory at 
+ * hardware/libraries/Ethernet/ 
+ * Then copy directory /libraries/String from this distribution to /hardware/libraries/String in
+ * your Arduino installation.
  *
  * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
  * 1) UDP does not guarantee the order in which assembled UDP packets are received. This
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  *
- * bjoern@cs.stanford.edu 12/29/2008
+ * bjoern@cs.stanford.edu 12/30/2008
  */
 
 #ifndef UdpString_h

libraries/Ethernet/examples/UdpReceiveBytewise/UdpReceiveBytewise.pde

 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC address to use
 byte ip[] = { 192, 168, 11, 200 }; // Arduino's IP address
 byte gw[] = { 192, 168, 11, 1 };   // Gateway IP address
-int localPort = 8888; // local port to listen on
+unsigned int localPort = 8888; // local port to listen on
 
 #define MAX_SIZE 32 // maximum packet size
 byte packetBuffer[MAX_SIZE]; //buffer to hold incoming packet
 int packetSize; // holds received packet size
-byte remoteIp[4]; // holds recvieved packet's originating IP
+byte remoteIp[4]; // holds recieved packet's originating IP
 unsigned int remotePort; // holds received packet's originating port
 
 int i;
 void setup() {
   Ethernet.begin(mac,ip,gw);
   UdpBytewise.begin(localPort);
-  Serial.begin(9600); 
+  Serial.begin(38400); 
 }
 /* LOOP: wait for incoming packets and print each packet to the serial port */
 void loop() {  
-  
   // if there's data available, read a packet
   if(packetSize = UdpBytewise.available()) {
     Serial.print("Received packet of size ");
     
     UdpBytewise.getSenderIp(remoteIp);
     Serial.print("From IP ");
-    for(i=0; i<3; i++) {
+    for(i=0; i<4; i++) {
       Serial.print(remoteIp[i],DEC);
-      Serial.print(".");
+      if(i<3) Serial.print(".");
     }
-    Serial.print(remoteIp[3],DEC);
     
     remotePort = UdpBytewise.getSenderPort();
     Serial.print(" Port ");

libraries/Ethernet/examples/UdpReceiveRaw/UdpReceiveRaw.pde

 #include <Ethernet.h>
 #include <UdpRaw.h>
 
-/* UdpReceive.pde: Example how to receive packets over UDP using UdpRaw library
+/* UdpReceiveRaw.pde: Example how to receive packets over UDP using UdpRaw library
  * prints received packet to serial port
  * bjoern@cs.stanford.edu 12/30/2008
  */
 void setup() {
   Ethernet.begin(mac,ip,gw);
   UdpRaw.begin(localPort);
-  Serial.begin(9600); 
+  Serial.begin(38400); 
 }
 /* LOOP: wait for incoming packets and print each packet to the serial port */
 void loop() {  

libraries/Ethernet/examples/UdpReceiveString/UdpReceiveString.pde

 void setup() {
   Ethernet.begin(mac,ip,gw);
   UdpString.begin(localPort);
-  Serial.begin(9600); 
+  Serial.begin(38400); 
 }
 
 /* LOOP: wait for incoming packets and print each packet to the serial port */

libraries/String/WString.cpp

+/*
+  WString.cpp - String library for Wiring & Arduino
+  version 0.4
+  
+  Based on Tom Igoe's TextString original C++ implementation
+  Copyright (c) 2006-08 Hernando Barragan.  All right 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
+*/
+
+#include <stdlib.h>
+#include "WProgram.h"
+#include "WString.h"
+
+   
+
+
+String::String(const int length)
+{
+  _array = (char*)malloc(length+1);
+  _capacity = length;
+  _length = 0;   
+
+  clear();  
+}
+
+
+String::String(const char* bytes)
+{
+  if(bytes == NULL)
+    bytes= "";
+  _length = strlen(bytes);
+  //if (_capacity < _length) {
+  _capacity = _length;
+  //  free(_array);
+  _array = (char*)malloc(_length+1);
+  //}
+     
+  clear();  
+  setArray(bytes);
+}
+
+
+String::String(const String &str)
+{
+  _length = _capacity = str._length;
+  _array = (char*)malloc(_length + 1);
+  clear();
+  setArray(str._array);
+}
+
+
+// Public Methods
+// Functions available in Wiring sketches, and other libraries
+
+
+const String & String::operator=( const String &rightStr )
+{
+  if ( this == &rightStr )
+    return *this;
+
+  if ( rightStr._length > _capacity )
+  {
+    free(_array);
+    _capacity = rightStr._length;
+    _array = (char*)malloc(_capacity+1);
+  }
+
+  _length = rightStr._length;
+  clear();
+  setArray( rightStr._array );
+
+  return *this;
+}
+
+
+const String & String::operator=(const char* bytes) {
+  //return *this = String(bytes);
+  if(bytes == NULL)
+    bytes = ""; 
+  _length = strlen(bytes);
+  if (_length > _capacity) {
+    _capacity = _length;
+    free(_array);
+    _array = (char*)malloc(_length+1);
+  }
+  clear();
+  setArray(bytes);
+  
+  return *this;
+} 
+
+
+const String & String::operator+=( const char* bytes) {
+  if(bytes == NULL)
+    bytes = "";
+  _length += strlen(bytes);
+  if ( _length > _capacity )
+  {
+    char *tmp = _array;
+    _capacity = _length;
+    _array = (char *) malloc (_length + 1);
+    setArray(tmp);
+    //strcpy( _array, tmp );
+    free(tmp);
+  }
+  strcat( _array, bytes );
+  _array[_length] = '\0';
+
+  return *this;
+} 
+
+
+const String & String::operator+=( const char ch )
+{
+  if ( _length == _capacity ) {
+    char *tmp = _array;
+    _capacity = ++_capacity*2;
+    _array = (char*)malloc(_capacity + 1);
+    setArray(tmp);
+    free(tmp);
+  } 
+
+  _array[ _length++ ] = ch;
+  _array[ _length ] = '\0';
+
+  return *this;
+}
+
+
+
+const String & String::operator+=(int n) 
+{
+  append((long)n, 10);
+
+  //char *t = valueOf((long)n, 10);
+  //*this+=t; 
+  //free(t);
+
+  return *this;
+
+}
+
+
+
+const String & String::operator+=(long n)
+{
+  append(n, 10);
+ 
+  //char *t = valueOf(n, 10);
+  //*this+=t; 
+  //free(t);
+
+  return *this;
+}
+
+
+const String & String::operator+=( const String &str )
+{
+  _length += str._length;
+  if ( _length > _capacity )
+  {
+    char *tmp = _array;
+    _capacity = _length;
+    _array = (char *) malloc (_length + 1);
+    setArray(tmp);
+    //strcpy( _array, tmp );
+    free(tmp);
+  }
+  strcat( _array, str._array );
+  _array[_length] = '\0';
+
+  return *this;
+}
+
+
+
+const String & String::append(char c)
+{
+  return (*this)+=c;
+}
+
+const String & String::append(char *str)
+{
+  return (*this)+=str;
+}
+
+const String & String::append(const String &s1)
+{
+  return (*this)+=s1;
+}
+
+const String & String::append(int n, int base)
+{
+  append((long)n, base);
+
+  //char *t = valueOf((long)n, base);
+  //*this+=t;
+  //free(t);
+
+  return *this;
+}
+
+const String & String::append(long n, int base)
+{
+  char *t = valueOf(n, base);
+  *this+=t;
+  free(t);
+
+  return *this;
+}
+
+const String & String::append(int n)
+{
+  append((long)n, 10);
+  return *this;
+
+  //return (*this)+=n;
+}
+
+const String & String::append(long n)
+{
+  append(n, 10);
+  return *this;
+
+  //return (*this)+=n;
+}
+
+
+// the library uses the append versions instead 
+// of the ones below, sometimes odd things happen :)
+/*
+String operator+(const String &s1, int n)
+{
+  String result(s1);
+  result+=n;
+  return result;
+}
+
+
+String operator+(const String &s1, char c2)
+{
+  String result(s1);
+  result+=c2;
+  return result;
+}
+
+
+String operator+(const String &s1, long n)
+{
+  String result(s1);
+  result+=n;
+  return result;
+}
+
+
+String operator+(int n, const String &s1)
+{
+  String result;
+  result+=n;
+  result+=s1;
+  return result;
+}
+
+
+String operator+(long n, const String &s1)
+{
+  String result;
+  result+=n;
+  result+=s1;
+  return result;
+}
+
+
+String operator+(const String &s1, const String &s2)
+{
+  String result(s1);
+  result+=s2;
+  return result;
+}
+
+
+String operator+(const String &s1, const char *s2)
+{
+  String result(s1);
+  result += s2;
+  return result;
+}
+
+
+String operator+(const char *s1, const String &s2)
+{
+  String result(s1);
+  result += s2;
+  return result;
+}
+
+
+String operator+(char c1, const String &s2)
+{
+  String result;
+  result += c1;
+  result += s2;
+  return result;
+}
+*/
+
+char String::charAt(int index)
+{
+  char thisChar;  // the character to return
+  //int length = strlen(_array); // the length of the string (not the whole array)
+  // as long as charNum isn't negative or bigger than the string length:
+  if (index >= 0 && _length > index) {
+    // get the character
+    thisChar =  _array[index];
+  } else {
+    // if charNum is higher than the string length, return 0:
+    thisChar = 0;
+  }
+  return thisChar;
+}
+
+
+void String::setCharAt(int charNum, char thisChar)
+{
+  if (_length > charNum) {	
+    _array[charNum] = thisChar;
+  }
+}
+
+// Not needed, use append(String) instead
+/*
+const String & String::concat(const String &str)
+{
+  return (*this) += str;
+}
+*/
+
+
+boolean String::equals(char* str)
+{
+  char match = 0;  // whether or not the strings match
+  // strcmp() returns the comparison value of two strings.  0 means they're the same:
+  int comparison = strcmp(_array, str);
+  
+  // if the comparison returns 0, you have a match:
+  match = (comparison == 0);
+  return match;
+}
+
+boolean String::equals(const String &str) 
+{
+  return equals(str.cstr());
+}
+
+
+boolean String::contains(char* subString)
+{
+  char * subStringPointer;
+  subStringPointer = strstr(_array,subString);
+  return (subStringPointer != 0);
+}
+
+
+byte* String::getBytes(void)
+{
+  return (byte*) _array;
+}
+
+
+void String::setArray(const char* bytes) {
+  //clear();
+  strncpy(_array,bytes,_length+1);
+  _array[_length] = '\0';
+}
+
+
+int String::indexOf(int ch)
+{
+  return indexOf(ch, 0);
+}
+
+
+int String::indexOf(const String &str)
+{
+  return indexOf(str, 0);
+}
+
+
+int String::indexOf(char ch, int fromIndex)
+{
+  if(fromIndex >= _length)
+    return -1;
+  char* tmp = strchr( &_array[fromIndex], ch);
+  if(tmp == NULL)
+    return -1;
+
+  return tmp - _array;
+}
+
+
+int String::indexOf(const String &str, int fromIndex)
+{
+  if(fromIndex >= _length)
+    return -1;
+
+  char *result = strstr(&_array[fromIndex], str.cstr());
+  if(result == NULL)
+    return -1;
+
+  return result - _array; 
+}
+
+
+boolean String::startsWith(const String &prefix)
+{
+  if(_length < prefix._length)
+    return false;
+  return startsWith( prefix, 0);
+}
+
+
+boolean String::startsWith( const String &prefix, int offset )
+{
+  if ( offset > _length - prefix._length )
+    return 0;
+
+  return strncmp( &_array[offset], prefix.cstr(), prefix._length ) == 0;
+}
+
+
+String String::substring(int beginIndex)
+{
+  return substring(beginIndex, _length);
+}
+
+
+String String::substring(int beginIndex, int endIndex)
+{
+  if ( beginIndex > endIndex )
+  {
+    int tmp = endIndex;
+    endIndex = beginIndex;
+    beginIndex = tmp;
+  }
+  if ( endIndex > _length )
+  {
+    exit(1);
+  }
+  char ch = _array[ endIndex ];   
+  _array[ endIndex ] = '\0';	  
+  String str = String( _array + beginIndex ); 
+  _array[ endIndex ] = ch;	  
+  return str;
+}
+
+
+char* String::valueOf(int n, int base)
+{
+  return valueOf((long) n, base);
+}
+
+
+char* String::valueOf(long n, int base)
+{
+  char *buf;
+  buf = (char*)malloc(8*sizeof(long)+1);
+  ltoa(n, buf, base);
+  return buf;
+}
+ 
+// The ones below are String version, not used in the actual library
+ 
+/*String String::valueOf(int n, int base) 
+{
+  return valueOf((long) n, base);
+}
+
+
+String String::valueOf(long n, int base) 
+{
+  String str(8*sizeof(long)+1);
+  uint8_t buf[8*sizeof(long)+1];
+
+  int i = 0;
+
+  if(n < 0) {
+    str+='-';
+    n = -n;
+  }
+  if(n == 0) {
+    str+='0';
+    return str;
+  }
+
+  while(n > 0) {
+    buf[i++] = n%base;
+    n /= base;
+  }
+  for (i--; i >= 0; i--){
+    str+=(char)(buf[i] < 10 ? '0' + buf[i] : 'A' + buf[i] - 10);
+  }
+  return str;
+}
+*/
+
+char* String::toCharArray(void)
+{
+// it should return a new array but it might end up
+// being a memory leak in user's programs so it just returns a pointer
+// to the internal array
+/*
+  char *str = (char *)malloc(_length + 1);
+  strcpy(str, _array);
+  return str;
+*/
+
+  return _array;
+}
+
+
+void String::trim(void)
+{
+  // get the position of the last non-zero byte:
+  int stringIndex = 0;
+	
+  // trim from the end of the string:
+  while  (_array[stringIndex] == '\t' || // tab
+    _array[stringIndex] == '\r' || 	 // carriage return
+    _array[stringIndex] == '\n' || 	 // newline
+    _array[stringIndex] == ' ' || 	 // space
+    _array[stringIndex] == 0x11) 	 // vertical tab
+    {
+    	stringIndex++;
+    }
+	
+    memmove(_array, _array+stringIndex, strlen(_array));
+
+    // get the position of the last non-zero byte:
+    stringIndex = strlen(_array) - 1;
+	
+    // trim from the end of the string:
+    while(_array[stringIndex] == '\t' || // tab
+      _array[stringIndex] == '\r' || 	// carriage return
+      _array[stringIndex] == '\n' || 	// newline
+      _array[stringIndex] == ' ' || 	// space
+      _array[stringIndex] == 0x11) 	// vertical tab
+    {
+    	_array[stringIndex] = '\0';
+    	stringIndex--;
+    }
+}
+
+
+boolean String::endsWith(const String &suffix)
+{
+  if ( _length < suffix._length )
+    return 0;
+
+  return strcmp(&_array[_length-suffix._length], suffix.cstr()) == 0;
+}
+
+
+void String::replace(char oldChar, char newChar)
+{
+  char* tmp = _array;
+  while( tmp = strchr( tmp, oldChar ) )
+    *tmp = newChar;
+}
+
+
+void String::toLowerCase(void)
+{
+  for(int i = 0; i < _length; i++) {
+    _array[i] = tolower( _array[i]);
+  }
+}
+
+
+void String::toUpperCase(void)
+{
+  for(int i = 0; i < _length; i++) {
+    _array[i] = toupper( _array[i]);
+  }
+}
+
+
+void String::clear(void) {
+  //int length = strlen(_array);
+  for (int  c = 0; c < _capacity; c++) {
+    _array[c] = '\0'; 
+  }
+
+}
+
+
+/*
+	version() returns the version of the library:
+*/
+String String::version(void) {
+	return "0.4";
+}
+

libraries/String/WString.h

+/*
+  WString.h - String library for Wiring & Arduino
+  Based on Tom Igoe's TextString original C++ implementation
+  Copyright (c) 2006-07 Hernando Barragan.  All right 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
+*/
+
+#ifndef String_h
+#define String_h
+
+#include "WProgram.h"
+#include <string.h>
+#include <ctype.h>
+
+class String
+{
+  public:
+    String(const int length = 16);
+    String(const char* bytes);
+    String(const String &str);
+
+    const String & operator = ( const String &rightStr );
+    const String & operator = ( const char*);
+
+    const String & operator +=( const String &rightStr );
+    const String & operator +=( const char );
+    const String & operator +=( const char* bytes);
+    const String & operator +=( long n );
+    const String & operator +=( int n );
+    
+    const String & append(char);
+    const String & append(char *str);
+    const String & append(int n, int base);
+    const String & append(long n, int base);
+    const String & append(int n);
+    const String & append(long n);
+    const String & append(const String &str);
+
+    //friend String operator +(const String &s1, char c2);
+    //friend String operator +(const String &s1, const String &s2);
+    //friend String operator +(const String &s1, const char *s2);
+    //friend String operator +(const char *s1, const String &s2);
+    //friend String operator +(char c1, const String &s2);
+    //friend String operator +(const String &s1, int n);
+    //friend String operator +(int n, const String &s1);
+    //friend String operator +(const String &s1, long n);
+    //friend String operator +(long n, const String &s1);
+
+    char charAt(int index);
+    void setCharAt(int index, char ch);
+
+    //const String & concat(const String &str);
+
+    boolean equals(char* str);
+    boolean equals(const String &str);
+    boolean contains(char* str);
+    byte* getBytes();
+    void setArray(const char* bytes);
+    int indexOf(int ch);
+    int indexOf(const String &str);
+    int indexOf(char ch, int fromIndex);
+    int indexOf(const String &str, int fromIndex);
+    int length() {return _length;}
+    int capacity() {return _capacity;}
+    boolean startsWith(const String &prefix);
+    boolean startsWith(const String &prefix, int offset);
+    String substring(int beginIndex);
+    String substring(int beginIndex, int endIndex);
+
+    //static String valueOf(int n, int base);
+    //static String valueOf(long n, int base);
+
+    static char* valueOf(int n, int base);
+    static char* valueOf(long n, int base);
+
+    const char*	cstr() const { return _array; }
+    operator char*() { return _array; }
+
+    char* toCharArray();
+    void trim(void);
+    boolean endsWith(const String &suffix);
+    void replace(char oldChar, char newChar);
+    void toLowerCase();
+    void toUpperCase();
+    void clear(void);
+
+
+    char *_array;       // the actual char array
+    uint16_t _length;   // the string length
+    uint16_t _capacity; // the array length 
+
+	String version(void);	// the version number
+
+
+  private:
+
+};
+
+#endif

libraries/String/examples/StringTest2/StringTest2.pde

+/*
+  String demo
+ 
+ Demonstrates the functionality of the String library
+ 
+ To test it, send the microcontroller a few different strings.
+ Try strings with white space at the beginning, at the end, and 
+ on both.
+ 
+ Try the string "What's up?"
+ 
+ by Tom Igoe
+ created 20 Oct. 2006
+ */
+
+
+#include <WString.h>                // include the String library
+String inString = String(90);  // allocate a new String
+String verString = String("String library version ");
+
+void setup() {
+  // open the serial port:
+ Serial.begin(9600);
+  // blink an LED:
+  blink(13,3);
+  // Say hello:
+ Serial.print(verString.toCharArray());
+  Serial.println(inString.version());
+}
+
+void loop () {
+  // read serial data in:
+  if(Serial.available() > 0) {
+    serialEvent(); 
+  }
+}
+
+void serialEvent() {
+
+  // get the latest character:
+  char inChar = Serial.read();
+  // if you don't get a newline, add it to the string:
+  if ((inChar != '\n') && (inChar != '\r')) {
+    inString.append(inChar);
+  } 
+  else {
+    // print the whole string:
+    Serial.print("String = ");
+    Serial.println(inString.toCharArray());
+    Serial.print("String Length: ");
+
+    // print the length of the string:
+    Serial.println(inString.length(), DEC);
+
+
+    // trim the string:
+    inString.trim();
+    Serial.println(inString.toCharArray());
+    Serial.print("trimmed string Length: ");
+    // print the length of the string:
+    Serial.println(inString.length(), DEC);
+
+    // compare the string to another arbitrary string:
+    Serial.print("String equals What's up?: ");
+    Serial.println(inString.equals("What's up?"), DEC);
+
+    // look for a subString:
+    Serial.print("String contains OK: ");
+    Serial.println(inString.contains("OK"), DEC);
+
+    // copy a given string in:
+    inString.setArray("What's up, Doc?");
+    Serial.println(inString.toCharArray());
+    // print the length of the string:
+    Serial.println(inString.length(), DEC);
+    // empty the string:
+    inString = "";
+    // print a blank line:
+    Serial.println("-----------------------------------------");
+   
+    }
+}
+
+
+//  blink method blinks an LED a given number of times
+ 
+void blink(int thisPin, int howManyTimes) {
+  // set the LED's pin as an output:
+  pinMode(thisPin, OUTPUT);
+  // blink the LED:
+  for (int t = 0; t < howManyTimes; t++) {
+    digitalWrite(thisPin, HIGH);
+    delay(200);
+    digitalWrite(thisPin, LOW);
+    delay(200);
+  } 
+}
+