[Solved] Having problems with my adaptation of TinyWebServer

Hello everyone! :slight_smile:

I have joined the forum after a lot of searching, and little progress, unfortunately! Please, allow me to explain...

I have an Uno and Ethernet Shield, and I am trying to control the brightness of the 3 colours of an RGB LED, using a form on a web page which is stored on the Ethernet Shield's SD card. What I have right now is heavily based on the TinyWebServer, which I have seen mentioned on here a few times, the BlinkLED example, to be more precise.

In the BlinkLED example, the web page has a single image which is used to turn the switch on and off, and because I want to enter values, I have had to change this completely. While I can get the page (and the accompanying JavaScript) to generate an array of 3 values (for red, green and blue respectively), I cannot make my current sketch accept this data.

To make things a little easier, I have been trying to work with just the red value for the last few hours, and I have noticed that using client.read(), as in the original example, only the first digit of the red value is read. When I print it out (using Serial.println()), it gives me a value corresponding to the ASCII value of the digit that has been read (i.e., 48 to 57).

So, I would like to know if it is possible to tell the Arduino not to change the incoming value to an ASCII number, and how exactly do I get it to take the whole number (which would go up to 255), and then following that, a set of 3 values?

I did email ovidiucp, the creator, a couple of weeks ago, but he didn't reply =(

I would sincerely appreciate any help,
plusminus_

P.S. I have attached my files and my current sketch, for anyone reading who may be familiar with TWS, and/or is willing to help :stuck_out_tongue:

help.zip (43.1 KB)

I have a feeling that the problem might have something to do with client.read(), since it only reads one byte at a time, it seems. But then why does it only recognise the first digit of the number, and not the whole number (which for now will have a maximum possible value of 255). :astonished:

It would appear it's because you do a web_server.get_client() - which presumably gives you a fresh start on reading from the client, and then you do a single if (client.available()) client.read() before returning.

IOW, you're only asking for a single digit for each call to get_client();

Hello, Mr Dovey! Thank you very much for your reply :slight_smile:

client.read() is one of the many things I have copied from the original example:

Client& client = web_server.get_client();
  if (client.available()) {
    char ch = (char)client.read();
    if (ch == '0') {
      setLedEnabled(false);
    } else if (ch == '1') {
      setLedEnabled(true);
    }
  }

I understand that it only reads one byte, but I am not sure how to change this to read a whole number. Would I need to use a for loop? And possibly the strtok function?

Thank you,
+-

Ah, I've just had another look at zoomkat's code, would I need to use while(client.available()), rather than if? I wonder...

edit: I have changed if to while, and now Serial.print gives me the ASCII value of each digit. I think I'm getting closer! :sweat_smile:

Good! My (small) experience with CGI has all been on larger systems, where I had enough memory to parse key/value pairs into a malloc()'d (dynamic) "dictionary" - which probably wouldn't be a good strategy in a machine with 8K of RAM. :slight_smile:

If zoomkat appears to have a good handle on the tiny web server software, I think I'd watch what he's doing, help when I could, and learn from each other. :grin:

Some test code below that shows how to capture the GET request string. Once the string is captured, it can be parsed and evaluated in many different ways. I've got some old bot code that sends values for two servo us positions that captures the ascii positions in the string then converts it into numbers for use. Fairly simple to do. With the code below you can open the serial monitor to see what the arduino server is receiving from the web page via the GET request.

//zoomkat 12-8-11
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use the \ slash to escape the " in the html 
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

#include <SPI.h>
#include <Ethernet.h>

#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

  myservo.write(90); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo co
  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("server LED test 1.0"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
          
          client.println("<a href=\"/?on\"\">ON</a>"); 
          client.println("<a href=\"/?off\"\">OFF</a>"); 

          client.println("</BODY>");
          client.println("</HTML>");
 
          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            myservo.write(40);
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            myservo.write(140);
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}

zoomkat:
Some test code below that shows how to capture the GET request string. Once the string is captured, it can be parsed and evaluated in many different ways. I've got some old bot code that sends values for two servo us positions that captures the ascii positions in the string then converts it into numbers for use. Fairly simple to do. With the code below you can open the serial monitor to see what the arduino server is receiving from the web page via the GET request.

