server/ RGB controller

Developing a 3-channel lighting controller to run off a server hosted by the arduino ethernet. I have a server operational and more-or-less functioning.

One problem I have is that if I enter in invalid data into the URL to pull up the browser-based software, the controller hangs and I must reset the controller.

Examples:

  1. I type in the IP address to open the controller interface
    http://192.168.24.253/

  2. I make setting adjustments to different channels
    http://192.168.24.253/?r=255&g=255&b=255&H=Program
    sets r, g, & b values. Program button applies the data

Everything works fine in this fashion

  1. If erroneous data is sent to the controller like this:
    http://192.168.24.253/blahblahblah

the controller hangs.

Is there a way I can safeguard against bad data? Or at least prevent the controller from hanging and needing a reset?

If I have to deal with it hanging, would a watchdog timer work in resetting the controller?

I suspect the problem in the code is within this section, but honestly I'm confused as to how this portion of the code works.

Portion of "Loop"

void loop()
{
  // Serial.println(str);

  //   Serial.print("freeMemory()=");
  //   Serial.println(freeMemory());
  //   delay(2000);


  int bufLength;
  Client client = server.available();
  if (client) {
    boolean current_line_is_blank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (inString.length() < maxLength) {
          //inString.append(c);
          inString += c;
        }      

        if (c == '\n' && current_line_is_blank) {
          if (inString.indexOf("?") > -1) {
            int Pos_r = inString.indexOf("r");
            int Pos_g = inString.indexOf("g", Pos_r);
            int Pos_b = inString.indexOf("b", Pos_g);
            int End = inString.indexOf("H", Pos_b);
            if(End < 0){
              End =  inString.length() + 1;
            }
            bufLength = ((Pos_g) - (Pos_r+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }    
            inString.substring((Pos_r+2), (Pos_g-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            r = atoi(colorBuff);
            bufLength = ((Pos_b) - (Pos_g+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }      
            inString.substring((Pos_g+2), (Pos_b-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            g = atoi(colorBuff);
            bufLength = ((End) - (Pos_b+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }      
            inString.substring((Pos_b+2), (End-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            b = atoi(colorBuff);

This is based off an individuals code here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238498454

The entire code won't fit in a single message, so here it is in 2 parts:

Includes, Variables, Functions, and Setup:

I'm using the Arduino Ethernet, and v0022 of the compiler

/* Advanced Illumination
 LEDLink
 */

#include <EEPROM.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#define maxLength 25
#include <memoryFree.h>


// these values are saved in EEPROM

const byte CH_EEPROM_ID = 0x99;   // used to identify if valid data in EEPROM

byte mac[] = {
  0x00, 0x22, 0xFE, 0x10, 0x00, 0x00 }; //update with factory MAC settings
byte default_ip[] = {
  192, 168, 24, 253};
byte default_gate[] = {
  192,168,24,1};
byte default_subnet[4];


String inString = String(maxLength);
int val;

byte r;               //initial values to hold channel settings
byte g;              
byte b; 
//byte w;

char colorBuff[5];
Server server(80);    //server port, HTML

const byte ch0Pin = 5;                // channel0 output PWM pin
const byte ch1Pin = 6;                // channel1 output PWM pin
const byte ch2Pin = 9;                // channel2 output PWM pin

const byte strb0Pin = 4;              // channel0 STRB enable pin
const byte strb1Pin = 7;              // channel1 STRB enable pin
const byte strb2Pin = 8;              // channel2 STRB enable pin

// TODO reserve pins 2 & 3 for hardware interrupts

const char swVer[]="0.02a - Alpha";  //ethernet software version number
const char fwVer[]="010-100";        //controller firmware version number
const char serialNum[]="000001";     //controller serial number


//constants used to identify EEPROM addresses
const int CH_ID_ADDR = 0;            // pointer to the EEPROM address used to store the user channel settings
const int R_PIN_ADDR = 1;            // the EEPROM address used to store the pin
const int G_PIN_ADDR = 2;            // ""
const int B_PIN_ADDR = 3;            // ""
const int CH_INTERVAL_ADDR = 4;      // the EEPROM address used to store the interval

const int IP_ADDR_ID = 5;            //pointer to the EEPROM address used to store the user IP ADDRESS
const int IP_ADDR[] = {
  6,7,8,9};

//FUNCTIONS*********

//------------------------------setChannelPower--------------------------------------
void setChannelPower()
{
  analogWrite(ch0Pin, r);
  analogWrite(ch1Pin, g);
  analogWrite(ch2Pin, b);
  delay(5);
  // analogWrite(ch3Pin, w);       //remnant from 4 channels
  // delay(5);

  EEPROM.write(CH_ID_ADDR, 0x99);
  EEPROM.write(R_PIN_ADDR, r);
  EEPROM.write(G_PIN_ADDR, g);
  EEPROM.write(B_PIN_ADDR, b);
  delay(5);
}

//-----------------------------------------------------------------------------------

//------------------------------setMode----------------------------------------------
void setMode()
{
  //TODO: add continuous and strobe modes
  // User will switch between modes using software
  // strobe mode will need to shut off the light and enable 
  //the high current sense resistor
}
//-----------------------------------------------------------------------------------

//------------------------------setFactoryDefault------------------------------------
void setFactoryDefault()
{
  //TODO: add code to write all 0x00 to EEPROM
  // When controller first boots, it will set default values when it sees that
  // EEPROM_IDs are 00 and assign default variables accordingly

  for(unsigned int i = 0; i < 512; i++)
  {
    EEPROM.write(i, 0x00);
  }
}  
//----------------------------------------------------------------------------------

//-----------------------------configureDHCP----------------------------------------

/* void configureDHCP()
 {
 // start the Ethernet connection:
 if (Server.begin(mac) == 0) {
 Serial.println("Failed to configure Ethernet using DHCP");
 // no point in carrying on, so do nothing forevermore:
 for(;;)
 ;
 }
 // print your local IP address:
 Serial.print("My IP address: ");
 for (byte thisByte = 0; thisByte < 4; thisByte++) {
 // print the value of each byte of the IP address:
 Serial.print(Ethernet.localIP()[thisByte], DEC);
 Serial.print("."); 
 }
 Serial.println();
 */

//-----------------------------displayInfo------------------------------------------

void displayInfo()
{ 

  Serial.print("\n<Channel Settings>");     
  Serial.println();  
  Serial.print("Channel 0: ");
  Serial.println(r,DEC);
  Serial.print("Channel 1: ");
  Serial.println(g,DEC);
  Serial.print("Channel 2: ");
  Serial.println(b,DEC);


  Serial.print("\n<TCP/IP settings>");     
  Serial.println();
  Serial.print("IP Address: ");
  for (byte i = 0; i <4; i++)
  {
    Serial.print(default_ip[i], DEC);    
    Serial.print(".");
  }

  Serial.println();
  Serial.print("Gateway: ");
  for (byte i = 0; i <4; i++)
  {
    Serial.print(default_gate[i], DEC);    
    Serial.print(".");

  }   
  Serial.println();
  Serial.print("Subnet: ");
  for (byte i = 0; i <4; i++)
  {
    Serial.print(default_subnet[i], DEC);    
    Serial.print(".");

  }
  Serial.println();

  Serial.print("\n<Hardware Information>");
  Serial.println();
  // Serial.println("Software Version: ");
  // Serial.print(swVer);
  Serial.print("Firmware Version: ");
  Serial.println(fwVer);
  Serial.print("Serial Number: ");
  Serial.println(serialNum);



  //TODO-- add more stuff to display here

}
//---------------------------------------------------------------------------------- 

//*******************************SETUP**********************************************

void setup()
{
  Serial.begin(115200);

  //read EEPROM and determine if channel data and setup data has been written
  //
  byte id = EEPROM.read(CH_ID_ADDR);     // read the first byte from the EEPROM
  if( id == CH_EEPROM_ID)                //looks for 0x99 in ID_ADDR 0
  {
    // here if the id value read matches the value saved when writing eeprom
    Serial.println("Using channel data from EEPROM");

    r = EEPROM.read(R_PIN_ADDR);
    g = EEPROM.read(G_PIN_ADDR);
    b = EEPROM.read(B_PIN_ADDR);

    delay(50);

    analogWrite(ch0Pin, r);    //write EEPROM channel data to output pins
    analogWrite(ch1Pin, g);
    analogWrite(ch2Pin, b);


  }
  else
  {
    // here if the ID is not found,  write the default data
    Serial.println("Writing default channel data to controller");
    analogWrite (ch0Pin, 255);            //default is 255 = 100% output
    analogWrite (ch1Pin, 255);
    analogWrite (ch2Pin, 255);
    // analogWrite (ch3Pin, 255);          //channel 4 remnant

  }

  Ethernet.begin(mac, default_ip);
  server.begin();
  displayInfo();


}

Loop

//***************************************LOOP**********************************************
void loop()
{
  // Serial.println(str);

  //   Serial.print("freeMemory()=");
  //   Serial.println(freeMemory());
  //   delay(2000);


  int bufLength;
  Client client = server.available();
  if (client) {
    boolean current_line_is_blank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (inString.length() < maxLength) {
          //inString.append(c);
          inString += c;
        }      

        if (c == '\n' && current_line_is_blank) {
          if (inString.indexOf("?") > -1) {
            int Pos_r = inString.indexOf("r");
            int Pos_g = inString.indexOf("g", Pos_r);
            int Pos_b = inString.indexOf("b", Pos_g);
            int End = inString.indexOf("H", Pos_b);
            if(End < 0){
              End =  inString.length() + 1;
            }
            bufLength = ((Pos_g) - (Pos_r+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }    
            inString.substring((Pos_r+2), (Pos_g-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            r = atoi(colorBuff);
            bufLength = ((Pos_b) - (Pos_g+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }      
            inString.substring((Pos_g+2), (Pos_b-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            g = atoi(colorBuff);
            bufLength = ((End) - (Pos_b+2));
            if(bufLength > 4){  //dont overflow the buffer
              bufLength = 4;
            }      
            inString.substring((Pos_b+2), (End-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
            b = atoi(colorBuff);



            /* remnant from having 4 channels
             if (c == '\n' && current_line_is_blank) {
             if (inString.indexOf("?") > -1) {
             int Pos_r = inString.indexOf("r");
             int Pos_g = inString.indexOf("g", Pos_r);
             int Pos_b = inString.indexOf("b", Pos_g);
             int Pos_w = inString.indexOf("w", Pos_b);
             int End = inString.indexOf("H", Pos_w);
             if(End < 0){
             End =  inString.length() + 1;
             }
             bufLength = ((Pos_g) - (Pos_r+2));
             if(bufLength > 5){  //dont overflow the buffer
             bufLength = 5;
             }    
             inString.substring((Pos_r+2), (Pos_g-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
             r = atoi(colorBuff);
             bufLength = ((Pos_b) - (Pos_g+2));
             if(bufLength > 5){  //dont overflow the buffer
             bufLength = 5;
             }      
             inString.substring((Pos_g+2), (Pos_b-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
             g = atoi(colorBuff);
             bufLength = ((Pos_w) - (Pos_b+2));
             if(bufLength > 5){  //dont overflow the buffer
             bufLength = 5;
             }      
             
             inString.substring((Pos_b+2), (Pos_w-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
             b = atoi(colorBuff);
             bufLength = ((End) - (Pos_w+2));
             if(bufLength > 5){  //dont overflow the buffer
             bufLength = 5;
             }   
             inString.substring((Pos_w+2), (End-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer
             w = atoi(colorBuff);
             //    if(bufLength > 5){  //dont overflow the buffer
             //      bufLength = 5;
             */


          }

          client.println("<html>");
          client.println("<head>");
          client.print("<title> LEDLink GUI</title>");
          client.println("</head>");

          client.println();
          client.println("<body>");

          //         client.println("<center>");
          client.print("<h2><font face=verdana color=green>LEDLink</font></h2>   ");
          client.println();

          client.print("<font face=verdana>"); 
          client.print("<h3><a href=\"http://www.advill.com\"<"
            "target=\"_blank\">Advanced Illumination</a></h3>");
          //          client.println("</center>");
          client.println();


          client.println("<fieldset style=width:275>");
          client.println("<legend>Channel Settings</legend>");
          client.println("<table>");
          client.println("<tr></td>Enter Values 0-255<td></tr></td>");

          client.println();
          client.println("
");
          client.println("
"); 


          client.println("<tr><td><form method=get>Channel 0: <input type=text size=10 name=r />""</tr></td>");
          client.println("<tr><td><form method=get>Channel 1: <input type=text size=10 name=g />""</tr></td>");
          client.println("<tr><td><form method=get>Channel 2: <input type=text size=10 name=b />""</tr></td>");
          //        client.println("<tr><td><form method=get>Channel 3: <input type=text size=10 name=w/>""</tr></td>");

          client.println("<tr><td>&nbsp;<input name=H type=submit value=Program align=RIGHT></form></tr></table>");

          setChannelPower();

          // TODO: Move this to a function to set channel power
          // function should check for value between 0-255
          // if >255, set value to 255

          client.println("</fieldset>");
          client.println("
");
          client.println();


          client.println("<fieldset style=width:275>");
          client.println("<legend>Status/Information</legend>");
          client.println("
");
          client.println();


          client.println("Channel 0:    ");
          client.print(r,DEC);
          client.println("
");
          client.println();

          client.println("Channel 1:    ");
          client.print(g,DEC);
          client.println("
");
          client.println();

          client.println("Channel 2:    ");
          client.print(b,DEC);
          client.println("
");
          client.println();
          client.println("
");
          client.println();

          /*          
           client.println("Channel 3:    ");
           client.print(w);
           client.println("
");
           client.println();
           client.println("
");
           client.println();
           */

          client.println(" Software Version: ");
          client.print(swVer);
          client.println("
");
          client.println();

          client.println(" Firmware Version: ");
          client.print(fwVer);
          client.println("
");
          client.println();


          client.println(" Serial Number: ");
          client.print(serialNum);
          client.println("
");
          client.println();


          client.println("</fieldset>");
          client.print("</font>");

          client.println("</body></html>");
          break;
        }
        if (c == '\n') {
          current_line_is_blank = true;
        }
        else if (c != '\r') {
          current_line_is_blank = false;
        }
      }
    }
    delay(1);
    inString = "";
    client.stop();


  }
}
            bufLength = ((Pos_g) - (Pos_r+2));

What happens here if Pos_g or Pos_r is -1?

            inString.substring((Pos_r+2), (Pos_g-1)).toCharArray(colorBuff, bufLength);  //transfer substring to buffer

Or here? Or the other places where you do not properly check the values used in the substring() function?