Ons project

Wij hebben besloten om een project te maken in verband met NFC tags en scanners. Ons project bestaat uit 4 NFC tags met een bepaalde kleur.

De NFC tags zullen in de juiste volgorde gescanned moeten worden aan de hand van een kleurcode. De kleurcode kan gevonden worden door gebruik te maken van de gekleurde knoppen.

Foto's

Een overzicht van onze componenten en opbouw.

Blokschema's

De blokschema's van elk onderdeel van ons project.

Componenten

Een overzicht van de onderdelen die we hebben gemaakt.

Schema van de RFID scanner

RFID / NFC Reader

De NFC reader is gemaakt met een ESP32 feather, een 7-segment display, een buzzer en een RFID reader. De NFC tags hebben elk een verschillende kleur geprogrammeerd vanbinnen. Als de RFID reader een tag leest, stuurt het die kleur door via MQTT naar de MQTT broker, die op de Raspberry Pi draait. Dit gebeurt allemaal via WiFi, want de Raspberry Pi fungeert als een WiFi access point.

Het bericht wordt dan opgevangen in Node-RED, die ook draait op de Pi. Daar wordt gecontroleerd welk nummer er overeen komt met de kleur. Dat nummer wordt dan teruggestuurd naar de ESP, die vervolgens het nummer op de 7-segment display toont. Ook zal er dan een buzzer te horen zijn.

Schema van de arcade knoppen

Knoppen

De knoppen hebben elk een verschillende kleur en hebben elk een LED ingebouwd. Deze opstelling is gemaakt met een ESP32 van Joy-IT en de 4 arcade buttons. De ESP controleert continu of er een knop is ingedrukt.

Als er 4 verschillende knoppen zijn ingedrukt, wordt er een MQTT bericht doorgestuurd naar de Pi met de volgorde van de ingedrukte knoppen. In Node-RED wordt dan de volgorde gecontroleerd en als dit de juiste volgorde is komt er op het scherm van de Pi een code tevoorschijn.

Schema van de Wifi Kit 8

Afstandsbediening

Dit onderdeel is gemaakt met een Wifi Kit 8, een IR receiver, een afstandsbediening en een buzzer. Als je de code op het scherm hebt gekregen, kan je met de afstandsbediening die code hier ingeven. Die zal ook als je deze ingeeft op het scherm verschijnen.

Als er 4 cijfers zijn ingegeven wordt die verzonden naar de Raspberry Pi, die controleert dan of deze correct is. Als dat zo is wordt er een bericht teruggestuurd (juist/fout), en hoor je een gepaste toon uit de buzzer komen. En nu is het spel gedaan.

De Raspberry Pi (mini -computer)

De Raspberry Pi is een server waarop er software (=Node-Red) actief is zodat een smartphone, tablet of PC het dashboard kan zien. Op de server draait ook een Mosquitto broker die nodig is voor de communicatie tussen de robot en NodeRed.

Afbeelding van de hele flow van de Raspberry Pi

Node-red

Node-red is de software die op de Rapsberry Pi en op de laptop draaien. Het dashboard is er ook mee gemaakt. Het verstuurt ook de gegevens van op het dashboard via een mosquitto broker naar de Raspberry Pi via het draadloos netwerk.

Node red is een programma dat draait op de laptop en de Raspberry Pi. Op de laptop is het enige waar het programma voor dient om een visueel bedienings-dashboard te maken en de inputs daarvan door te sturen naar de Raspberry Pi op de robot via MQTT. Op de Raspberry Pi dient het programma om de MQTT-berichten te verwerken en de juiste pinnen op de Raspberry Pi aan te sturen, zodat de robot de juiste richting uit gaat.

Mosquitto broker

MQTT is een lichtgewicht communicatieprotocol, vooral gebruikt in IoT-toepassingen. Het werkt via een publish/subscribe-model:

  • Broker: centrale server die berichten ontvangt en doorstuurt.
  • Publisher: stuurt berichten naar een topic.
  • Subscriber: ontvangt berichten van topics waarop hij is geabonneerd.

Bijvoorbeeld: een sensor stuurt temperatuur naar het topic sensor/temperatuur, en een app die daarop geabonneerd is, ontvangt die waarde automatisch. Voordelen:

  • Weinig data- en energieverbruik
  • Betrouwbaar via QoS-niveaus
  • Ideaal voor trage of onbetrouwbare netwerken

