Today I want to give an update on my NFC badge with some improved functions. Unfortunately the code and the video are of low quality as this is work in progress.

 

In my last blog post (NFC-Badge - Update your badge with your smartphone - NDEF and app ) I described how the data is formatted on the eeprom. So I wrote some code which decodes this data. It checks if there is valid NDEF data on the eeprom and then goes down through all the NDEF messages and recordings. When it finds a text record it displays the content on the display. If no text record is found it displays an error. If there are other NDEF records like for example URI records on the eeprom they are ignored.

 

Additionally I connected the busy pin of the M24LR to pin 12 of the Arduino Uno just like on the X-NUCLEO-NFC02A1. Now the Arduino is able to detect if the content of the eeprom was updated and redraw the display. So no reboot is necessary anymore.

 

Another small improvement is the centering of the name on the display. Dependent on the length of the string the position is calculated.

 

Below is a video (bad quality, sorry) which shows how a smart phone is connected to the badge. The phone uses the ST25 NFC Tap app. The NDEF text record is deleted and then generated again and changed. The display is updated each time accordingly. After the phone is removed the last content stays on the display. You can see how the display blinks regularly. This is due to changes on the busy pin. So I suppose the phone accesses the tag in regular time periods.

 

 

Here is the code (bad documentation):

 

#include <Wire.h> // i2c-lib
#include <dog_7565R.h>
#include "font2.h"
#include "font.h"

#define M24LR_BUSY 12    // M24LR busy pin

dog_7565R DOG;

byte backlight=255;
int led       = 10;
void init_backlight(boolean mono);
void mono_backlight(byte brightness);

bool is_valid_tag = false;
int tag_pointer=0;
int record_pointer=0;

//create badge sceen content
void badge_screen(char *x, int xlen)
{
  int font_width=0;
  int font_start=0;
  font_width=font2[4];
  font_start=64-(font_width*xlen/2);
  if(font_start<0) font_start=0;

  Serial.print("font width: ");
  Serial.println(font_width);
  Serial.print("font start: ");
  Serial.println(font_start);
  DOG.clear();  //clear whole display
  DOG.string(25,0,font2,"Hello");
  DOG.string(22,3,font,"my name is");
  DOG.string(font_start,5,font2,x);
}

//create error content
void error_screen(char *x)
{
  DOG.clear();  //clear whole display
  DOG.string(25,1,font2,"Error");
  DOG.string(0,5,font,x);
}

//read byte from eeprom with 16 register
unsigned char i2c_read_byte(char address, unsigned short reg)
{
  char x=0;
  Wire.beginTransmission(address);
  Wire.write((reg&0xff00)>>8);
  Wire.write(reg&0x00ff);
  Wire.endTransmission();
  Wire.requestFrom(address,1);
  if(Wire.available()) x=Wire.read();
  Wire.endTransmission();
  return x;
}

bool check_valid_tag(void)
{
  if(i2c_read_byte(0x53,0)==0xE1) is_valid_tag=true;  // check NDEF magic number 0xE1
  else is_valid_tag=false;
  return is_valid_tag;
}

int memory_length(void)
{
  if(is_valid_tag) return (i2c_read_byte(0x53,2)*8);
  else return -1;
}

int find_next_ndef_message(void)
{
  char byte_buffer=0;
  int message_length=0;
  if(!is_valid_tag) return -1;  // exit if no valid tag
  if(tag_pointer==0) tag_pointer=4; // skip cc block
  do
  {
    byte_buffer=i2c_read_byte(0x53,tag_pointer);
    if(byte_buffer==3)  // found next NDEF message
    {
      message_length=i2c_read_byte(0x53,tag_pointer+1);
      record_pointer=tag_pointer+2; // set record_pointer to beginning of first record
      tag_pointer=tag_pointer+2+message_length;  // set tag pointer to beginning of next ndef message
      return message_length;
    }
    if(byte_buffer==0xFE)  // found Terminator
    {
      return -1;
    }
    byte_buffer++;
  } while(byte_buffer==0);  // read all null bytes
  return -1;  // something went wrong
}

unsigned char get_record_tnf(void)
{
  return (i2c_read_byte(0x53,record_pointer)&0x07);
}

unsigned char get_record_type(void)
{
  return i2c_read_byte(0x53,record_pointer+3);
}

int get_record_length(void)
{
  return i2c_read_byte(0x53,record_pointer+2);
}

int find_next_record(void)
{
  unsigned char record_header=0;
  record_header=i2c_read_byte(0x53,record_pointer);
  if(record_header&0x40) return -1;   // check ME bit
  else record_pointer+=(get_record_length()+4);
  return record_pointer;
}

