pauses in data

Discussion about any of the included sketches (i.e. WebServer), or user-generated applications.

pauses in data

Postby joemarshall » Tue Aug 10, 2010 8:16 am

I've written a sketch, based on the socket demo, which reads data off the arduino analog pins, and sends it to a TCP stream every time the stream is polled by the remote server (by the server sending a character to the wishield).

It works fine 99% of the time, but about once every 400 readings, it suddenly pauses for a little bit, sometimes almost half a second, then resumes. I'm not getting any retransmission requests or anything, just a pause in the arduino sending stuff.

Any ideas why this might be happening? It is really annoying - I am so close to a working system, but this pause messes everything up.

Joe
joemarshall
 
Posts: 3
Joined: Tue Aug 10, 2010 8:08 am

Re: pauses in data

Postby GregEigsti » Tue Aug 10, 2010 2:33 pm

Can you be sure that the pause is not occurring on the remote server side - e.g. a hiccup sending out its poll request?
How often does the remote server poll the Arduino/WiShield?
Posting your Arduino/WiShield sketch is always helpful for others to look for potential issues.

Greg
Check out the wiki!
uIP Stack Docs
Compatible Access Point List
WiShield user contrib branch - DNS, DHCP, AP Scanning, bug fixes, etc.
SlackLab.org - My geek projects blog.
User avatar
GregEigsti
 
Posts: 1067
Joined: Sun Aug 02, 2009 5:23 pm
Location: Sammamish WA USA (near Seattle)
  • Website

Re: pauses in data

Postby joemarshall » Wed Aug 11, 2010 3:45 am

The server is sending out a poll 10 or 20 times a second (I've tried both, and both have the pauses). I'm confident that the polling code is good - I've cut it down to a pretty basic python script, and there is no reason it should pause at all. Here is the sketch.

Code: Select all
    /*
    * Socket App
    *
      * A simple socket application example using the WiShield 1.0
    */

    #include <WiShield.h>
    #include <string.h>
extern "C" {   
    #include <uip.h>
}

//    extern "C" int connectToServer(void);
       
    //Wireless configuration defines ----------------------------------------
    #define WIRELESS_MODE_INFRA   1
    #define WIRELESS_MODE_ADHOC   2

    // Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,1,3};   // IP address of WiShield [change]
unsigned char gateway_ip[] = {192,168,1,1};   // router or gateway IP address spacemedia= [change]
unsigned char subnet_mask[] = {255,255,255,0};   // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"maskomatic"};      // max 32 bytes [change]

unsigned char security_type = 2;   // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"mykeyishere"};   // max 64 characters


// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A,   // Key 0
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   // Key 1
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   // Key 2
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00   // Key 3
            };

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
//unsigned char wireless_mode = WIRELESS_MODE_ADHOC;
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;

// End of wireless configuration parameters ----------------------------------------

    boolean connectToServer(void);
   
    char            header[] = "INIT Mask 1";
    unsigned int    xmitCount = 0;

    boolean         connectedToServer = false;
    boolean         inited = false;       
    unsigned long   nextXMitTime=0;
   
    unsigned long lastXMitTime=0;
   
    // I'm paranoid and give my buffers a few extra bytes.
    char        buffer[300];
   
    void setup()
    {
       Serial.begin(9600);       
       Serial.println("starting");             
       connectedToServer = false;             
       WiFi.init();
       Serial.println("wifi INIT");             
       
    }

    void loop()
    {

       WiFi.run();
     
       
       if (millis() > nextXMitTime && inited && !connectedToServer) {
          Serial.print("Connecting to server... ");
          xmitCount = 0;
          if (connectToServer()) {
              connectedToServer = true;
              Serial.println("start connection OK");
          }
          else {
              Serial.println("start connection NOT OK");
          }
       }                 
    }
   
    // Make the initial connection to the remote server at 192.168.0.2
    boolean connectToServer(void)
    { 
       struct uip_conn *conn;
       uip_ipaddr_t ipaddr;
       
       uip_ipaddr(&ipaddr, 192,168,1,100); //[change]
       conn = uip_connect(&ipaddr, HTONS(49990));
       
     
      return (conn != NULL);
    }
   
    // The 'extern "C"' allows these functions to be called directly by the framework.  You see the framework is
    // written in 'C' and Arduino code is compiled to C++.  The two are similar but not the same.
   
    // It's easer to have these functions here in the Arduino code because:
    // (1) You can use Serial.println commands
    // (2) Integration with other Arduino code is easier.
    // (3) No need to pass strings back and forth in large buffers.  String processing can be done directly on RX values

    extern "C" {

        // Called by the framework when framework is ready.
        // This function was previously in a "C" file.
        // Lets me know the app has been inited and is ready to connect.               
        void socket_app_init() {
            Serial.println("inited!!!");
            inited = true;
            nextXMitTime = millis();
        }
       
        // Called by the framework when there's an event which needs attention
        // Here's where the work gets done.
        void socket_app_appcall() {
//            if( uip_poll() ){
//              Serial.println("poll");
//            }
            // A connection was just made and it's OK to send data.
            if (uip_connected()) {
                Serial.println("{connected}");
                uip_send(header, strlen(header));
            }
            // connection was broken.  Perhaps server went down or cable unplugged?  Retry immediately
            if (uip_aborted()) {
                Serial.println("{aborted}");
                connectedToServer = false;
//                nextXMitTime = millis()+1000;
            }
            // connection timed out.  Not good but we can try again.  Retry immediately
            if (uip_timedout()) {
                Serial.println("{timedout}");
                connectedToServer = false;
            }   
           
            // connection was closed.  This is usually expected after the transaction is complete
            // It seems we'll get this once with the UIP_NEWDATA flag set, and then again with the
            // UIP_NEWDATA cleared.  We only want to advance 'nextXMitTime' once, so we ignore the
            // UIP_CLOSED unless it has no new data
            if(uip_closed() && !uip_newdata()) {
                Serial.println("{closed}");
               connectedToServer = false;
           }   
                     
           // An error occurred and we need to retransmit.
           if (uip_rexmit()) {
               Serial.println("{retransmit}");
               uip_send(buffer, strlen(buffer));                       
           }       
           
          int counter =0;
            // Send packet when we get a poll from the server
            if( uip_newdata()){
             
                    // Inhale Sensor         
                  int analogValue = analogRead(0);
                 
                 //Exhale Sensor
                 int analogValue2 = analogRead(1);
               //  Serial.println(analogValue);
                 
               
                  //SLO
                  //sprintf(reading_buffer, "reading number:  <%04d> ", counter);
                  sprintf(buffer, "%6d,%4d,%4d", xmitCount, analogValue2, analogValue);

                 
                 xmitCount += 1;
                 uip_send(buffer, strlen(buffer));         
             
           }

                       
        }
    }           


