SocketApp CLIENT sample code

Questions specifically related to the TCP/IP stack that interfaces with the driver.

SocketApp CLIENT sample code

Postby benres » Wed Nov 04, 2009 11:12 am

I'd like to use SocketApp as a CLIENT. I want to connect to an IP Address/port, exchange some data, and then disconnect. Very simple. Can someone provide pointers and/or example code? It's not clear to me how to modify the SocketApp to do this.

Thanks,

- Ben
benres
 
Posts: 9
Joined: Sun Nov 01, 2009 1:03 am

Re: SocketApp CLIENT sample code

Postby GregEigsti » Thu Nov 05, 2009 4:05 am

Ben, this is by no means complete nor am I satisfied with it. I am finding that leaving an Arduino/WiShield client connected to a remote server and just having data spewed to it is somewhat problematic. By that I mean after 10-20 minutes the Arduino/WiShield will restart itself. I'm guessing that if your client opens, sends/reads and closes the socket this will not be an issue for you - I think in my case where the socket is left open to accept incoming data ad naseum the uIP stack gets confused and pukes. Anyway, here goes, let me know if you have any questions. I'll leave it up to you to interpret/change this working code (with above caveats). I think that a client which [connects, tx's/rx's, closes] will work well - my issue could very well be due to my handling of the "incoming" data buffer which is used to transfer data between the .c and .pde (.cpp) source files.

Also, you will notice that the socket connect is held off for 10 seconds with the use of MSTimer2; I found that the uIP stack needs a little time to "warm up" before doing a connect. When I tried to connect from setup() / the first thing in loop() / socket_app_init() things broke down very quickly. The 10 second hold off before connecting seems to help.

BTW this code has the Arduino/WiShield connect to an APRS feed and grab incoming packet data. APRS is a HAM radio acronym which stand for Automatic Position Reporting System - basically a way to encode GPS data and send it out via RF for all of us HAM/cartography fans. VSAM is the digi/igate that I run at the house.

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

#include <WiShield.h>
#include <MsTimer2.h>
#include <string.h>


extern "C" void connectToServer(void);

//Wireless configuration defines ----------------------------------------
#define WIRELESS_MODE_INFRA   1
#define WIRELESS_MODE_ADHOC   2

//Wireless configuration parameters ----------------------------------------
unsigned char local_ip[]       = {192,168,0,10};  // IP address of WiShield
unsigned char gateway_ip[]     = {192,168,0,1};     // router or gateway IP address
unsigned char subnet_mask[]    = {255,255,255,0}; // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"SSID"};    // max 32 bytes
unsigned char security_type    = 0;               // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2
// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"12345678"};   // max 64 characters
// WEP 128-bit keys
prog_uchar wep_keys[] PROGMEM = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // 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_INFRA;
unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------


boolean connectedToServer;
volatile boolean intTimer;
int packetCount;
char incoming[1024];


//MsTimer2 timer ISR
void timerISR() {
   intTimer = true;
}

void setup()
{
   //Serial.begin(9600);
   Serial.begin(57600);
   memset(incoming, 0 , 1024 * sizeof(char));
   connectedToServer = false;
   intTimer = false;
   packetCount = 0;
   
   WiFi.init();

   //setup the timerISR to be called every 10 seconds
   MsTimer2::set(10000, timerISR);
   MsTimer2::start();
}

void loop()
{
   int callsignFound;
   WiFi.run();

   //wait 10 seconds after starting up to connect via WiShield
   //allows the WiShield stack code to ready itself...
   if(false == connectedToServer && true == intTimer) {
      Serial.println("Connecting to server...");
      connectedToServer = true;
      intTimer = false;
      MsTimer2::stop();
      connectToServer();
   }

   callsignFound = false;

   if('\0' != incoming[0]) {

      if(NULL != strcasestr(incoming, "VSAM")) {
         Serial.println("********");
         callsignFound = true;
      }
     
      Serial.print(++packetCount);
      Serial.print(" : ");
      Serial.print(incoming);
      incoming[0] = '\0';
   
      if(true == callsignFound) {
         Serial.println("********");
         callsignFound = false;
      } 

   }
}


