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.