After much trial and error, I have sort of gotten it to work. Using a method much like this one (see joachimp's post), I can get the value of the red LED that I entered on the web page, and get it to both print in Serial Monitor and change the brightness of the LED to this value.

The problem now is that it only works when I put in a 3 digit value (so if I wanted a brightness of 0, I would have to enter 000). If I do not do this, the Arduino crashes! Do you have an idea of how I could allow the entry of a one or two digit number?

I have attached my current .ino file, and I am going to try adding the other two values.

What I am working on now will eventually be the P, I and D gains of a PID controller, so I will have to change to float or another number type at some point!

RGBControl.ino (4.26 KB)

Hello again,

I have managed to extract all 3 inputs, and print them in Serial Monitor. I had to change the way my JavaScript POSTed the data, and I used strtok_r to split (or parse?) the string.

But... I am still having trouble with values with less than 3 digits. I think the problem may lie in the setting up of the dataString array - the IDE keeps telling me to specify a size, but if one of the inputs does not have 3 digits, then the array is not filled... and my Arduino crashes.

How do you think I could change this to allow 1 or 2 digit numbers?

Thanks for all your help so far! :wink:

P.S. I have attached my currently working version of the sketch.

RGBControl.ino (4.59 KB)

This is just a WAG - but it might be that you're not getting a semicolon after the blue value. If strtok_r() doesn't find the separator, then it returns NULL, and that might not work so well when you do the atoi() call.

It's difficult to be very helpful without knowing what the data looks like - a Serial.println(dataString) call might be informative. :slight_smile:

BTW, the use of strtok_r() doesn't make much sense in code that uses atoi() because, while strtok_r is re-entrant, atoi() isn't.

Hi Morris :slight_smile:

I think I have gotten everything to work. I added a function in my JavaScript to add leading zeros if the number was less than 3 digits, so the string from the form is always in a form that the Arduino can understand.

Because dataString is currently a char, the output of the strtok_r is also a char (I think!). When I tried to change everything to int, without atoi(), it only printed the first value of each box, and as an ASCII value again!

I will try tweaking this current version for float type numbers next, but I need a break. :cold_sweat:

Thank you very much for your help, Mr. Dovey!

I wasn't suggesting that you change everything to an int - just that if the code isn't (completely) re-entrant, then it doesn't make sense to use the re-entrant form of strtok().

I'm glad that you've gotten it working, but the credit goes to you and zoomcat. You're the ones doing all the heavy lifting here - while I've just been along for the ride (in the hope that I might learn a bit by joining in the conversation).

Good job!

I think I have gotten everything to work. I added a function in my JavaScript to add leading zeros if the number was less than 3 digits, so the string from the form is always in a form that the Arduino can understand.

When converting a string to a number, the leading zeros are delt with in the conversion (like in the below code). The arduino understanding the formatting of the data string it receives is important. The below code relies on each servo position being four characters long and in a specific order for successful parsing. If multiple data strings are being sent, an end of string delimiter is handy as the length of the string is no longer critical (7 instead of 007 could be sent), characters preceeding the delimiter are captured and evaluated.

// zoomkat 12-13-11 serial servo (2) test
// for writeMicroseconds, use a value like 1500
// for IDE 1.0
// Powering a servo from the arduino usually DOES NOT WORK.
// two servo setup with two servo commands
// send eight character string like 15001500 or 14501550
// use serial monitor to test

#include <Servo.h> 
String readString, servo1, servo2;
Servo myservo1;  // create servo object to control a servo 
Servo myservo2;

void setup() {
  Serial.begin(9600);
  myservo1.attach(6);  //the pin for the servo control 
  myservo2.attach(7);
  Serial.println("two-servo-test-1.0"); // so I can keep track of what is loaded
}

void loop() {

  while (Serial.available()) {
    delay(3);  //delay to allow buffer to fill 
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    } 
  }

  if (readString.length() >0) {
      Serial.println(readString); //see what was received
      
      // expect a string like 07002100 containing the two servo positions      
      servo1 = readString.substring(0, 4); //get the first four characters
      servo2 = readString.substring(4, 8); //get the next four characters 
      
      Serial.println(servo1);  //print to serial monitor to see parsed results
      Serial.println(servo2);

      int n1 = servo1.toInt();
      int n2 = servo2.toInt();

      Serial.println("the numbers are :");
      Serial.println(n1);  //print to serial monitor to see number results
      Serial.println(n2);
            
      myservo1.writeMicroseconds(n1); //set servo position 
      myservo2.writeMicroseconds(n2);
    readString="";
  } 
}

zoomkat:
When converting a string to a number, the leading zeros are delt with in the conversion (like in the below code). The arduino understanding the formatting of the data string it receives is important. The below code relies on each servo position being four characters long and in a specific order for successful parsing. If multiple data strings are being sent, an end of string delimiter is handy as the length of the string is no longer critical (7 instead of 007 could be sent), characters preceeding the delimiter are captured and evaluated.

Thanks for this code, I've just re-written that bit of my sketch to work like your code; I liked the look of it.

[quote author=Morris Dovey link=topic=98259.msg738022#msg738022 date=1332716324]
I wasn't suggesting that you change everything to an int - just that if the code isn't (completely) re-entrant, then it doesn't make sense to use the re-entrant form of strtok().

I'm glad that you've gotten it working, but the credit goes to you and zoomcat. You're the ones doing all the heavy lifting here - while I've just been along for the ride (in the hope that I might learn a bit by joining in the conversation).

Good job![/quote]
I initially wanted to use strtok(), but I couldn't see how I would get it to work. Given what you have said about the re-entrant form of strtok(), I have decided to use zoomkat's method above. It just seems more elegant to me.

Thank you both :open_mouth: