Tuesday, May 24, 2011

Arduino to Android IO on the cheap (aka Poor Man's NFC)

Summary: This article describes how to implement a very low bandwidth one way communication channel between an Arduino (or any other microcontroller) and an Android device using nothing more than about a meter of magnet wire, a resistor and diode. Links to a software sketch for the Arduino and the Android source code is included.

A cheap and simple way of communication between a microcontroller (eg Arduino) and an Android device is surprisingly difficult to achieve. The most practical means of accomplishing this is to use a Bluetooth or WiFi module adding significantly to the cost of a project (in the order of $30 - $60).

This is a little hack that allows very low bandwidth communications in one direction for practically no cost. It's not practical for most applications, but I thought the idea was sufficiently interesting to explore and write up in a blog post.

You'll need the following:
  • An Android phone or device which features a magnetometer (electronic compass). I'm not aware of any device without one.
  • An Arduino or other microcontroller (my examples are for the Arduino)
  • About 1 meter of  enameled copper wire (magnet wire), although any thin insulated wire will do.
  • The Android 'Tricorder' app (available free from the Android app store)
  • A 120 ohm resistor 
  • A diode (optional)
Make a coil by wrapping the 1 meter of magnet wire around something cylindrical  (eg AAA cell) with a diameter about 1cm. You should get about 30 turns from 1 meter. The diameter or number of turns is not important as long as they are in that ball park. Wrap a light thread around the coil to help keep its shape.



Connect the coil as illustrated in the schematic above. The 120 ohm resistor limits current draw to about 40mA which is the maximum allowed [1]. The purpose of the diode is to protect the Arduino from an EMF kick when pin 13 is driven low. This is known as a  flyback diode. With only 30 turns and an air core, you'll probably get away without using it. But it's good practice to include it.

Tricorder app displaying magnetic field. For the
HTC Desire the signal is strongest at the bottom
left of the LCD where the magnetometer is located
This is likely to vary from device to device.
Now to locate the magnetometer. It's location will vary from device to device. This is where the tricorder comes in handy. First load this sketch on the Arduino before you connect the coil.


/**
 * Generate 1Hz square wave on pin 13
 */
void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
    digitalWrite(13,1);
    delay(500);
    digitalWrite(13,0);
    delay(500);
}


When the coil is disconnected you should see the Arduino's LED blink. Now connect the coil. The LED should stop blinking at this point because the coil shorts the LED -- this is expected. Start the Tricorder app and switch to MAG mode and start scanning. Methodically scan the coil across the surface of the Android phone until you start seeing a strong square wave. Where the amplitude of this signal peaks is where the magnetometer is located. You should see a similar square wave if you position the coil at the same location on the bottom surface of the device. Use some tape to keep it positioned there.

Now we have a way of signalling the Android. The temptation at this point is to connect the coil to the Arduino UART and to start firing data at 9600bps. Unfortunately this isn't going to happen for a few reasons:

We are limited by the magnetometer sampling rate available to us by the Android OS. Here is a histogram of time-between-samples from a HTC Desire running Froyo (Anrdoi 2.2) at maximum sampling speed:



We can see here that almost all the samples come in at between 18ms and 25ms. There are a few outliers going all the way up to 45ms but there numbers are so few we can ignore them. This means we are limited to a maximum of a 40Hz sampling rate. If we drive the coil with a digital IO line that immediately caps our maximum bandwidth at 40bps.

But does the magnetometer respond quickly enough?

This Arduino sketch energizes the coil with a square wave of starting at 500Hz and decreases to about 1Hz over a few seconds.

int i;
int d = 1;
void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
  for (i = 1; i < 500; i+=d) {
    
    // Increase increment as frequency decreases
    d = (i>>6) + 1;
   
    digitalWrite(13,1);
    delay(i);
    digitalWrite(13,0);
    delay(i);
  }
}


Here is the magnetometer data:

As expected high frequencies (on the left) are not be detected. It seems that we only get good pickup when the frequency drops to about 6Hz!

For encoding I'm going to use NRZ (non return to zero) similar to that used in RS232 with a bit period of 140ms (about 7 bps). The Arduino's UART lower limit is 300bps, so I can't use it to generate the signal.  So I'm going to 'bit bang' the signal in software. Likewise, on the Android I'm going to have to decode in software also.

#define BIT_DELAY 140

int i;
char *text = "Hello World! ";

void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
 
  char *s;
  s = text;
  while (*s != 0) {
    transmitByte(*s++);
  }
  
  delay (BIT_DELAY*10); 
}
/**
 * Bit bang a byte
 */