YourProject.c
Code: Select all

/******************************************************************************

  Filename:      socketapp.c
  Description:   Simple socket programming example for the WiShield 1.0

******************************************************************************

  TCP/IP stack and driver for the WiShield 1.0 wireless devices

  Copyright(c) 2009 Async Labs Inc. All rights reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  Contact Information:
  <[email protected]>

   Author               Date        Comment
  ---------------------------------------------------------------
   AsyncLabs         06/06/2009   Initial version

*****************************************************************************/

/*
* This is a short example of how to write uIP applications using
* protosockets.
*/

/*
* We define the application state (struct socket_app_state) in the
* socketapp.h file, so we need to include it here. We also include
* uip.h (since this cannot be included in socketapp.h) and
* <string.h>, since we use the memcpy() function in the code.
*/
#include "socketapp.h"
#include "uip.h"
//#include "psock.h"
#include <string.h>

struct socket_app_state *s;
//extern int packetCount;
char login[] = "user NOCALL pass 00000 vers Arduino/WiShield\n";
extern char incoming[1024];


void connectToServer(void)
{
   struct uip_conn *conn;
   uip_ipaddr_t ipaddr;
   
   //uip_ipaddr(&ipaddr, 209,160,32,141);
   //conn = uip_connect(&ipaddr, HTONS(14577));
   uip_ipaddr(&ipaddr, 192,168,0,20);
   conn = uip_connect(&ipaddr, HTONS(2023));
   //uip_connect seems to never return NULL even in the case of a non-connect
   if(NULL == conn) {
      memcpy(incoming, "uip_connect failed\n", 19);
   }
   else {
      memcpy(incoming, "uip_connect success\n", 20);
   }
}


/*---------------------------------------------------------------------------*/
/*
* The initialization function. We must explicitly call this function
* from the system initialization code, some time after uip_init() is
* called.
*/
void socket_app_init(void)
{
}


/*---------------------------------------------------------------------------*/
/*
* In socketapp.h we have defined the UIP_APPCALL macro to
* socket_app_appcall so that this function is uIP's application
* function. This function is called whenever an uIP event occurs
* (e.g. when a new connection is established, new data arrives, sent
* data is acknowledged, data needs to be retransmitted, etc.).
*/
void socket_app_appcall(void)
{
   u16_t len;
   
   if(uip_connected()) {
      s = &(uip_conn->appstate);
      PSOCK_INIT(&(s->p), s->inputbuffer, sizeof(s->inputbuffer));
      uip_send(login, strlen(login));
   }

   if(uip_closed()) {
      memcpy(incoming, "Connection Closed...", 20);
   }   

   if(uip_newdata()) {
      len = uip_datalen();
      memcpy(incoming, uip_appdata, len);
      incoming[len] = '\0';
   }   
}


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: SocketApp CLIENT sample code

Postby benres » Thu Nov 05, 2009 9:42 pm

Thanks so much for posting this. I've only had a little time to play with it but it's working as advertised. My next step is to see about transferring files a few hundred k large. I also need to disconnect from the server when done with the file transfer. I only transfer about once an hour and don't want the server to be burdened with an open connection.

I'll let you know what happens and post any mods to the code to get large files transferring. It might be a few days so I wanted to quickly acknowledge your post.
benres
 
Posts: 9
Joined: Sun Nov 01, 2009 1:03 am

Re: SocketApp CLIENT sample code

Postby benres » Mon Nov 09, 2009 7:08 pm

First, this may be obvious to everyone else, but I realized the TCP/IP library is a known open-source library with documentation.

http://www.sics.se/~adam/uip/uip-0.9-refman/

