wifi web server that doesn't slow down a robot?

I have been experimenting with the example of the wifi webserver. I altered it so that it outputs data from some sonar ping sensors on my robot. What I don't understand is how to write or modify a version that can be incorporated into the void loop of my robot, pass data to my computer of the net (gotten there so far), but not slow down the robot tremendously. I imagine the routines in the web server example where it waits for the client before disconnecting

delay(1);
// close the connection:
client.stop();

is a significant pause in the program.

It has a wait 5 seconds portion as well.

client.println("Refresh: 5"); // refresh the page automatically every 5 sec

Is there an example of code that can pass data out to the wifi, remain connected, and not cause delays in the code? I am using the newping function in my code to read three or more sonic sensors for distance, and any delays will cause a malfunction.

This is the example program from the ethernet library that I am using:

/*
Web Server

A simple web server that shows the value of the analog input pins.
using an Arduino Wiznet Ethernet shield.

Circuit:

  • Ethernet shield attached to pins 10, 11, 12, 13
  • Analog inputs attached to pins A0 through A5 (optional)

created 18 Dec 2009
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe

*/

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}

// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}

void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("");
client.println("");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("
");
}
client.println("");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}

This wasn't your first post. Where is the code tag?

To not lock up your robot while doing server stuff, you must rewrite the current code with state machine. I used wifi shield as server a year ago with then latest firmware. The firmware was not stable so I gave up after a lot of tries.

I imagine the routines in the web server example where it waits for the client before disconnecting

The 1ms delay on the server code is not a lot and will only occur every five seconds. The "client.println("Refresh: 5");" is for the browser and has no effect on the server except have the browser refresh the page every 5 seconds.

So the problem likely lies elsewhere than some ill perceived delay in the code?

I tried to post the entire sketch, but it said that it exceeds 9500 characters.

liudr:
This wasn't your first post. Where is the code tag?

To not lock up your robot while doing server stuff, you must rewrite the current code with state machine. I used wifi shield as server a year ago with then latest firmware. The firmware was not stable so I gave up after a lot of tries.

Code tag? Sorry, i don't understand.

Any suggestions on a more stable method to output data via ethernet?

#include<Servo.h>

//Declare Servos
Servo steerservo;    //steering servo
Servo throttleservo;   //drive servo
const int turntime=0; //number of milliseconds to hold turn when turning
const int turnaroundtime=0; // number of milliseconds to turn completely around
const int steerservopin=22;  //pin number for steering servo
const int throttleservopin=24;  //pin number for throttle servo


const int frontdistlimit=60;// starts to turn
const int sidedistlimit=35;// starts to turn

const int backupdistlimit=40; //backs up when this close to the front sensor
const int sidebackupdistlimit=20; //backs up when this close to a side sensor

#include <NewPing.h>

#define SONAR_NUM     3 // Number or sensors.
#define MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(40, 41, MAX_DISTANCE), // Centre sensor (0)  Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(42, 43, MAX_DISTANCE),// Left sensor (1)
  NewPing(44, 45, MAX_DISTANCE), //Right sensor (2)
  //NewPing(21, 22, MAX_DISTANCE),
  //NewPing(23, 24, MAX_DISTANCE),
  //NewPing(25, 26, MAX_DISTANCE),
  //NewPing(27, 28, MAX_DISTANCE),
  //NewPing(29, 30, MAX_DISTANCE),
  //NewPing(31, 32, MAX_DISTANCE),
  //NewPing(34, 33, MAX_DISTANCE),
  //NewPing(35, 36, MAX_DISTANCE),
  //NewPing(37, 38, MAX_DISTANCE),
  //NewPing(39, 40, MAX_DISTANCE),
  //NewPing(50, 51, MAX_DISTANCE),
  //NewPing(52, 53, MAX_DISTANCE)
};


//Setup function.  Runs once when Arduino starts or is reset

//webserver components of initialization

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0x08, 0x57, 0x00, 0x41, 0x3d, 0x55 };
IPAddress ip(192,168,0,155);
EthernetServer server(80);
// end of webserver init

void setup() { 

  Serial.begin(9600);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

  steerservo.attach(steerservopin);  // attaches the steering servo to its pin 12
  throttleservo.attach(throttleservopin);  // attaches the throttle servo to its pin 13
  delay(1000); // delay one second

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

} 

