Sunday, March 03, 2013

Internet of little things

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.

4 comments:

ibrahim abukharmeh said...

Hello,
i am trying to get some data (hence the NDEF messages)from the M24LR board you are using to my arduino uno
i was looking over the internet about the arduino i2c pins and i have noticed that some people connect a level shifter to arduino because of the voltage difference

i am wondering if there a need for anything to be connected between the m24lr i2c pins
and the arduino uno one, could u please post an image showing the whole connection or describe it for me

Many thanks

Michael Saunby said...

You'll not need a level shifter so long as you don't add pull-up resistors. Google on Arduino to Raspberry Pi I2C for similar discussions.

Yannis Boukhalfa said...

Hi Michael, thank you a lot for your post.
I have two questions.
First why do you use the NDEF instead of the simple function to write in the eeprom?
Second I wanted to know how can we ask to the chip to start a NFC transmission to another chip.

Thank you for your answers.
Yannis BOUKHALFA

Neil ForceTronics said...

Thanks for the post. I leveraged your STTS751 code for a wireless sensor network project I am doing