With this in hand, I think I understand the code better. The example below POSTs a 10k file to a web server at 192.168.0.2 every 30 seconds. In this example, the 10k file is fixed but in practice this can be anything. The file is uploaded 100 bytes at a time, but the maximum segment size (MSS) is actually 327 bytes. See "uip_mss()" for details.

* All the 'C' code was moved into the Arduino PDE file.
--> Easier integration with other Arduino code.
--> Easy to call Serial.println functions for debugging.
--> No need to pass strings back and forth in large buffers. String processing can be done directly on RX values

* There's no longer a need for mysterious startup delays. The code still reports "connection OK" even if the server the Arduino is attaching to is down. But then a timeout occurs and the process repeats. So at least there's indirect notice that something is not right. About 15% of the time, the connection hangs the first time, but then it timeouts and runs fine thereafter. I can live with this.

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,0,149};   // IP address of WiShield
unsigned char gateway_ip[] = {192,168,0,1};   // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0};   // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"neighborsfreewifi"};      // max 32 bytes

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

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"12345678"};   // 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_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;

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

    boolean connectToServer(void);
   
    char            header[] = "POST /tutorial/showBrowser.jsp HTTP/1.0\r\nContent-Length: 10000\r\n\r\n";
    unsigned int    xmitCount = 0;

    boolean         connectedToServer = false;
    boolean         inited = false;       
    unsigned long   nextXMitTime;
   
    // I'm paranoid and give my buffers a few extra bytes.
    char        buffer[105];

    void setup()
    {
       Serial.begin(57600);       
       Serial.println("starting");             
       connectedToServer = false;             
       WiFi.init();       
    }

    void loop()
    {
       WiFi.run();
       
       if (millis() > nextXMitTime && inited && !connectedToServer) {
          Serial.print("Connecting to server... ");
          xmitCount = 0;
          if (connectToServer()) {
              connectedToServer = true;
              Serial.println("connection OK");
          }
          else {
              Serial.println("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,0,2);
       conn = uip_connect(&ipaddr, HTONS(8080));
       
      // uip_connect seems to never return NULL even in the case of a non-connect
      // Fortuantely you'll get a timeout if the connection is not good.
      // But it would be nice if this happened right away.
      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() {
            // 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;
            }
            // 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;
                nextXMitTime += 30000L;        // XMit again in 30 seconds
           }   
                     
           // An error occurred and we need to retransmit.
           if (uip_rexmit()) {
               Serial.println("{retransmit}");
               uip_send(buffer, strlen(buffer));                       
           }       
           
           // The current packet is done being transmitted and now we're OK to transmit another packet.
           if (uip_acked()) {                                 
              if (xmitCount < 10000) {
                  Serial.print("xmit: ");
                  Serial.println(xmitCount, DEC);
                  // Build a string that is 100 lines long
                  // We can actually transmit a packet up to the size of 'uip_mss()', which is tyipcally 327 bytes.
                 
                  sprintf(buffer, "<%04d>012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789ab\r\n", xmitCount);
                  xmitCount += strlen(buffer);
                  uip_send(buffer, strlen(buffer));         
              }
              else {        // send another linefeed to indicate the end of this POST data.
                  if (uip_connected()) {
                      uip_send("\r\n", 2);         
                  }
              }
           }

           // here's where new data arrives.  Do any string processing here.
           if(uip_newdata()) {             
              unsigned int len = uip_datalen();
              Serial.write((const uint8_t*)uip_appdata, len);
           }             
        }
    }           
benres
 
Posts: 9
Joined: Sun Nov 01, 2009 1:03 am

Re: SocketApp CLIENT sample code

Postby GregEigsti » Mon Nov 09, 2009 8:11 pm

Good stuff! The uIP doc is invaluable ;)

All the 'C' code was moved into the Arduino PDE file.

I have gone back and forth on doing what you did here but mainly stuck with the .pde + .c "paradigm" to follow along in the style of the WiShield samples. Though now that I look at your post and think about it I think I'll likely go all .pde as well - having Serial.println() available for debugging is worth it on its own. String processing could be a benefit too; but I'd have to think about that one and all of the permutations ;)

