Thursday 11 July 2013

Use the TP-Link TL-WR703N to send Arduino sensor data


Most solutions to send data through Wifi with your Arduino include an Ethernet shield or Wifi shield.

Another alternative I like a lot uses the PCB of a TP-Link router as a shield bridging Ethernet to Wifi.

I felt all these solutions were overkill and figured some decent connectivity could be acheived without any shield and although my solution it does not really provide complete TCP/IP connectivity to your Arduino, it  gives (root) access to your router console, and therefore the possibility to send data to the Internet through the command line.


Inside the TL-WR703N (with OpenWRT installed on it), you have a serial port console logged in as root. All you have to do, is hook it up to your arduino's serial port. There are however a couple of things you need to be careful about.

This post describes my approach.



Wiring and physical connection

You will find in another post how to make the connection between a TL-WR703N and a FTDI adapter. Well, if you're going to use an Arduino Pro Mini, you are going to need to swap the TP_OUT (TX) and TP_IN (RX) wires otherwise, both the Arduino Pro Mini and the TL-WR703N are going to transmit over the same wire.

Writing your code and debugging

Since the Arduino Mini Pro has only one serial port, you are left with two options:
  • The serial port, also used for programming and debugging (which I will detail below)
  • The software serial port (which I'm not going to cover here)
First things first, before connecting your programmed Pro Mini to your TP-Link, you need to make sure your code works. You need to remember that your Arduino is on a ROOT console and a command can be potentially devastating.

The right connection speed

Make sure you set the right serial port speed (115200 baud) to communicate with the TL-WR703N. e.g.:
void setup() {
   Serial.begin(115200);
}
This is the only speed that is going to work with the TP-Link router.

But not too fast...

My experience showed that although your serial port needs to be set at 115200 baud, you should not send your commands to the console too fast or your characters will become scrambled and it won't work.

If the command I want to pass to the router terminal is:
wget http://www.mydomain.com/postSensorData.php?value=22.5
Instead of writing a command like
serial.Print("wget http://www.mydomain.com/postSensorData.php?value=22.5");
Which almost always gets scrambled along the way, I wrote a small function to slow things down:
void slowSerialPrint(char text[]) {
  for ( int i ; i < strlen(text) ; i ++ ) {
    Serial.print(text[i]);
    delay(5);
  }
Note: you can play with the delay value if you still get corrupted characters.
This function can then be called as follows:
slowSerialPrint("wget http://www.mydomain.com/postSensorData.php?value=22.5");

Buggy sprintf (and an alternative)

You will probably want to pass that 22.5 value dynamically. With the Arduino environment 0023, I found the sprintf function buggy and always returning '?'.

Jonathan McCrohan proposes the following solution on his blog:
If you have ever tried to use sprintf() on an Arduino to convert from a float to a string, you will notice it doesn't work. 
sprintf(buf,"%f", floatvar); 
The above function will most likely return a "?" to your char buffer.
If you google around, you'll see various functions that people have written to overcome this, but all of them seem broken in one way or another. The alternative is to use dtostrf(), a standard avr-libc function. This provides a simple, tested function to convert from a float to a string.
To use this function, simply replace sprintf() with dtostrf(), along with the self explanatory parameters below. 
dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf); 
As the function name suggests, this will also work for the conversion of doubles to strings.
For those of you where an example is always required, he even made a small example here.

Important tips

  • If you need debugging outputs, I recommend using a #define DEBUG_MESSAGES 1 statement or something, so you can flip of a switch and get rid of them. Although this is a good practice, I recommend always starting your output lines with a hash (#) to make sure that it will be ignored if it ever reaches your console.
  • When the router finishes booting, you get the "Please press Enter to activate this console." message. This obviously means that you need to start with a "\n" (newline) before sending your first command. Since it does not hurt to send empty lines, I always start by sending a newline.
  • Please remember that your router takes some time to boot. Mine takes about 45secs to boot (exroot and wifi). For this reason, I always leave a delay of 45sec in the setup function: "delay(45000);".
  • If your command ever gets scrambled by corruption on the serial line, you are likely to end up with an unclosed quote or double quote. The only solution I found for this was to send a CTRL+D. For this reason I would like to always send a "CTRL+D" followed by a newline before sending my command prompt is there and clean. I however have not found any solution for this so far, so please drop a comment below if you know how to do this.

You're up and running