Het dashboard

Het dashboard is een soort website/app waarop de admin bepaalde aansturingen kan doen. Je kan op het dashboard de codes laten resetten, het spel starten/stoppen en kijken welke tag het laatst is gescand alsook de resterende tijd van de spelers. Ook is er een dashboard die wordt vertoont op het scherm bij de Raspberry Pi, waarop deelnemers de tips, uitleg en een aflopende timer kunnen zien.

Dashboard van de admins:

Afbeelding van Het dashboard voor de administrators

Dashboard voor de spelers:

Afbeelding van Het dashboard voor de spelers van het spel

Code

De broncode van elk onderdeel van ons project, met ruimte voor commentaar.

RFID / NFC Reader Arduino / C++
#include <SPI.h>
#include <MFRC522.h>
#include "WiFi.h"
#include <PubSubClient.h>

#define RST_PIN 21
#define SS_PIN 23

int pinsVanSegmenten[7] = {32, 14, 13, 12, 27, 15, 33};

// Methoden
void mqttConnect();
void callback(char* topic, byte* message, unsigned int length);
void initWiFi();
void initMQTTbroker();
void segments(int A, int B, int C, int D, int E, int F, int G);

const char* ssid = "EscapeGame_Groep16";
const char* paswoord = "EscapeGame_PASS";

WiFiClient WiFiClient1;
MFRC522 mfrc522(SS_PIN, RST_PIN);

// MQTT instellingen
const char* mqttBroker = "10.42.0.1";
const char* mqttClientName = "RFID_SCANNER";
const char* subNumber = "EscapeGame/Number";
const char* pubScannedCode = "EscapeGame/ScannedCode";
const char* mqttPwd = "EscapeGame";
const char* clientID = "EscapeGame_Scanner";

PubSubClient mqttClient1(WiFiClient1);

// 7-segment display
void segments(int A, int B, int C, int D, int E, int F, int G) {
  digitalWrite(pinsVanSegmenten[0], A);
  digitalWrite(pinsVanSegmenten[1], B);
  digitalWrite(pinsVanSegmenten[2], C);
  digitalWrite(pinsVanSegmenten[3], D);
  digitalWrite(pinsVanSegmenten[4], E);
  digitalWrite(pinsVanSegmenten[5], F);
  digitalWrite(pinsVanSegmenten[6], G);
}

// MQTT connectie
void mqttConnect() {
  while (!mqttClient1.connected()) {
    if (mqttClient1.connect(mqttClientName)) {
      mqttClient1.subscribe(subNumber);
    } else {
      delay(5000);
    }
  }
}

// Callback
void callback(char* topic, byte* message, unsigned int length) {
  String ontvangenMsg;
  for (int i = 0; i < length; i++) {
    ontvangenMsg += (char)message[i];
  }

  if (String(topic) == subNumber) {
    if (ontvangenMsg == "1") segments(0,1,1,0,0,0,0);
    else if (ontvangenMsg == "2") segments(1,1,0,1,1,0,1);
    else if (ontvangenMsg == "3") segments(1,1,1,1,0,0,1);
    else if (ontvangenMsg == "4") segments(0,1,1,0,0,1,1);
    else if (ontvangenMsg == "0") segments(0,0,0,0,0,0,0);
  }
}

