Go Down

Topic: Non-blocking Server (Read 1 time) previous topic - next topic

KirAsh4

Mar 08, 2012, 10:20 am Last Edit: Mar 08, 2012, 10:25 am by KirAsh4 Reason: 1
I finally got a moment to test out my Ethernet shield.  First issue was that I have a rev.3 shield, and a rev.2 Uno ... ah so it has 4 extra pins.  Covered them up and plugged it in.  Voila.

I was able to bring the shield up through DHCP, no problem.  Next I configured it as a server and tried a simple query and response sketch.  Loaded up the IP in my browser and sure enough, there's a response being sent back.  So far so good.  Now on to what I really need to do, which involves a servo.

I connected the servo, wrote a function that sweeps it, and I called the function from within the main loop.  The idea is that while the servo is sweeping back and forth, I can load it up in a browser and it tells me at what angle the servo is at (at the moment the browser sent the request.)  I can hit refresh and watch the angle change.  Great ... except, I noticed it's blocking the sweep.  Every time I hit refresh, the servo stops for a brief moment, then continues.

I can't wrap my head around this one ... I need to be able to request the servo position without affecting the servo itself.  I know it's not the servo.read() call.  When I comment it out, I still notice the brief pause.  So that tells me it's the process of sending data back to the browser that causes the blocking.  Is there a way to prevent that?  Ultimately I'm going to be reading two servos, probably at 10Hz resolution, and I don't want to block the servos from what they're doing.

Suggestions?  Here's the mandatory code:
Code: [Select]
#include <Servo.h>
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x03, 0x00, 0x10 };
EthernetServer server = EthernetServer(80);

// Initialize Servo
Servo myServo;
int pos = 0;
long prevMillis;
int dir = 1;
int pause = 5;

void setup() {
 Serial.begin(9600);
 myServo.attach(9);

 if (Ethernet.begin(mac) == 0) {
   Serial.println("Failed to configure Ethernet using DHCP");
   // no point in carrying on, so do nothing forevermore:
   for(;;)
     ;
 }
 Serial.print("IP: ");
 Serial.println(Ethernet.localIP());
 server.begin();
}

void loop() {
 // listen for incoming clients
 EthernetClient client = server.available();
 servoSweep();
 if (client) {
   boolean currentLineIsBlank = true;
   while (client.connected()) {
     if (client.available()) {
       char c = client.read();
       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();
         client.print("servo is at: ");
         client.print(myServo.read());
         client.println(" degrees.");
         break;
       }
       if (c == '\n') {
         currentLineIsBlank = true;
       }
       else if (c != '\r') {
         currentLineIsBlank = false;
       }
     }
   }
   delay(1);
   client.stop();
 }
}

void servoSweep(void) {
 if (millis() - prevMillis > pause) {
   if (dir) {
     if (pos < 180) {
       pos += 1;
       pause = 5;
     } else {
       dir =! dir;
       pause = 1000; // Pause for 1 second
     }
   } else {
     if (pos > 0) {
       pos -= 1;
       pause = 5;
     } else {
       dir =! dir;
       pause = 1000; // pause for one second
     }
   }
   myServo.write(pos);
   prevMillis = millis();
 }
}


PS: That code generates 15340 bytes.  Talk about big ... doesn't leave much room for other stuff I need to be doing, like adding Wire support to talk to another controller for starters.  And there will be more libraries to be added ...

Targettio

I would imagine it is the delay(1); which is causing the pause, as delay basically stops the arduino.

Try taking the delay out and see what happens. You ought to avoid using delays wherever possible.

PaulS

Quote
I need to be able to request the servo position without affecting the servo itself.  I know it's not the servo.read() call.

I don't know how you expect to be able to accomplish this. It takes time to accept and process a client request. Obviously, during that time, the servoSweep() function is not being called. You might see less impact on the servo movement if you call servoSweep() often during the processing of the client request.

That delay(), of course, does not help.

doughboy

I don't know the servo library, but it seems like you can just use the value of pos variable instead of calling read(), no?

KirAsh4


I would imagine it is the delay(1); which is causing the pause, as delay basically stops the arduino.

Try taking the delay out and see what happens. You ought to avoid using delays wherever possible.


Nah, I tried that.  The delay(1) stems from ... crap, now I can't remember where I saw it in, but I already removed it.  It is the whole process of receiving the browser request, process, and send data back that causes the servo stop briefly.


I don't know how you expect to be able to accomplish this. It takes time to accept and process a client request. Obviously, during that time, the servoSweep() function is not being called. You might see less impact on the servo movement if you call servoSweep() often during the processing of the client request.


So you're suggesting sprinkling the servoSweep() call inside of the "while (client.connected()) { ... }" loop?


I don't know the servo library, but it seems like you can just use the value of pos variable instead of calling read(), no?


Doubt that will make any difference, it's not the servo call that's causing the blocking.

PaulS

Quote
So you're suggesting sprinkling the servoSweep() call inside of the "while (client.connected()) { ... }" loop?

Yes.

doughboy

#6
Mar 08, 2012, 09:00 pm Last Edit: Mar 08, 2012, 09:04 pm by doughboy Reason: 1
The only thing I can think of is you need to make your servoSweep function be interrupt driven if it is that critical.
or use one of the many timer libraries based on "polling".
you need to make the ethernet server routine "yield" to the servoSweep routine so it can execute.

I have only tried TimerAlarm and Metro libraries.

TimerAlarm - setup the alarm to repeat every x seconds to invoke a function (servoSweep).
within your main loop, just sprinkle the command Alarm.delay(10) at different places.

KirAsh4


Quote
So you're suggesting sprinkling the servoSweep() call inside of the "while (client.connected()) { ... }" loop?

Yes.


Aight, I can do that.  I will more than likely also pull that whole piece into a function on its own, only because there are other bits and pieces that will be added and i prefer dealing with specific functions, rather than trying to read a single loop() that's long.  So I'll stick the servo calls in there as well and hope for the best.

By the way, I'm still baffled at the almost 16K worth of bytes that generated.  I suppose that stems from the Ethernet library?  Is there anyway to make that leaner?  I have this fear that I will hit the 32K mark very fast this way, and I really really, no REALLY, don't want to have to move up to a Mega just for the memory.

KirAsh4


The only thing I can think of is you need to make your servoSweep function be interrupt driven if it is that critical.
or use one of the many timer libraries based on "polling".
you need to make the ethernet server routine "yield" to the servoSweep routine so it can execute.


Well yes, I do understand that part.  It's how to implement it that I don't know.  I'm strictly going by all the library examples online.

I do need to be able to move the servo (or at least send it the instruction to move) regardless of what else might be going on inside the main loop.  Ultimately the whole ethernet call/respose section will be pulled out of the main loop and stuck into it's own function.  And it will simply become another function call just like the servoSweep() one.  However, there will be more functions added in the long run, and even without using a single delay(), they will all eat time time ticks that will cause the servo itself to become jittery since it's own instructions are now slowed down because of other stuff running at the same time.

KirAsh4

Hrm, came across this:
http://scolton.blogspot.com/2010/07/arduino-interrupt-driven-servo-routine.html

Is something like that what you're talking about doughboy?

doughboy

that looks perfect.

I could use that in my project as well, thanks for the link.


KirAsh4

And I just noticed, there are several other servo libraries in the playground, many of which use timers to control the servos.  I think I may have to start experimenting with them, see what works best.  A lot of them are for large quantities of servos, 8 up to 32 or more.  I only need TWO. :)

Go Up