In dit project heb ik een NFC lezer en een Adafruit OLED display aan een arduino UNO aangesloten om NFC chips te kunnen uitlezen. De NFC lezer en OLED display communiceren met elkaar via het I2C protocol, dat is een protocool waarmee apparaten met elkaar kunnen communiceren met maar 2 draadjes (de scl serial clock en de sda serial data) en het master slave principe. De Arduino is hier de Master die met de slaves communiceert via de A4 en A5 pin. Als de master informatie wil opvragen aan de slaves vertelt de master eerst met welk apparaat hij wil communiceren doormiddel van het address van de slave te versturen. Voor mijn OLED is dat bijvoorbeeld 0x3c.



Om de arduino te programmeren heb je de Wire library nodig, voor de NFC lezer de PN532 library en de NfcAdapter libary nodig en voor het display de Adafruit_SSD1306 en Adafruit_GFX nodig.
#include <SPI.h>
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Door #define te gebruiken maak je geen variable aan, maar voor elke plek in de code waar de naam staat die na #define staat word de waarde ingevuld wat het programma weer een paar byte kleiner maakt aangezien een arduino vrij beperkt is in zijn opslag.
#define OLED_RESET 4
#define SCREEN_ADDRESS 0x3C
Hier word de NFC lezer en het OLED display geïnitialiseerd.
PN532_I2C pn532_i2c(Wire);
NfcAdapter nfc = NfcAdapter(pn532_i2c);
Adafruit_SSD1306 display(OLED_RESET);
De void setup(void){} word eenmalig aangeroepen als het programma start, hier begint de nfc lezer op het address dat in de library is geprogrammeerd en het oled display start ook waarbij de arduino gaat communiceren met het display via het address 0x3c. Daarna word de buffer van het display geleegd en bij display.display(); word op het scherm geschreven wat er op dit moment in de buffer zit… niks dus.
void setup(void) {
Serial.begin(9600);
Serial.println("NDEF Reader");
nfc.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
}
De void loop word direct aangeroepen als de laatste regel van setup is uitgevoerd en als de laatste regel van loop is uitgevoerd. void betekend dat er geen waarde word teruggeven en tussen de haakjes dat er geen waarde in word gestopt. eerst print ik op de seriële monitor Scan a NFC tag en daarna roep ik de functie drawOled aan met input in de functie “Scan tag please.
void loop(void) {
Serial.println("\nScan a NFC tag\n");
drawOled("Scan tag please.");
Hier is de functie, die staat buiten de void loop maar nu neem ik even door wat hier gebeurt. Als je deze functie aanroept verwacht die dat er een String als input word gegeven die in deze scope information word genoemd.
Eerst wordt de buffer van het display geleegd, daarna wordt de grootte van de text naar 1 gezet dat is de kleinst mogelijke waarde voor dit display. De tekstkleur wordt wit. De cursor wordt naar 0,0 verplaatst (linksboven) dan wordt de tekst in de variabele information naar de buffer geschreven. En daarna word de tekst weergegeven op het display.
void drawOled(String information) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(information);
display.display();
}
Dit is het vervolg op de loop.
Eerst wordt er gekeken of er een NFC chip bij de lezer in de buurt is. Als dat zo is wordt er op het scherm weergegeven Reading tag… met de drawOled functie. Daarna wordt de NFC tag gelezen en wordt de informatie opgeslagen in de variabele tag. Met tag.print word de informatie op de seriële monitor geschreven. Als er berichten staan op de NFC chip worden die uitgelezen en het aantal berichten wordt in de variabele recordCount gestopt. Voor nu is het alleen nog mogelijk om er 1 bericht op te zetten. Voor elk bericht wordt het bericht opgeslagen in record en de regel daarna wordt er gekeken wat de payload is in het bericht. De payload is waarin je je informatie in kunt opslaan de lengte van de payload word opgeslagen in de variabele payloadlength. Daarna wordt een een array gemaakt met de naam payload en de lengte van de lengte van de payload. Het is een Byte want die is 8 bit en daar kun je 255 nummers in opslaan wat genoeg is aangezien ASCII 127 tekens heeft en elk teken ook in 8 bit staat opgeslagen op de NFC chip. daarna worden de karakters opgeslagen in de byte array en word er een String aangemaakt met de naam payloadAsString. Aangezien de karakters nu nog los van elkaar staan in een array worden ze bij de volgende for loop geschreven in de string payloadAsString. Daarna word het weergegeven op het display, dat ging niet via de functie drawOled omdat er dan niet juist op het display geschreven werd, waar dat aan ligt weet ik nog niet maar ik denk dat het iets met timing te maken heeft. Als dat op het display geschreven is wordt er 3 seconden gewacht voor de volgende instructie.
if (nfc.tagPresent())
{
drawOled("Reading tag...");
NfcTag tag = nfc.read();
tag.print();
if (tag.hasNdefMessage()) {
NdefMessage message = tag.getNdefMessage();
int recordCount = message.getRecordCount();
for (int i = 0; i < recordCount; i++) {
NdefRecord record = message.getRecord(i);
int payloadLength = record.getPayloadLength();
byte payload[payloadLength];
record.getPayload(payload);
String payloadAsString = "";
for (int c = 0; c < payloadLength; c++) {
payloadAsString += (char)payload[c];
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(payloadAsString);
display.display();
}
delay(3000);
}
}
Als er geen NFC chip bij de scanner was word er op het display geschreven No tag found en gaat die na 2 seconde weer verder naar de volgende instructie.
else {
drawOled("No tag found");
}
delay(2000);
}
Hier is de volledige code om te kopiëren en te plakken.
#include <SPI.h>
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
#define SCREEN_ADDRESS 0x3C
PN532_I2C pn532_i2c(Wire);
NfcAdapter nfc = NfcAdapter(pn532_i2c);
Adafruit_SSD1306 display(OLED_RESET);
void setup(void) {
Serial.begin(9600);
Serial.println("NDEF Reader");
nfc.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
}
void loop(void) {
Serial.println("\nScan a NFC tag\n");
drawOled("Scan tag please.");
if (nfc.tagPresent())
{
drawOled("Reading tag...");
NfcTag tag = nfc.read();
tag.print();
if (tag.hasNdefMessage()) {
NdefMessage message = tag.getNdefMessage();
int recordCount = message.getRecordCount();
for (int i = 0; i < recordCount; i++) {
NdefRecord record = message.getRecord(i);
int payloadLength = record.getPayloadLength();
byte payload[payloadLength];
record.getPayload(payload);
String payloadAsString = "";
for (int c = 0; c < payloadLength; c++) {
payloadAsString += (char)payload[c];
} Serial.println(payloadAsString);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(payloadAsString);
display.display();
}
delay(3000);
}
}
else {
drawOled("No tag found");
}
delay(2000);
}
void drawOled(String information) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(information);
display.display();
}
Om de tag te schrijven heb je de volgende code nodig en kun je bovenin de string aanpassen met de tekst die je er zelf in wilt opslaan.
String payload = "Type your payload in this string";
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
PN532_I2C pn532_i2c(Wire);
NfcAdapter nfc = NfcAdapter(pn532_i2c);
void setup() {
Serial.begin(9600);
Serial.println("NDEF Writer");
nfc.begin();
}
void loop() {
Serial.println("\nPlace a formatted Mifare Classic NFC tag on the reader.");
if (nfc.tagPresent()) {
NdefMessage message = NdefMessage();
message.addUriRecord(payload);
bool success = nfc.write(message);
if (success) {
Serial.println("Success.");
} else {
Serial.println("Write failed.");
}
}
delay(5000);
}
Om het I2C address te vinden van jouw slave kun je deze code runnen. Ook te vinden bij File -> Examples -> Wire i2c_scanner
// --------------------------------------
// i2c_scanner
//
// Version 1
// This program (or code that looks like it)
// can be found in many places.
// For example on the Arduino.cc forum.
// The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
// Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26 2013
// V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
// by Arduino.cc user Krodal.
// Changes by louarnold removed.
// Scanning addresses changed from 0...127 to 1...119,
// according to the i2c scanner by Nick Gammon
// https://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
// As version 4, but address scans now to 127.
// A sensor seems to use address 120.
// Version 6, November 27, 2015.
// Added waiting for the Leonardo serial communication.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\nI2C Scanner");
}
void loop() {
int nDevices = 0;
Serial.println("Scanning...");
for (byte 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);
byte 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);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
} else {
Serial.println("done\n");
}
delay(5000); // Wait 5 seconds for next scan
}
- Jumper wires
- Arduino UNO
- NFC module V3 Arduino winkel tag niet bij inbegrepen
- Adafruit OLED display 128 x 64 winkel
Disclaimer:
Deze links zijn ter indicatie hoe de producten er uit zien, de winkel is vertrouwd door mij maar ik kan niet beloven dat dit de goedkoopste plek is. Ik verdien ook niks als jullie iets hiervandaan halen dus koop het gerust ergens anders.
Michiel Dirks
07-10-2021