Is there anything obviously wrong with it?

I think I maybe should try using UDP instead, as it is really the right protocol for what we're doing (it'll just be a slight pain with some other code I need to work with), but it would be nice to know what I'm doing wrong.

cheers,

Joe
joemarshall
 
Posts: 3
Joined: Tue Aug 10, 2010 8:08 am

Re: pauses in data

Postby GregEigsti » Wed Aug 11, 2010 1:46 pm

I don't see anything obvious in your code... One thing you may try is to decrease the uIP stack poll timer timeout - this has the side effect of making the entire stack "more responsive" (or run at a faster rate). I don't have the WiShield source in front of me right now but if you look in stack.c you will see where the poll timer timeout is set; the default value is "(1 second) / 2" - I have found that changing the divisor to 5 or 10 (and if you are brave you can go higher) is safe and causes the stack to be more snappy.

Again, this is all from memory and may or may not work for you - but its something to try... And here is a blatant plug for the user contrib stack ;) In the user contrib stack the poll timeout divisor has been exposed in apps-conf.h for easier one stop shopping (as have some other common user settings).

I'll also caution you about using UDP. In my experience UDP works fine for a short amount of time (8 - 10 hours ???) but then falls over and becomes unresponsive. I have had some luck working around this by occasionally reseting the stack (in my motion detector / kid catcher project) however this was not satisfactory for my RGB LED kitchen lighting project - which I switched to TCP sockets and it has been flawless...

Anyway, some food for thought and I hope that it helps!
Greg
Check out the wiki!
uIP Stack Docs
Compatible Access Point List
WiShield user contrib branch - DNS, DHCP, AP Scanning, bug fixes, etc.
SlackLab.org - My geek projects blog.
User avatar
GregEigsti
 
Posts: 1067
Joined: Sun Aug 02, 2009 5:23 pm
Location: Sammamish WA USA (near Seattle)
  • Website

Re: pauses in data

Postby spidermonkey04 » Thu Aug 12, 2010 11:42 am

Is it an ARP request? I've seen the self arp request (for periodic data to stay connected) fire right in the middle of sending data. I think it fires every 30 sec no matter what, when the timer should be reset if any activity is going on.

---Jared
spidermonkey04
 
Posts: 66
Joined: Thu Oct 29, 2009 6:45 pm

Re: pauses in data

Postby GregEigsti » Sat Aug 14, 2010 5:08 pm

Good point on the ARP timer goop. I am not familiar with that code - could you post some pointers to where the ARP timer is set and where it fires? Yeah, yeah, I know ;)

Greg
Check out the wiki!
uIP Stack Docs
Compatible Access Point List
WiShield user contrib branch - DNS, DHCP, AP Scanning, bug fixes, etc.
SlackLab.org - My geek projects blog.
User avatar
GregEigsti
 
