Not for consumers
Yesterday I read Rory Cellan-Jones' article about NFC which pretty much shows that so far there isn't a consumer killer app for NFC. I'm not a professional pundit, so I'm not going to argue with him, but reading his article made me decide to do some first experiments.
Before Christmas I had an idea that low cost lower power data loggers could use NFC to send data to a mobile phone. I mean really low cost, pence, and really low power, like a watch battery. In fact a watch would be a good example of the kind of device I'm thinking of, it could be set to current time anywhere in the world just with a tap from a phone. More recently I saw that Farnell are now stocking the new ST M24LR Discovery kit, it costs a few pounds and features an energy harvesting NFC/I2C EEPROM and everything needed to read and write data, including an LCD display.
Learning with the Arduino
I'm an infrequent Arduino user as I feel cheap though it is, it's overkill for much of my tinkering. However it's an ideal system for experiments with I2C bus devices.Here's what I wrote. It's a fairly simple program that writes some text to the M24LR04E, which it then reads back. The text can also be read using an NFC phone, or the Discovery kit USB NFC transceiver. The program also reads the current temperature from the I2C temperature sensor included on the Discovery board.
// Use the Arduino to talk to ST M24LR-discovery board. // Michael Saunby. March 2013. #include <Wire.h> //I2C library // Deviceaddress is a 7 bit value. i.e. the read/write bit is missing and the rest // is shifted right. // i.e. deviceaddress = 0x53 is sent by Wire as 0xA6 to write or 0xA7 to read. // Dual interface (I2C+NFC) eeprom #define M24LR04 0x53 // I2C temperature sensor - see table 1 of data sheet. Resistor selects address. #define STTS751 0x39 // See also - // http://playground.arduino.cc/code/I2CEEPROM // http://www.developer.nokia.com/Community/Wiki/Understanding_NFC_Data_Exchange_Format_(NDEF)_messages // void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) { int rdata = data; Wire.beginTransmission(deviceaddress); Wire.write((int)(eeaddress >> 8)); // MSB Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.write(rdata); Wire.endTransmission(); } void i2c_eeprom_write_bytes( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) { byte c; for ( c = 0; c < length; c++){ Wire.beginTransmission(deviceaddress); Wire.write((int)(eeaddresspage >> 8)); // MSB Wire.write((int)(eeaddresspage & 0xFF)); // LSB Wire.write(data[c]); Wire.endTransmission(); delay(10); eeaddresspage++; } } // 03 | nbytes | D1 |01 | payload_length | type | id | payload | FE // nbytes = payload_len + 4 // payload_length = ‘enSome text here \FE’ // type = ‘T’ (text), ‘U’ (URI) // id = 02, 03 (http://) void i2c_eeprom_write_ndef( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) { // Write NDEF header char header[] = "\003\000\xD1\001\000T\002"; header[1] = length + 5; // Total bytes after D1 marker. header[4] = length + 1; // Will add \xFE to payload. i2c_eeprom_write_bytes( deviceaddress, eeaddresspage, (byte *)header, sizeof(header) ); i2c_eeprom_write_bytes( deviceaddress, eeaddresspage+sizeof(header)-1, data, length ); i2c_eeprom_write_bytes( deviceaddress, eeaddresspage+sizeof(header)+length-2, (byte *)"\xFE\0\0\0", 4 ); } byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) { byte rdata = 0xFF; Wire.beginTransmission(deviceaddress); Wire.write((int)(eeaddress >> 8)); // MSB Wire.write((int)(eeaddress & 0xFF)); // LSB Wire.endTransmission(); Wire.requestFrom(deviceaddress,1); if (Wire.available()){ rdata = Wire.read(); } return rdata; } byte i2c_sensor_read_byte( int deviceaddress, int eeaddress ) { byte rdata = 0xFF; int rc; Wire.beginTransmission(deviceaddress); Wire.write((int)eeaddress); rc = Wire.endTransmission(); Wire.requestFrom(deviceaddress,1); if (Wire.available()){ rdata = Wire.read(); } if(rc != 0){ Serial.print("Error "); Serial.println(rc); } return rdata; } void setup() { char message[] = "enHello World"; // data to write. N.B. language prefix 'en' Wire.begin(); // initialise the connection Serial.begin(9600); i2c_eeprom_write_ndef(M24LR04, 4, (byte *)message, sizeof(message)); Serial.println("Setup done"); } void loop() { int addr=13; // location of text string in NDEF payload. byte b, lo; signed char hi; float temperature; Serial.print(' '); while (addr<53) { b = i2c_eeprom_read_byte(M24LR04, addr); if(b == 0xFE)break; Serial.print((char)b); addr++; } Serial.println(" "); // read temperature hi = i2c_sensor_read_byte(STTS751, 0); lo = i2c_sensor_read_byte(STTS751, 2); if( hi > 0){ temperature = hi + lo * 1.0/256.0; }else{ temperature = hi - lo * 1.0/256.0; } Serial.print(temperature); Serial.println(" "); delay(1000); }
Lessons learnt
- Read the data sheet. When it doesn't work, read it again.
- Wire uses I2C_device_address / 2. It that makes no sense, compare datasheet addresses with those used in the code. Yup, that caught me out.
- The onboard microcontroller can be disabled with a jumper on reset. But it's not necessary, two masters works in this case as the default ST code only reads.
- NDEF - yet another data format. Yipee!
What next?
Now I've figured out how to use these NFC EEPROMs I've ordered a few chips, they're less than a pound each. Of course I'll need to make my own antenna, but that should be easy and fun. For the microcontroller I'll use the absurdly low power and very cheap MSP430.
Oh, and if you want to create your own Internet Of Little Things, then I suggest as a minimum you edit the code to write a URI to the EEPROM, then hey presto, you're little thing is on the Internet, sharing it's temperature, or whatever else it wants to share.