//decode string from ndef file and tag and return buffer and length
int get_record_language_code(char* string_buffer)
{
  int language_code_length=0,i=0;

  language_code_length=(i2c_read_byte(0x53,record_pointer+4)&0x1F);

  for(i=0;i<language_code_length;i++)
  {
    *string_buffer=i2c_read_byte(0x53,record_pointer+5+i);  // text starts at offset 10 bytes from the beginning
    string_buffer++;
  }
  *string_buffer=0;
  return language_code_length;
}
//decode string from ndef file and tag and return buffer and length
int get_record_text_string(char* string_buffer)
{
  int record_length=0, i=0;
  int language_code_length=0;

  language_code_length=(i2c_read_byte(0x53,record_pointer+4)&0x1F);
  record_length=get_record_length();

  for(i=language_code_length+1;i<record_length;i++)
  {
    *string_buffer=i2c_read_byte(0x53,record_pointer+4+i);  // text starts at offset 10 bytes from the beginnig
    string_buffer++;
  }
  *string_buffer=0;
  return (record_length-language_code_length-1);
}

void check_ndef_tag()
{
  char nfc_string[20];  // buffer for text string
  int string_length=0;
  int record_tnf=0;
  int found_text_record=0;
  tag_pointer=0;
  record_pointer=0;
  if(!check_valid_tag()) error_screen("no valid NDEF tag");  // check if tag is valid
  else 
  {
    Serial.print("NDEF memory length:  ");
    Serial.println(memory_length());
    Serial.print("NDEF message length: ");
    Serial.println(find_next_ndef_message());
    Serial.print("Record pointer: ");
    Serial.println(record_pointer);
    do
    {
      Serial.print("Record pointer: ");
      Serial.println(record_pointer);
      record_tnf=get_record_tnf();
      Serial.print("Record tnf:    ");
      Serial.println(record_tnf,HEX);
      if(record_tnf==1)   // Well-Known Record
      {
        Serial.print("Record type:   ");
        Serial.println(get_record_type(),HEX);
        Serial.print("Record length: ");
        Serial.println(get_record_length());
        if(get_record_type()==0x54)   // found text record
        {
          get_record_language_code(nfc_string); // get string from eeprom
          Serial.print("language_code: ");
          Serial.println(nfc_string); // print string on serial
          string_length=get_record_text_string(nfc_string); // get string from eeprom
          Serial.print("text string:   ");
          Serial.println(nfc_string); // print string on serial
          Serial.print("text length:   ");
          Serial.println(string_length); // print string on serial
          badge_screen(nfc_string,string_length);        //show content
          found_text_record=1;
          break;    // break after first text record
        }
      }
    } while(find_next_record()>0); // go through all records
    if(found_text_record==0) error_screen("found no text record");        //show error
  }
}

void setup() {
  // put your setup code here, to run once:
  byte error,address,x;
  int nDevices;

  pinMode(M24LR_BUSY, INPUT_PULLUP);    // M24LR busy pin

  Serial.begin(115200); // setup serial
  Wire.begin(); // setup I2C
  Wire.setClock(1000);
  delay(10);

// scan for I2C devices
  Serial.println("Scanning I2C...");

  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknown error at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }


  Serial.println("Init Display");
  init_backlight(true); //use monochrome backlight in this sample code. Please change it to your configuration
  DOG.initialize(6,5,7,8,9,DOGM128);   //SS = 10, 0,0= use Hardware SPI, 9 = A0, 4 = RESET, EA DOGM128-6 (=128x64 dots)
  DOG.clear();  //clear whole display
  mono_backlight(255);    //BL to full brightness
  DOG.view(VIEW_BOTTOM);  //default viewing direction

  check_ndef_tag();
}

void loop() {
  // put your main code here, to run repeatedly:
  static int trigger_input_old=HIGH;
  int trigger_input=HIGH;

  trigger_input=digitalRead(M24LR_BUSY);
  if((trigger_input_old==LOW)&&(trigger_input==HIGH)) 
  {
    delay(500);     // wait a little bit until write is complete
    check_ndef_tag();  // check ndef tag at etch of busy signal
  }
  trigger_input_old=trigger_input;
  delay(10);
}

//The following functions controll the backlight with a PWM. Not needed for the display content
void init_backlight(boolean mono)
{
  if(mono) //EA LED55X31-G, EA LED55X31-W, EA LED55X31-B, EA LED55X31-A, EA LED55X31-R
  {
    pinMode(led,  OUTPUT);
    mono_backlight(255);
  }
}
//Use this funtion for monochrome backlight
void mono_backlight(byte brightness)
{
  analogWrite(led, brightness);  
}

 

What's next

 

rewrite the code and do some more testing. And maybe a better video.