Wifly adhoc ajax (no internet)???

I’ve done some digging and can’t find how to do the following. I would like to enable adhoc on my uno/wifly and use them as a webserver to control something but I want real time displays of the analogs using ajax. The only ajax that I have used successfully in the past was with the ASP.Net Ajax Toolkit which is basically a large set of files that reside on the server. Obviously I don’t have the luxery of space. In addition, I wont be connected to the internet.

Can somebody provide me with example code on a simple ajax application?

Thanks.

Ok, I was able to make the javascript requests work but I am getting hung up on the “xmlhttprequest” function. I’m not sure what to use in the “URL” parameter. This would normally be an xml file or a database address but with the arduino, I’m not sure what url is required for “analogRead(1)”. I’ve tried many different options including the IP address and analogRead, with no luck. Any ideas? I’m starting to think that I cannot have standalone real time web values. BTW…I don’t think I can go the PHP route since I want to access and control the arduino via adhoc in a remote area via my iphone (Safari).

Here is a javascript sample from my WiFly project:

<script type="text/javascript">
function getVolts()
{
var xmlhttp;
var reply;
var t;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
	reply = xmlhttp.responseText.split(",");
	
	reply[0] = parseInt(reply[0], 16);
	reply[1] = parseInt(reply[1], 16);
	reply[2] = parseInt(reply[2], 16);
	
	reply[0] = reply[0] * 0.1953;
	
    document.getElementById("volts").innerHTML=reply[0];
	document.getElementById("current").innerHTML=reply[1];
	document.getElementById("temp").innerHTML=reply[2];
    }
  }
xmlhttp.open("GET","getvolts",true);
xmlhttp.send();
t = setTimeout("getVolts()", 8000);
}
</script>

The code above retrieves data from my WiFly Webserver every 8 seconds.

You cannot directly request AnalogRead(1) from javascript or a web browser.

If you are looking to display real time data in a web browser you must do the following:

1.) Send an HTTP Request to the Arduino with a identifying url, such as xmlhttp.open(“GET”,“FUNCTION_1”,true);

2.) Process the HTTP Request on the Arduino.

Example:

GET /FUNCTION_1 HTTP/1.1

You are intersted in the /FUNCTION_1 located in the first line of the HTTP Request.

3.) If you receive a “FUNCTION_1” HTTP Request, perform your analogReads() and build a string to return to the client.

I use a comma seperated string to make parsing the data easier in the browser.

4.) Print the string to the client.

5.) With the data received in the browser, you can now format and display the data as you wish.

Thanks for the response CapacitiveMind but this code appears to require a complete screen refresh in order to change the data. Upon reading the function passed in the header arduino would need to resend the entire page. Attached is a iphone screenshot of my project in the infancy level. The Auto/Manual command and feedback fucntion similarly to what you are describing but I would like the value to update perhaps as frequently as once every two seconds without refreshing the entire screen.

The only portion of the page that is changed by the script are the html objects with the listed ID’s.

document.getElementById("volts").innerHTML=reply[0];
   document.getElementById("current").innerHTML=reply[1];
   document.getElementById("temp").innerHTML=reply[2];

The Arduino only sends back a comma separated string which is broken down in the script, such as “23, 123, 92”.

Example:

Browser Calls for 169.254.1.1.

Arduino serves up the homepage.

Function is called via a button.

Function requests data to refresh.

Arduino returns comma seperated string of analog data.

Function breaks down and refreshes the returned data on the page.

Ok…I’m just not getting it. Below is my code. This makes me feel like a poor programmer even though I wrote a fair amount of C#. I’m simply not able to execute the funtion “getVolts”. Or maybe I am but it’s not returning a value to the textbox. I couldn’t get the timer to work so I added the button with no success. How does the Arduino function “getaiOne” refresh the data to the client?

I apologize for being such a novice but I’ve been hung up on this, even after reading multiple JavaScript tutorials.

/*
 * Web Server
 *
 * (Based on Ethernet's WebServer Example)
 *
 * A simple web server that shows the value of the analog input pins.
 */

#include "WiFly.h"
#include "Credentials.h"

String readString = String(30); //string for fetching data from address
int ledPin = 4;  // LED pin
boolean LEDON = false; //LED status flag

Server server(80);

void setup() {
  
  pinMode(ledPin, OUTPUT); 
  
  WiFly.begin();

  if (!WiFly.join(ssid)) {
    while (1) {
      // Hang on failure.
    }
  }

  Serial.begin(9600);
  Serial.print("IP: ");
  Serial.println(WiFly.ip());
  
  server.begin();
}