void transmitByte (byte c) {
  
  // Start bit
  digitalWrite (13,1);
  delay(BIT_DELAY);
  
  // Data bits
  for (i = 0; i < 8; i++) {
      digitalWrite (13, ( (c&0x80)!=0 ? 1 : 0) );
      delay(BIT_DELAY);
      c <<= 1;
  }
  
  // Stop bit
  digitalWrite (13, 0);
  delay(BIT_DELAY);
}

Here it is in action:


The Android source is available here.

Potential improvements


My simple example uses a digital IO line to drive the coil. The Android magnetometers typically have at least 8 bits of resolution per channel [2]. It may be possible to squeeze another few bits per symbol by driving the coil with a DAC [3]. For example, 4 power levels can be used to encode 2 bits per symbol. The odd bit error  can be corrected using forward error correction.

Also the magnetometer has three channels (X,Y and Z). It may be possible to construct a set of coils that excite the X,Y and Z channels independently … multiplying bandwidth by a further x3. Another thought: I've only discussed digital communication with the Android. Some applications might only require an analog signal.

These ideas are left as an exercise for the reader, but I’d love to hear how you get on if you try it. 

Footnotes


[1] Googleing "Arduino pin 13", one is lead to believe there is a current limiting resistor in series with the MCU pin. However looking at the schematics, the tap for the pin 13 header comes *before* the resistor: so the resistor does not limit current. I only discovered this at the end of the experiment while proofing this post. Omitting the resistor did me no harm, but would advise to include the resistor to be safe. We need as much current as possible (more current means more magnetic flux in the coil). The Arduino Duemilanove datasheet specifies a maximum draw of 40mA on a IO pin, so 120 to 150 ohms should do the trick. If you need more current you'll need to use a transistor.

[2] The HTC Desire uses a AKM Semiconductor AK8973 3-axis magnetometer which uses a combination of a 8 bit ADC and a 8 bit offset DAC to yield up to 12 bits of equivalent resolution spanning +/- 2000µT of magnetic flux.

[3] The Arduino does not have a true DAC, however DAC functionality can be achieved by passing the PWM output through a low pass filter.

28 comments:

Giuseppe Federico said...

can i send data from android to arduino??

jdesbonnet said...

Sorry -- only one way with this particular hack.

jdesbonnet said...

Two ideas come to mind for something equally simple in the opposite direction.

1. A photodiode on the Arduino and then modulate a light signal on the screen (the entire screen, or perhaps just a good sized square will do).
2. Photodiode on Arduino and modulate a signal on the flash LED which most devices have. (You could also do LED on the Arduino and pick it up via the camera... so you could make a neat little optical connector that can be taped over the camera/flash on the back.

A more complex (but perhaps more practical idea) is to implement a software modem and connect to the headphone jack. You could do two way comms this way and the part count shouldn't be very large.

Tom said...

Great work with this, I'm thinking of implementing it but I was looking at using the light sensor and the notification LED as the Rx and Tx. Any ideas on how fast the light sensor is sampled? I think it may be worse that the 7bps of the magnetometer...

jdesbonnet said...

Any Android sensor app I've seen that displays the ambient light level updates it very slowly. So I think you might be right about the speed.

Keep in mind the camera has a reasonable update speed and can serve the same function (not very battery efficient however).

11011 said...

awesome hack. one nice way to output data in previous arduino projects that had to bip and light leds until now :)

Ryanze said...

hello, i am really interested in this hack.

but i can't go forward after the first sketch, the led keeps blink even after i connected the coil to arduino. anything wrong?

jdesbonnet said...

