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);
}
}
}