Posts: 1067
Joined: Sun Aug 02, 2009 5:23 pm
Location: Sammamish WA USA (near Seattle)
  • Website

Re: pauses in data

Postby spidermonkey04 » Sun Aug 15, 2010 11:54 am

I like to watch the connection in WireShark, where it'll say 'Gratuitous ARP (Reply)' when this happens. The timers are handled in stack.c and fired near the bottom of stack_process(). Normally, the self_arp_timer is fired when 30 sec has passed and there is nothing to TX. That's usually fine, except when you have multiple packets to send off. In this case you would send a packet and then wait for the ACKnowledgement before making the next packet. If the timer just so happens to fire while you are waiting(nothing TX'ing), then the WiShield will send out the ARP packet first. So that COULD be a cause for some delay.
Here's a small change I've been testing for only a few hours, but seems like it works. I'm only adding a 'timer_restart()' in there ( NOT timer_reset() ).
Code: Select all
if(uip_len > 0) {
         if(BUF->type == htons(UIP_ETHTYPE_IP)){
            uip_arp_ipin();
            uip_input();
            if(uip_len > 0) {
               uip_arp_out();
               network_send();
            }
         }else if(BUF->type == htons(UIP_ETHTYPE_ARP)){
            uip_arp_arpin();
            if(uip_len > 0){
               network_send();
            }
         }
            timer_restart(&self_arp_timer);
      }else ...

This will restart the 30 second timer upon activity. I'm not sure if it would be beneficial/necessary to also add this to the UDP polling loop...

---Jared
spidermonkey04
 
Posts: 66
Joined: Thu Oct 29, 2009 6:45 pm

Re: pauses in data

Postby GregEigsti » Sun Aug 15, 2010 12:23 pm

This will restart the 30 second timer upon activity. I'm not sure if it would be beneficial/necessary to also add this to the UDP polling loop...

Excellent! Resetting the timer is kinda what I was thinking but I have to claim ignorance on the subject so... Probably would make sense to add it to the UDP side of the house as well. One more question... Should the ARP timer be reset when the WiShield receives data or will the ACK that is sent, for TCP, cause this TX code to be executed?

Maybe I'll finally test out the "too big packet fix" today ;) The daughter is away at a friends, the boy is in a good mood and keeping himself busy and the wife is allowing a low key day! Well between my kitchen cleaning duties - and I'm almost done ;)

Thanks!
Greg
Check out the wiki!
uIP Stack Docs
Compatible Access Point List
WiShield user contrib branch - DNS, DHCP, AP Scanning, bug fixes, etc.
SlackLab.org - My geek projects blog.
User avatar
GregEigsti
 
Posts: 1067
Joined: Sun Aug 02, 2009 5:23 pm
Location: Sammamish WA USA (near Seattle)
  • Website

Re: pauses in data

Postby spidermonkey04 » Sun Aug 15, 2010 4:56 pm

Yup. Anything RX'ed will execute this code. Actually, all things TCP are handled right there too. So I was more worried about TX'ing, because UDP can still TX stuff outside this chunk of code. And I'll admit, I still haven't even attempted a UDP application. The reliability of TCP is just too nice. BUT, I think I understand it all and this should do the trick.
Code: Select all
for(i = 0; i < UIP_UDP_CONNS; i++) {
   uip_udp_periodic(i);
   if(uip_len > 0) {
      uip_arp_out();
      network_send();
      timer_restart(&self_arp_timer);
   }
}

I believe that's the only place UDP work is done, so that should cover all the bases. And while I'm on the subject, a small optimization. In timer.c/.h, the return value of timer_expired() can be changed from an int to a char, to save a few bytes.

---Jared
spidermonkey04
 
Posts: 66
Joined: Thu Oct 29, 2009 6:45 pm

Re: pauses in data

Postby GregEigsti » Sun Aug 15, 2010 5:12 pm

the return value of timer_expired() can be changed from an int to a char, to save a few bytes.

Yeah there are lots of places in the uIP stack where a byte can be saved here or there... Kind of interesting given that uIP is designed for memory constrained devices.

EDIT - and speaking of "fat"; I have been noticing that the user contrib stack is dragging in too much; will need to do some work to make apps built with it smaller...

Thanks for the description/explanation! I'm gonna diff your "packet too large" change with what is in the user contrib stack and make the appropriate change and test it out. Its pretty easy to send a large packet and muck things up - to repro the case to test it out.

Thanks!
Greg
Check out the wiki!
uIP Stack Docs
Compatible Access Point List
WiShield user contrib branch - DNS, DHCP, AP Scanning, bug fixes, etc.
SlackLab.org - My geek projects blog.
User avatar
GregEigsti
 
Posts: 1067
Joined: Sun Aug 02, 2009 5:23 pm
Location: Sammamish WA USA (near Seattle)
  • Website

Next

Return to Sketches and Applications

Who is online

Users browsing this forum: No registered users and 1 guest