void loop(){

  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 1000;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).

    }
  }
   communicate();
  // the rest of your code would go here
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");

    Serial.print("Front centre distance is  ");
    Serial.print(cm[0]);
    Serial.println(" cm");

    Serial.print("Left  sensor distance is ");
    Serial.print(cm[1]);
    Serial.println(" cm");

    Serial.print("Right sensor distance is ");
    Serial.print(cm[2]);
    Serial.println(" cm");

  }
  Serial.println();

  if(cm[0]<frontdistlimit || cm[1]<sidedistlimit || cm[2]<sidedistlimit) { 

    char turndirection=scan(); // Goes to scan function to decide which way to turn based on l,r,s values.
    switch (turndirection){
    case 'l':
      turnleft(turntime);
      //delay(turntime);
      break; // exits the case
    case 'r':
      turnright(turntime);
      //delay(turntime);
      break;
    case 'b':
      backup();
      break;
    case 's':
      spin();
      break;
    }
  }
  else 
  {
    go();
  }
}

//Driving the servo motors
void go(){
  steerservo.write(90); //for now, we don't go anywhere, thus value is 90 for both which is stopped
  throttleservo.write(110);
  Serial.println("Going forward  ");
}
void turnleft(int t){
  steerservo.write(120);
  throttleservo.write(100);
  delay(turntime);
  Serial.println("Turning Left ");
}
void turnright(int t){
  steerservo.write(60);
  throttleservo.write(100);
  delay(turntime);
  Serial.println("Turning Right ");
}
void backup(){
  steerservo.write(150); // 90 is stop
  throttleservo.write(80);
  Serial.println("Reversed ");
}
void spin(){
  steerservo.write(130);
  throttleservo.write(0);
}
char scan(){

  char choice;
  if (cm[0]<backupdistlimit && cm[1]>sidebackupdistlimit){
    choice='s';
  }
  else if (cm[0]<backupdistlimit && cm[2]>sidebackupdistlimit){
    choice='s';
  }
  else if (cm[0]<backupdistlimit || cm[1]<sidebackupdistlimit || cm[2]<sidebackupdistlimit){
    choice='b';
  }
  else if (cm[1]>cm[2]){
    choice='l';
  }
  else if (cm[2]>cm[1]){
    choice='r';
  }
  else{
    choice='b';
  }
  Serial.print("Choice:  ");
  Serial.println(choice);
  return choice;
}

void communicate(){
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 1");  // refresh the page automatically every 1 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          client.print("Here is a message from Tim's Arduino Mega Rover");
          client.println("
");

          // output some ping data
          for (uint8_t i = 0; i < SONAR_NUM; i++) {
            client.println(i);
            client.println("=");
            client.println(cm[i]);
            client.println("cm ");

            client.println("Front centre distance is  ");
            client.println(cm[0]);
            client.println(" cm");

            client.println("Left  sensor distance is ");
            client.println(cm[1]);
            client.println(" cm");

            client.println("Right sensor distance is ");
            client.println(cm[2]);
            client.println(" cm");

            // end of ping function

              // output the value of each analog input pin
            for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
              int sensorReading = analogRead(analogChannel);
              client.print("analog input ");
              client.print(analogChannel);
              client.print(" is ");
              client.print(sensorReading);
              client.println("
");       
            }
            client.println("</html>");
            break;
          }
          if (c == '\n') {
            // you're starting a new line
            currentLineIsBlank = true;
          } 
          else if (c != '\r') {
            // you've gotten a character on the current line
            currentLineIsBlank = false;
          }
        }
      }
      // give the web browser time to receive the data
      //delay(1);// edited out delay
      // close the connection:
      client.stop();
      Serial.println("client disonnected");

    }

  }
  // */
}

My latest observations on this web server is that it doesn't impair the robot, but, for some reason will no longer connect to a client, when it is made as a function run in the void loop. On its own, it runs for hours without losing connection, refreshing when it is supposed to, and relaying the data back to the client.

If anyone has any other web based communication ideas, I am all ears. I'm looking for a way to read data off the robot sensors and send it back by ethernet. I am currently running an ethernet card that runs into a wireless router that I have programmed with open wrt to act as a client bridge back to the main wireless router on our home network. This works very well, but I haven't figured out how to make it part of the autonomous routine and not have either the communication function hold up the fluidity of movement of the robot, or have the robot movement code interfere with the web server, which is what appears to be currently happening.

You probably need to get rid of any delays in the bot control code. Any delays or loops there may prevent the server portion of the code from receiving a client request.

OP is sensing all the time, is it correct? You should run timers to only sense when timer expires and use the rest of the time for server.

I have added serial printed comments to the various loops to see how many times they run. I see 14-25 runs of the main void loop before the timers on the sensors run their time and produce new values, resulting in the distances being printed. Each run of the void loop, the "communicate ()" function runs. But it never connects.