Another weirdness that I have found is that a lot of the basic data types are undefined in a .c file (boolean for example). At one point I found the correct header to include in my .c file to get the "normal" set of types but have slept since then and forgotten what that header is. Consolidating it all into the .pde fixes that too (and that is why you will see me using such fundamental types in the .c file - for instance int rather than boolean). The Arduino is arguably weird - they have taken is so far in the direction of trying to make it easy for newcomers that us crusty old programmers get freaked out by it ;) And I hate the fact that there are two pin 1's on an Arduino - a digital pin 1 and an analog pin 1 - sheesh just give them all different numbers and remove the confusion. Ahhh, the stuff that philosophical arguments are made of ;)

The code still reports "connection OK" even if the server the Arduino is attaching to is down

From my experimenting it seems that uip_connect() never returns NULL on failure...

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: SocketApp CLIENT sample code

Postby benres » Mon Nov 09, 2009 8:40 pm

Agreed the Arduino has some funkyness associated with the low barrier to entry. Overall I think they've done an excellent job. I can move very fast with the premade hardware. Now if I could just get DHCP and DNS lookup to work, we'd really be cooking with kerosene.

I see the UIP library includes support for DHCP and DNS. Do you (or anyone) know why AsynchLabs hasn't merged this in? Is it simply a case of they've been busy with other stuff, or is there a more fundamental issue? I may give it a whirl.

One more thing -- I tried using "printf() / fprintf()" in the "C" code but it didn't work. I'll bet there's a stream that will make fprintf() work.
benres
 
Posts: 9
Joined: Sun Nov 01, 2009 1:03 am

Re: SocketApp CLIENT sample code

Postby GregEigsti » Mon Nov 09, 2009 9:50 pm

Yeah, you have the "lower barrier to entry" coupled with c programming ;) I thought I had seen python for the Arduino and later looked for it but found nothing. I agree with the ease of use - for around $30 I can buy a pre-built micro, there are tons of user supplied support libs, tools are free and work on different platforms. Its a no brainer - even with its "weirdness".

Have not played with the DHCP/DNS stuff - I'd rather have DNS first, lack of DHCP is not really a big deal for me. This stuff was probably not moved over due to size (and time) constraints. If you do decide to move over some of the uIP code beware - I found the SMTP sample to have errors and not fully implemented.

And printf() on the Arduino would be cool; may already be a library that ties it to Serial.print(). But what would you do with fprintf()? print to eeprom? print to the network?

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: SocketApp CLIENT sample code

Postby GregEigsti » Mon Nov 09, 2009 11:21 pm

I started looking at the DNS stuff (resolv.c, resolv.h) and noticed that it was a UDP app. I have made a few tweaks and it is building for me, but the real problem will be to muck with the WiShield / uIP code to allow you to build a mutual TCP and UDP - cause what fun is UDP based DNS if you don't have TCP ;)

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: SocketApp CLIENT sample code

Postby GregEigsti » Sat Nov 14, 2009 10:36 pm

I could not get the .pde + .c merge that you referred to to compile when using proto thread versions of the PSOCK functions. PSOCK_SEND_STR gave me compiler errors re: invalid conversion of the buffer passed to it - but calling psock_send with the same buffer compiled just fine. Weird thing is that PSOCK_SEND_STR is just a macro that calls psock_send with a little proto thread magic mixed in. Sucks because the proto thread functions allow you to do multiple sends/receives per app_call.

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: SocketApp CLIENT sample code

Postby GregEigsti » Tue Nov 24, 2009 2:49 pm

I got this "one pde file" setup compiling if I do not use any PSOCK_* functions that take char* as params. Interesting thing is that if I call the equivalent psock_* function it all compiles ok. So there is something in the proto thread macros that is bound and determined to drive me crazy ;)

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 TCP/IP Stack

Who is online

Users browsing this forum: No registered users and 1 guest