It sounds like the coil is not making electrical contact. Magnet wire is covered by a layer of clear insulation (that's why you can coil it without causing a short). Make sure you scrape this insulation off the last 1cm or so of both ends of the coil using a knife before stuffing it into the Aurdino header sockets.

As the diameter of the magnet wire is likely to be much smaller than the socket holes you may need to do something to get a good electrical contact. Two ideas are:

1. Stuff a small segment of bell wire along with the magnet wire into the Arduino socket. If you look closely at the photo in my post you can see that's what I did.

2. To both ends of the coil solder a segment of bell wire which will fit snugly into the socket. Or if you have a suitable male header solder the coil to that.

Ryanze said...
This comment has been removed by the author.
Ryanze said...

it works great!

but as i see in yout electronic scheme, you put a resistor and a diode, but not with your video. whats with that?

and also one more question.
how to compile the software in android to get the "Hello World" work? i'm sorry i'm really new in stuff like this, all i used to see is .apk not linux's .tar.gz

regards.

jdesbonnet said...

You are indeed correct. The resistor and diode are not present in the video.

The resistor is a current limiting resistor to prevent too much current being drawn from the Arduino IO pin. I had originally miss-read the Arduino documentation and thought that there was a resistor already in series for IO pin 13. So I never bothered to use an external resistor. I later discovered that was incorrect. But as there was no harm done and I had been using it for hours that way I decided to make the video without putting in the extra resistor.

The diode is a 'flyback' diode which protects against a voltage spike when the coil is suddenly driven low. They are important when driving large coils (eg relays). But for such a small coil it's not necessary. It's included in the schematic for completeness.

Re the Android code. I just packed up the code from my Eclipse project. I didn't think too hard about the best way of packaging it -- sorry if it doesn't work out-of-the-box. If I get a chance later I'll write a short howto on getting that setup.

Ryanze said...

it's so fine. i found my way to finish all the steps you wrote.

but i think there is a bug in android source code if use in another android device, i am using motorola defy.

it appears the charracter of word "Y" double dotted above it. even when the coil is not near the magnetometer, this is my video of it --> http://www.youtube.com/watch?v=kG7zlInyBR8

i think the code for android need to be specified for each device. thanks

jdesbonnet said...

The 'ÿ' character is 0xFF... all binary 1. It's not picking up a good signal.

Two suggestions: 1. Looking at the output from the Tricorder app... I'm wondering is that a strong signal. I don't think so. Are you sure you have the location of the magnetometer correct? What make/model of phone is it?

2. Something I should have mentioned in the text. I've got a hard coded threshold value. See constant 'THRESHOLD' in the code. You'll need to set that about half way between the on and off values.

My email address is jdesbonnet at gmail dot com if you want to follow up.

HRJ said...

I came here looking for which magnetometer the HTC Desire uses. Thanks for mentioning it.

How did you find it out? I want to know the magnetometer used by the Samsung Galaxy 3.

PS. Very interesting blog. I love such DIY hacks.

jdesbonnet said...

The app "Sensor List" from publisher "Idea Matters" (available free on the Android Market) gives a list of the sensor devices used. I think that's how I got the sensor chip number.

alf leo said...
This comment has been removed by a blog administrator.
Sofia said...

Hi
I am a student of Electrical and Computer Engineering in Portugal.
I'm doing a work like this but I will do only the receive code in android.
Can you explain me how you do the receiver code?
thank you for your attention.

C. Brito

jdesbonnet said...

C. Brito: Take a look at the Android source code linked near the end of the article:

http://code.google.com/p/wombat-blog/downloads/detail?name=WombatPMNFC-0.1.tar.gz

You will need to setup the Android development environment to compile it. See http://developer.android.com/

stefano said...

nice hack!
I am trying to implement it, using arduino uno and a samsung galaxy S. I am using resistor and flyback diod as you described, but (from tricorder), it seems that when I use a square wave with period less then 800 ms, the sensor does not get it. Plus, the wave seen by tricorder is a sinusoid, and not a square one. Do you think it might be the sensor of the phone? I am a total beginner, this is the first resistor that I touch.

jdesbonnet said...

stefano, It's quite possible the sensor in the Galaxy Nexus has a slower response. Unfortunately I cannot find out what sensor the Galaxy Nexus uses for the compass/magnetometer.

I've actually got that phone so I'll check it out when I get a chance.

stefano said...

Yes, it might be. Maybe then I will implement some optic poor man communication :)

pgf said...

Hi, great project. Would a larger antenna increase the communication range?

Elton said...

Excellent tutorial, I am able to generate the same result on the first try. :)

I have a question and hope you can help. If I need to create a magnetic field to turn on a reed switch, do I a coil with longer diameter or more turns?

El

Goebish said...

THIS is a hack !

Jean-Philippe Deblonde said...

Sorry to dig an old post.

I was asking myself a question : is it possible to replace the coil by a speaker ? The idea : instead of using Arduino + a coil, we would use a smartphone or a desktop, playing a pre-defined square wave audio sample, moving the speaker (and it's magner at the same time). Do you think that the magnetometer can catch that magnetic pulse ?

jdesbonnet said...

I suspect anything audible would be beyond the frequency response of the magnetometer. But you might see some response at frequencies below 10Hz. Only way to find out is to try :-)

Mike Whitenton said...

Seems like a much higher data rate could be achieved by connecting a peizo speaker to the Arduino and using the microphone on the Android. Basically use FSK (2 different tones) for 0 and 1. This is the way early computer modems worked.

Rishabh said...

The reason it is a one way communication is because magnatometer of phone is a passive device ( detects magnetic field) and arduino here works as an active device (provides power). The magnatometer provides power but not as much as to become an active device.
For dual communication NFC (Near Field Communication) is what is coming in phones. Its very similar to what u r doing here. Look into it if u r interested.