So I'm not sure if this means anything, but at about a 14 to 25 to one ratio, the loop with the ethernet communication runs, compared to the timers for the sonic sensors producing new values and serial printing it.

Even if I could serial print the data to another device that handles the web communications, that would be good. Perhaps even a good strategy? A second arduino purely for a web server?

Then the following line must be taking a lot of milliseconds:

sonar[currentSensor].ping_timer(echoCheck);

Put Serial.println(millis()); before and after it to see how much time it costs. Are you using any interrupt to catch the ping?

I suggest you put the web server code in the main loop, and put the bot control code in conditional functions. Any significant delays need to check current time vs past time to avoid delays in program execution.

zoomkat:
I suggest you put the web server code in the main loop, and put the bot control code in conditional functions. Any significant delays need to check current time vs past time to avoid delays in program execution.

I don't think so. If the web browser misbehaves etc. then the connection will stay alive over a period of time too long for the robot to roam without sensing its environment. As I suggested already, OP should use state machine to take in each client byte at a time and NOT to run the server code like nothing else matters!

liudr:
Then the following line must be taking a lot of milliseconds:

sonar[currentSensor].ping_timer(echoCheck);

Put Serial.println(millis()); before and after it to see how much time it costs. Are you using any interrupt to catch the ping?

I don't know what code is inside the Newping.h, but the timing uses millis count rather than delays to trigger and listen for the echo. The values are stored in an array and available for access for decision making. It works very slickly for gathering distances near continuously as the robot drives forward and making turn decisions on the fly, without stopping and taking measurements.

It "looks" fast, but obviously it isn't in the context of running the web server before or after it.

zoomkat:
I suggest you put the web server code in the main loop, and put the bot control code in conditional functions. Any significant delays need to check current time vs past time to avoid delays in program execution.

Thanks zoomkat, I really appreciate your assistance. I see you have done some very cool things with robotics and internet combined. I'm very new to writing programs. Is there any difference between me having put the web server code in a function, and calling up the function in the main loop, as opposed to putting the web server code in the main loop? In my inexperience, I thought it would be the same thing, as long as accessing the function wasn't in any way conditional? Yes/No?

You might time and print the millisecond value of each loop to the serial monitor to see how long a single loop is taking. Most browsers will wait a couple of seconds for a server to respond to request. A test might be to remove any delays in the bot code to see if the server will start responding to request (the delay removal should not matter if all hardware is disconnected for testing).

Is there any difference between me having put the web server code in a function, and calling up the function in the main loop

Not really and it makes your code cleaner. There is overhead associated with calling a function but it's negligible. If you get to the point where that overhead is a problem, there's always the 'inline' directive to get the best of both worlds.

liudr:

zoomkat:
I suggest you put the web server code in the main loop, and put the bot control code in conditional functions. Any significant delays need to check current time vs past time to avoid delays in program execution.

I don't think so. If the web browser misbehaves etc. then the connection will stay alive over a period of time too long for the robot to roam without sensing its environment. As I suggested already, OP should use state machine to take in each client byte at a time and NOT to run the server code like nothing else matters!

The arduino web servers are fairly robust when being connected to a well designed browser client. The OP's server code apparently runs on its own without issues. Adding the robot activities appear to be where the issues start. For trouble shooting I usually start with code that is known to work and slowly add discrete portions of new code until the code fails. For this project I'd start with the web server as the basic operation and then add a single bot activity at a time to the code and see if the combined code fails after a specific addition. Finding the point of failure is key to finding why the failure is occurring.

What is basically going on in that server example when it comes to the client connecting and disconnecting? Obviously the client initiates the connection, but after that, is it the server that takes control of the connection? How long will it stay "connected" without activity? Who or what initiates the disconnect? What goes on in the refresh? It appears to be a fresh reconnect.

Can I connect at some early stage, stay connected, start the bot controls and sensing and be more efficient?

Google is your friend for gaining knowledge of tcp/ip connections and http protocol. I set my code up such that the web server code is first just as a personal preference for following the code flow. When the server input buffer has data, the server code is run to provide the client with what is needed and then closed. Then the code flow moves on to other operations code being run. Does your bot code run as expected without the server code being included? Also, you probably need to detail what type of arduino you are using and what type of wifi hardware you are using (wifi shield or Ethernet shield connecting to a router). The servo pins 22 and 24 indicate you are not using a uno or similar board (mega?). Also, what pins are used for the sensors and what is the interrupt usage of the #include NewPing.h library? There may be conflicts between the NewPing.h library and Ethernet.h/SPI.h libraries.