// WiFi
void initWiFi() {
  WiFi.begin(ssid, paswoord);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

// MQTT init
void initMQTTbroker() {
  mqttClient1.setServer(mqttBroker, 1883);
  mqttClient1.setCallback(callback);
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initMQTTbroker();

  SPI.begin();
  mfrc522.PCD_Init();

  for (int i = 0; i < 7; i++) {
    pinMode(pinsVanSegmenten[i], OUTPUT);
  }
}

void loop() {
  if (!mqttClient1.connected()) {
    mqttConnect();
  }
  mqttClient1.loop();

  MFRC522::MIFARE_Key key;
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

  if (!mfrc522.PICC_IsNewCardPresent()) return;
  if (!mfrc522.PICC_ReadCardSerial()) return;

  byte buffer2[18];
  byte len = 18;

  MFRC522::StatusCode status;

  status = mfrc522.PCD_Authenticate(
    MFRC522::PICC_CMD_MF_AUTH_KEY_A, 
    1, 
    &key, 
    &(mfrc522.uid)
  );

  if (status != MFRC522::STATUS_OK) return;

  status = mfrc522.MIFARE_Read(1, buffer2, &len);
  if (status != MFRC522::STATUS_OK) return;

  mqttClient1.publish(pubScannedCode, (char*)buffer2);

  delay(1000);

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}
Knoppen Arduino / C++
#include "WiFi.h"
#include <PubSubClient.h>

#define knopROOD 23
#define knopBLAUW 22
#define knopGROEN 15
#define knopGEEL 4

#define ledROOD 21
#define ledBLAUW 19
#define ledGROEN 18
#define ledGEEL 5

int knopArr[4] = {knopROOD, knopBLAUW, knopGROEN, knopGEEL};
int ledArr[4] = {ledROOD, ledBLAUW, ledGROEN, ledGEEL};
String kleurArr[4] = {"ROOD", "BLAUW", "GROEN", "GEEL"};

String strVolgorde = "";
int teller = 0;

// Declaratie methoden
void mqttConnect();
void initWiFi();
void initMQTTbroker();

const char* ssid = "EscapeGame_Groep16";
const char* paswoord = "EscapeGame_PASS";

WiFiClient WiFiClient1;

// MQTT instellingen
const char* mqttBroker = "10.42.0.1";
const char* mqttClientName = "Knoppen";
const char* pubVolgorde = "EscapeGame/Volgorde";
const char* mqttPwd = "EscapeGame";
const char* clientID = "EscapeGame_Knoppen";

PubSubClient mqttClient1(WiFiClient1);

// MQTT connectie
void mqttConnect() {
  while (!mqttClient1.connected()) {
    if (mqttClient1.connect(mqttClientName)) {
      digitalWrite(ledGROEN, HIGH);
      delay(1000);
      digitalWrite(ledGROEN, LOW);
      mqttClient1.publish("Hallo", mqttClientName);
    } else {
      delay(5000);
    }
  }
}

// WiFi init
void initWiFi() {
  WiFi.begin(ssid, paswoord);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

// MQTT init
void initMQTTbroker() {
  mqttClient1.setServer(mqttBroker, 1883);
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initMQTTbroker();

  pinMode(knopBLAUW, INPUT_PULLUP);
  pinMode(knopROOD, INPUT_PULLUP);
  pinMode(knopGROEN, INPUT_PULLUP);
  pinMode(knopGEEL, INPUT_PULLUP);

  pinMode(ledROOD, OUTPUT);
  pinMode(ledBLAUW, OUTPUT);
  pinMode(ledGROEN, OUTPUT);
  pinMode(ledGEEL, OUTPUT);
}

void loop() {
  if (!mqttClient1.connected()) {
    mqttConnect();
  }
  mqttClient1.loop();

  for (int i = 0; i < 4; i++) {
    if (digitalRead(knopArr[i]) == LOW) {
      if (digitalRead(ledArr[i]) == LOW) {
        digitalWrite(ledArr[i], HIGH);
        strVolgorde += kleurArr[i] + " ";
        teller++;
        Serial.println(strVolgorde);
      }
      delay(50);
    }
  }

  if (teller == 4) {
    mqttClient1.publish(pubVolgorde, strVolgorde.c_str());
    teller = 0;
    strVolgorde = "";

    digitalWrite(ledROOD, LOW);
    digitalWrite(ledBLAUW, LOW);
    digitalWrite(ledGROEN, LOW);
    digitalWrite(ledGEEL, LOW);
  }
  else if (teller > 4) {
    for (int i = 0; i < 4; i++) {
      digitalWrite(ledArr[i], LOW);
    }
  }

  delay(200);
}
Afstandsbediening Arduino / C++
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Declaratie van de methoden
void mqttConnect();
void callback(char* topic, byte* message, unsigned int length);
void initWiFi();
void initMQTTbroker();

const char* ssid = "EscapeGame_Groep16";
const char* paswoord = "EscapeGame_PASS";
WiFiClient WiFiClient1;

// MQTT instellingen
const char* mqttBroker = "10.42.0.1"; 
const char* mqttClientName = "Afstandsbediening";
const char* SubCorrect = "EscapeGame/Correct";
const char* PubCode = "EscapeGame/Code";
const char* mqttPwd = "EscapeGame";
const char* clientID = "EscapeGame_Afstandsbediening";
PubSubClient mqttClient1(WiFiClient1);

// OLED setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET    16
#define OLED_SDA       4
#define OLED_SCL       5
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Pins
const int IR_RECEIVE_PIN = 14;
const int BUZZER_PIN     = 12;

// IR codes
const unsigned long IR_CODES[] = {
  0xAD52FF00, 0xFF6897, 0xFF9867, 0xFFB04F,
  0xFF30CF, 0xFF18E7, 0xFF7A85, 0xFF10EF,
  0xFF38C7, 0xFF5AA5
};

IRrecv irrecv(IR_RECEIVE_PIN);
decode_results results;

// Puzzle
int enteredDigits[4];
int enteredCount = 0;
bool waitingForServer = false;

// Buzzer
struct BuzzerNote {
  int freq;
  int duration;
};

BuzzerNote correctNotes[] = {{1047,150},{1319,150},{1568,150},{2093,300}};
BuzzerNote wrongNotes[]   = {{300,200},{200,400}};

BuzzerNote* currentMelody = nullptr;
int melodyLength = 0;
int melodyIndex = 0;
unsigned long noteStartTime = 0;

// MQTT connectie
void mqttConnect() {
  while (!mqttClient1.connected()) {
    if (mqttClient1.connect(mqttClientName)) {
      mqttClient1.subscribe(SubCorrect);
    } else {
      delay(5000);
    }
  }
}

// Callback
void callback(char* topic, byte* message, unsigned int length) {
  String ontvangenMsg;
  for (int i = 0; i < length; i++) {
    ontvangenMsg += (char)message[i];
  }

  if (String(topic) == SubCorrect) {
    waitingForServer = false;

    if (ontvangenMsg == "1") {
      startMelody(correctNotes, 4);
      delay(3000);
      enteredCount = 0;
    } else {
      startMelody(wrongNotes, 2);
      enteredCount = 0;
    }
  }
}

// WiFi
void initWiFi() {
  WiFi.begin(ssid, paswoord);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

// MQTT init
void initMQTTbroker() {
  mqttClient1.setServer(mqttBroker, 1883);
  mqttClient1.setCallback(callback);
}

// Buzzer functies
void playTone(int freq) {
  if (freq == 0) analogWrite(BUZZER_PIN, 0);
  else {
    analogWriteFreq(freq);
    analogWrite(BUZZER_PIN, 128);
  }
}

void stopTone() {
  analogWrite(BUZZER_PIN, 0);
}

void startMelody(BuzzerNote* melody, int length) {
  currentMelody = melody;
  melodyLength = length;
  melodyIndex = 0;
  noteStartTime = millis();
  playTone(melody[0].freq);
}

void updateBuzzer() {
  if (!currentMelody) return;

  if (millis() - noteStartTime >= currentMelody[melodyIndex].duration) {
    melodyIndex++;
    if (melodyIndex >= melodyLength) {
      stopTone();
      currentMelody = nullptr;
      return;
    }
    noteStartTime = millis();
    playTone(currentMelody[melodyIndex].freq);
  }
}

// IR naar cijfer
int irCodeToDigit(unsigned long code) {
  for (int i = 0; i < 10; i++) {
    if (IR_CODES[i] == code) return i;
  }
  return -1;
}

// Display
void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(3);
  display.setCursor(28, 4);

  for (int i = 0; i < 4; i++) {
    if (i < enteredCount) display.print(enteredDigits[i]);
    else display.print('_');
  }

  display.display();
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initMQTTbroker();

  Wire.begin(OLED_SDA, OLED_SCL);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();

  irrecv.enableIRIn();

  pinMode(BUZZER_PIN, OUTPUT);
  analogWriteRange(255);
}

void loop() {
  if (!mqttClient1.connected()) mqttConnect();
  mqttClient1.loop();

  updateBuzzer();

  if (!waitingForServer && irrecv.decode(&results)) {
    int digit = irCodeToDigit(results.value);

    if (digit != -1 && enteredCount < 4) {
      enteredDigits[enteredCount++] = digit;
      updateDisplay();

      if (enteredCount == 4) {
        waitingForServer = true;

        String payload = String(enteredDigits[0]) + " " +
                         String(enteredDigits[1]) + " " +
                         String(enteredDigits[2]) + " " +
                         String(enteredDigits[3]);

        mqttClient1.publish(PubCode, payload.c_str());
      }
    }
    irrecv.resume();
  }
}