void loop() {
  Client client = server.available();
  if (client) {
    // an http request ends with a blank line
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
              //read char by char HTTP request
            if (readString.length() < 30) 
              {
                //store characters to string 
                readString == c;
              }  
                //output chars to serial port
                
             Serial.print(c);
             
             //if HTTP request has ended
             if (c == '\n') {
             //lets check if LED should be lighted
             if(readString.indexOf("?") < 0)
                {
                  //skip everything
                }
                else
                //lets check if LED should be lighted
                if(readString.indexOf("L=1") > 0)
                {
                  //led has to be turned on
                  digitalWrite(ledPin, HIGH);
                  LEDON = true;
                }else{
                  //led has to be turned OFF
                  digitalWrite(ledPin, LOW);
                  LEDON = false;
                }   
             }             

             
           if(readString.indexOf("getaiOne") >0)
           {
              getaiOne();           
           }
           
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<meta name=viewport content=width=device-width, initial-scale=2.3, maximum-scale=1, user-scalable=no>");
          client.println("<body style=background-color:#C0C0C0>");
//          client.println("<table align=center border=0 colspan=3 width=200>");
//          client.println("<font color='#333333'><th colspan=3>PID</font></th>");
//          client.println("<tr>");
//          if(LEDON)
//          {
//            client.println("<form method=get name=PIDmode><td align=center width=100><input type=radio name=mode value=1 /> Auto      </td></form>");
//            client.println("<form method=get name=PIDmode><td align=center width=100><input type=radio name=mode value=0 /> Manual    </td></form>");
//          }
//          else
//          {
//            client.println("<form method=get name=PIDmode><td align=center width=100><input type=radio name=mode value=0 /> Auto      </td></form>");
//            client.println("<form method=get name=PIDmode><td align=center width=100><input type=radio name=mode value=1 /> Manual    </td></form>");
//          }
//          client.println("</tr></table>");
          client.println("<script type=text/javascript>");
          client.println("function getVolts()");
          client.println("{");
          client.println("var xmlhttp;");
          client.println("var reply;");
          client.println("var t;");
          client.println("if (window.XMLHttpRequest)");
          client.println("  xmlhttp=new XMLHttpRequest();");
          client.println("  }");
          client.println("else");
          client.println("  xmlhttp=new ActiveXObject(Microsoft.XMLHTTP);");
          client.println("  }");
          client.println("xmlhttp.onreadystatechange=function()");
          client.println("  {");
          client.println("  if (xmlhttp.readyState==4 && xmlhttp.status==200)");
          client.println("    {");
          client.println("   reply = xmlhttp.responseText.split(,);");
          client.println("   reply[0] = parseInt(reply[0], 16);");
//          client.println("   reply[1] = parseInt(reply[1], 16);");
//          client.println("   reply[2] = parseInt(reply[2], 16);");
//          client.println("   reply[0] = reply[0] * 0.1953;");
          client.println("    document.getElementById(volts).innerHTML=reply[0];");
//          client.println("   document.getElementById(current).innerHTML=reply[1];");
//          client.println("   document.getElementById(temp).innerHTML=reply[2];");
          client.println("    }");
          client.println("  }");
          client.println("xmlhttp.open(GET,getaiOne,true);");
          client.println("xmlhttp.send();");
//          client.println("t = setTimeout(getVolts(), 8000);");
          client.println("}");
          client.println("</script>");
          client.println("<form>");
          client.println("<input type=text id=volts />
");
          client.println("<input type=button value=Display value onClick=getVolts() />");
          client.println("</form>");
          client.println("</body></html>");
          //clearing string for next read
          readString="";
          //stopping client
          client.stop();
  }
}
  }
}


  String getaiOne(){
    String stringOne =  String(analogRead(0), DEC);
  return stringOne;   
  }

No worries, we are all here to learn.

You’re close to a working project.

You’re code is sending the same html every time an http request is received. As a result, the page will always refresh.

To allow for data to be displayed without refreshing the page, you will need to add some handling to determine the type of http request you are receiving.

A little background information before we proceed:

An GET Http Request looks like the following:

GET / HTTP/1.1
Host: 192.168.1.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2
009082707 Firefox/3.0.14
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

Notice the first line:

GET / HTTP/1.1

This request is a request for the home page. When you receive this request, with nothing following the /, you will return the html for your home page.

However, if you receive a request like this:

GET /getvolts HTTP/1.1

You will return a formatted string, containing the data you wish to display.

This is acheived with a handler like this:

if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println("<h2>Files:</h2>");
          ListFiles(client, LS_SIZE);
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
          Serial.println(filename);

          if (! file.open(&root, filename, O_READ)) {
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            break;
          }
          
          Serial.println("Opened!");
                    
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/plain");
          client.println();
          
          int16_t c;
          while ((c = file.read()) > 0) {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }

The tutorial explaining this code is found at http://www.ladyada.net/learn/arduino/ethfiles.html

By adapting this style of http request handling, you will be able to display real time data in your browser.

When a request for the home page is received, send the html for the homepage.

When a request for data is received, send the data.

Also, I would recommend reading this tutorial to store the html into the program memory to save on RAM.

http://www.arduino.cc/en/Reference/PROGMEM

@CapacitiveMind, may I ask what hardware you’re runnign this with? You mentioned Wifly, so is it an Arduino with a Wifly shield? if so, what implements the web server?

Thanks!

jarcher:
@CapacitiveMind, may I ask what hardware you’re runnign this with? You mentioned Wifly, so is it an Arduino with a Wifly shield? if so, what implements the web server?

Thanks!

The particular hardware in my application are the following:

[Arduino Uno

[WiFly Shield

[uSD Shield

http://farm7.static.flickr.com/6137/598 … eff280.jpg

The Arduino implements the web server.

All html is served from the uSD card.](microSD Shield Retail - RTL-09899 - SparkFun Electronics)](SparkFun Electronics)](http://www.sparkfun.com/products/9950)

Ok…so I took your latest advice CapacitiveMind…

You will return a formatted string, containing the data you wish to display.

This is acheived with a handler like this:

...and I just want to confirm that i can pass back to the browser only a string, no header. If this is the case, then the following basic code (immediately below) should work as my function (xmlhttp.open('GET','ListFiles(client, 0)',true) to return the updated values back to the website. When I call this function on a page load, the value is returned so I'm pretty confident that it works.
void ListFiles(Client client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
    String stringOne =  String(analogRead(0), DEC); 
    String stringTwo = String(analogRead(1), DEC);
    String stringThree = String(analogRead(2), DEC); 
    String stringAll = stringOne + "," + stringTwo + "," + stringThree;
    client.println(stringAll);
    Serial.print(stringAll);
}

I’m now having issues with an overflow in what’s indicated to my by an IE alert box as line 10 of my code. It keeps pointing to the “if (window.XMLHttpRequest)” and this code iterates many times when I execute the javascript. For some reason the code iterates until it crashes. I still have not even had success at reaching the```
xmlhttp.open(‘GET’,‘ListFiles(client, 0)’,true);


client.println(““);
client.println(”“);
client.println(”“);
client.println(”“);
client.println(”“);
client.println(”“);
client.println(”“);
client.println(”“);
client.println(”");

Sorry for the delay jlang.

Below you will find the code I use to for my web server:

/*
 * Web Server
 *
 * (Based on Ethernet's WebServer Example)
 *
 * A simple web server that shows the value of the analog input pins.
 */

#include "WiFly.h"
#include "Credentials.h"
#include <SD.h>;

//Put commonly used variables in FLASH memory instead of SRAM
prog_char html_success[] PROGMEM = "HTTP/1.1 200 OK";
prog_char html_nf[] PROGMEM = "HTTP/1.1 404 Not Found";
prog_char html_fnf[] PROGMEM = "<h2>File Not Found!</h2>";
prog_char jpg[] PROGMEM = "Content-Type: image/jpeg";
prog_char gif[] PROGMEM = "Content-Type: image/gif";
prog_char png[] PROGMEM = "Content-Type: image/png";
prog_char htm[] PROGMEM = "Content-Type: text/html";
prog_char js[] PROGMEM = "Content-Type: text/javascript";
prog_char txt[] PROGMEM = "Content-Type: text/plain";
prog_char ico[] PROGMEM = "Content-Type: image/x-icon";
prog_char css[] PROGMEM = "Content-Type: text/css";

PROGMEM const char *client_response[] = 	 
{   
  html_success,
  html_nf,
  html_fnf,
  jpg,
  gif,
  png,
  htm,
  js,
  txt,
  ico,
  css
};

Server server(80);
char client_req[32];

void setup() {
  pinMode(10, OUTPUT);

  if (!SD.begin(8)) {
    while (1)
    {

    }
  }
  
  WiFly.beginAdhoc();

  Serial.begin(9600);
  
  server.begin();
}

void loop() {
  Client client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    boolean have_req = false;
    uint8_t i = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        if (!have_req)
        {
          client_req[i] = c;
          
          i++;
          
          if (i >= 32) 
            i = 32 - 1;
        }
        // if we've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so we can send a reply
        if (c == '\n' && current_line_is_blank) {
          // send a standard http response header

          if (strstr(client_req, "GET / ") != 0)
          {
            htmlSuccess(client); 
            sendFile(client, "index.htm");
          }
          else if (strstr(client_req, "GET /") != 0)
          {
            char* filename;
            htmlSuccess(client);
            
            (strstr(client_req, " HTTP"))[0] = 0;
            filename = client_req + 5;
            
            if (strcmp(filename, "getvolts") == 0)
            {
             <Insert a call to your desired function here>
            }
            else
            {
             sendFile(client, filename); 
            }
          }
          else
          {
            htmlNotFound(client);
          }
        }
        if (c == '\n') {
          // we're starting a new line
          current_line_is_blank = true;
          client_req[i - 1] = 0;
          have_req = true;
        } else if (c != '\r') {
          // we've gotten a character on the current line
          current_line_is_blank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(100);
    client.stop();
  }
}

void htmlSuccess(Client &client)
{
  char c_buff[32];

  strcpy_P(c_buff, (char*)pgm_read_word(&(client_response[0])));
  client.println(c_buff);
  strcpy_P(c_buff, (char*)pgm_read_word(&(client_response[6])));
  client.println(c_buff);
  client.println();
}

void htmlNotFound(Client &client)
{
  char c_buff[32];
  
  strcpy_P(c_buff, (char*)pgm_read_word(&(client_response[1]))); // Transfer string to SRAM from FLASH 
  client.println(c_buff);
  strcpy_P(c_buff, (char*)pgm_read_word(&(client_response[6])));
  client.println(c_buff);
  client.println();
  strcpy_P(c_buff, (char*)pgm_read_word(&(client_response[2])));
  client.println(c_buff);
}

void sendFile(Client &client, char *filename)
{
  File page;
  uint8_t b_buff[64];
  uint8_t size;
  
  page = SD.open(filename);
            
  if (page)
  {
    while (page.available() > 0)
    {
      while ((size = page.read(b_buff, sizeof(b_buff))) > 0)
      {
        client.write(b_buff, size);
      }
    }  
  } 
}

I serve all of my html off of an SD card, so your implementation will be slightly different.

The portion you will be interested in is this:

 if (strstr(client_req, "GET / ") != 0)
          {
            htmlSuccess(client); 
            sendFile(client, "index.htm");
          }
          else if (strstr(client_req, "GET /") != 0)
          {
            char* filename;
            htmlSuccess(client);
            
            (strstr(client_req, " HTTP"))[0] = 0;
            filename = client_req + 5;
            
            if (strcmp(filename, "getvolts") == 0)
            {
             <Insert a call to your desired function here>
            }
            else
            {
             sendFile(client, filename); 
            }
          }
          else
          {
            htmlNotFound(client);
          }

Basically, If I receive a request for the home page, I open and send index.htm.

If I receive a request for a file, I open and send the file.

If I receive a ‘getvolts’ request, I call a function to build a string to send to the browser.

You must send a header with every response to the browser. That is how the browser knows what to do with the data. The javascript function above looks for a 200 OK Success Header. If that does not appear it will not work.

Thanks for sharing your code CapacitiveMind. I’ve put quite a few hours into this since your post.

There are two things that simply are not adding up for me. First of all, I can’t appear to make a function call. If inside the “if…” statement I simply insert the many lines of html code, the page loads fine. However, if I insert a function such as “mainPage(client)” and inside “mainPage” is a the same exact html code, the page never loads. In fact, it appears to reboot the arduino every time the page is called. I’ve tried many times with different code in an attempt to make it work with functions with no success. I can live with that issue, the code simply is a bit messy. Second, I’m able to make the page load successfully, I’m able to push the button to execute the javascript timer and every 8 seconds the request header pushes out a “GET /getvolts HTTP/1.1” and the response is “HTTP/1.1 200 OK” but the code to pull the analog values never executes. I cannot get the code to parse the two “if” statements. Please see the screen shot below…

I’ve referenced the code provided above as well as the example file but the differences are enough to throw me off. In addition, I needed to add “client.stop();” at the end of the main page load in order to keep browser from continuing to search. Any further help would be much appreciated. I will contiue to troubleshoot away!

Ok…so I made some good progress and I’m really close to a working project. The following are some things that I recently learned…

  1. Capacitive Mind, I’m not sure how your code worked without a “break” after the
Serial.println(filename);

My code would only work with a “break” in the following places…

        if (c == '\n' && current_line_is_blank) {

          if (strstr(client_req, "GET / ") != 0) // strstr will say if it contains the string 
          {
            htmlSuccess(client); //header
            sendFile(client);
            break;
          }
          else if (strstr(client_req, "GET /") != 0)
          {
            char* filename;
            htmlSuccess(client);//header
            
            (strstr(client_req, " HTTP"))[0] = 0;
            filename = client_req + 5;
            if (strcmp(filename, "getvolts") == 0)
            {
             String stringOne =  String(analogRead(0), DEC); 
             //String stringTwo = String(analogRead(1), DEC);
             //String stringThree = String(analogRead(2), DEC); 
             //String stringAll = stringOne + "," + stringTwo;
             client.println(stringOne);
             break;
            }
            else
            {
            htmlSuccess(client); //header
            sendFile(client);
            break;
            }
            break;
          }
          else
          {
            htmlNotFound(client);
            break;
          }

With that change I am now able to execute the javascript and every 8 seconds

which will then execute the analog read after the server sees a “getvolts”

the header. I could not get the code to work when I put the analog reads

inside of a function and called the function. I had to call the analogs

within the if statement, direct. In addition, I was never able to get 3

analogs to read. At most, only 2 and even then I would occasionally only

recieve one analog input.

As mentioned, I’m really close as I am able to get an analog value in my

HTTP response content every 8 seconds. The only thing that I cannot get

working yet is that the value will not appear in the text boxes on the screen.

The values appear in an alert box every 8 seconds, prooving that the information

is there, I just can’t get it to the text boxes (see below)…

client.println("<meta name=viewport content=width=device-width, initial-scale=2.3, maximum-scale=1, user-scalable=no>");
    client.println("<body style=background-color:#C0C0C0>");
    client.println("<head>");
    client.println("Analog In 1:");
    client.println("<script type='text/javascript'>");
    client.println("function getVolts(){");
    client.println("var xmlhttp;");
    client.println("var reply;");
    client.println("var t;");
    client.println("if (window.XMLHttpRequest){");
    client.println("xmlhttp=new XMLHttpRequest();}else{");
    client.println("xmlhttp=new ActiveXObject('Microsoft.XMLHTTP');}");
    client.println("xmlhttp.onreadystatechange=function(){");
    client.println("if (xmlhttp.readyState==4 && xmlhttp.status==200){");
    client.println("reply=xmlhttp.responseText.split(',');");
    //client.println("alert(reply[0]);");
    client.println("reply[0]=parseInt(reply[0],16);");
    client.println("reply[1]=parseInt(reply[1],16);");
    client.println("reply[2]=parseInt(reply[2],16);");
    client.println("reply[0] = reply[0] * 0.1953;");
    client.println("document.getElementById('dcVolts').innerHTML=reply[0];");
    client.println("document.getElementById('dcCurrent').innerHTML=reply[1];");
    client.println("document.getElementById('dcTemp').innerHTML=reply[2];}}");
    client.println("xmlhttp.open('GET','getvolts',true);");
    client.println("xmlhttp.send();");
    client.println("t = setTimeout('getVolts()', 6000);}");
    client.println("</script>");
                      
    client.println("</head>");
    client.println("<form>");
    client.println("<input type='button' value='Display value' onClick='getVolts()' />");
    client.println("<input type='text'id='dcVolts'/>");
    client.println("<input type='text'id='dcCurrent'/>");
    client.println("<input type='text'id='dcTemp'/>");
    client.println("</form>");
    client.println("</body></html>");

SUCCESS! I was finally able to make this work. A couple of things to note. The key to the server side code was the above which included the “breaks” in the appropriate places. The second was that the javascript that I originally had was wrong. At first I used “document.getElementById(‘dcTemp’).innerHTML=reply[0]” but what I really needed was "document.getElementById(‘dcTemp’).value=reply[0].

Another thing to note. I was never able to get all three analog values to read. With only two it works fine. With a PC using either IE or Firefox I am able to have an update rate of 2 seconds but with the iphone it needs to be 4 seconds in order to work.

Thanks for the help CapacitiveMind!