Connected BlackJack

Tired of lugging your tickets around to the casinos? Adopt our solution, the connected BlackJack✨

IntermediateShowcase (no instructions)Over 1 day34
Connected BlackJack

Things used in this project

Hardware components

sodaq explorer
×1
rfid module
×1
matrix keyboard
×1
3mm basswood
×1

Hand tools and fabrication machines

laserbox
Laser cutter allowing for the creation of the box and the aesthetics of the project
graveuse laser
laser engraver for the project visual

Story

Read more

Code

Sodaq explorer code

Arduino
#define DEBUG
#include <Keypad.h>
#include <Sodaq_RN2483.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define uplinkCnt 10

#define debugSerial SerialUSB
#define loraSerial Serial2
#define SS_PIN 10
#define RST_PIN 9

//ajout des clés pour les échanges lora, à récuperer sur TTN
#define OTAA

#ifdef ABP
const uint8_t devAddr[4] = { 0x00, 0x00, 0x00, 0x00 };
const uint8_t nwkSKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0x93, 0xEE, 0x79, 0x2F, 0x05, 0xF7 };
const uint8_t appSKey[16] = { 0xD4, 0x46, 0xCF, 0x21, 0x32, 0x8B, 0xB9, 0xEB, 0xFF, 0x7D, 0x83, 0x9D, 0xC8, 0x79, 0xE4, 0x10 };
#else
// OTAA Keys - Use your own KEYS!
const uint8_t devEUI[8] = { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x07, 0x58, 0x44 };
const uint8_t appEUI[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
const uint8_t appKey[16] = { 0xC9, 0x77, 0xEC, 0xB2, 0x28, 0x4F, 0x9E, 0x01, 0x35, 0xC8, 0x5B, 0xF5, 0xAD, 0xD1, 0x72, 0x3F };
#endif
const uint8_t testPayload[] = { 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x63, 0x68, 0x69, 0x70, 0x20, 0x45, 0x78, 0x70, 0x4c, 0x6f, 0x52, 0x61 };

//Création de deux variables permettant de gérer le lcd et le lecteur rfid
MFRC522 rfid(SS_PIN, RST_PIN);
LiquidCrystal_I2C lcd(0x27, 16, 2);

//inscription en dur du numéro de la table
const byte table = 0x01;

//création de la matrice pour gérer le clavier
const byte ROWS = 4;
const byte COLS = 4;
char hexakeys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' },
};
byte colPins[ROWS] = { 4, 3, 2, 1 };
byte rowPins[COLS] = { 8, 7, 6, 5 };
Keypad customKeypad = Keypad(makeKeymap(hexakeys), rowPins, colPins, ROWS, COLS);
//booléen pour limiter les messages de scan sur le lcd
bool attente = false;
void setup() {
  debugSerial.begin(57600);
  SPI.begin();
  rfid.PCD_Init();
  loraSerial.begin(LoRaBee.getDefaultBaudRate());
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Initialisation");
  LoRaBee.setDiag(debugSerial);  // optional

#ifdef ABP
  if (LoRaBee.initABP(loraSerial, devAddr, appSKey, nwkSKey, true)) {
    debugSerial.println("ABP Keys Accepted.");
  } else {
    debugSerial.println("ABP Key Setup Failed!");
  }
#else
  if (LoRaBee.initOTA(loraSerial, devEUI, appEUI, appKey, true)) {
    debugSerial.println("OTAA Keys Accepted.");
  } else {
    debugSerial.println("OTAA Keys Setup Failed!");
  }
#endif

  debugSerial.println("Sleeping for 1 seconds before starting sending out test packets.");
  for (uint8_t i = 5; i > 0; i--) {
    debugSerial.println(i);
    delay(200);
  }

  if (LoRaBee.send(1, testPayload, sizeof(testPayload)) != NoError) {
    debugSerial.println("First payload was sent");
  }
  Serial.println(F("This code scan the MIFARE Classsic NUID."));
}

void loop() {
  //création de la variable receivedata pour les downlinks et payload pour les uplinks LoRa
  uint8_t receiveData[] = { 0xFF };
  uint8_t payload[] = { 0x9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  //demander de scanner la carte sur le lcd
  if (!attente) {
    infoscan();
    attente = !attente;
  }
  // on demande si une carte est présente devant le lecteur
  if (!rfid.PICC_IsNewCardPresent()) {
    return;
  }
  // Récupération des informations de la carte RFID
  if (!rfid.PICC_ReadCardSerial()) {
    return;
  }
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Verification");
  lcd.setCursor(0, 1);
  lcd.print("en cours...");
  //préparation du payload à envoyer vers ttn pour envoyer l'uid (la commande 8, voir nommenclature)
  for (byte i = 0; i < 5; i++) {
    if (i == 0) {
      payload[0] = 0x08;
    } else {
      payload[i] = rfid.uid.uidByte[i - 1];
      SerialUSB.println(payload[i]);
    }
  }
  switch (LoRaBee.send(1, payload, 6)) {
    case NoError:
      debugSerial.println("Successful transmission.");
      break;
    case NoResponse:
      debugSerial.println("There was no response from the device.");
      erreur();
      break;
    case Timeout:
      debugSerial.println("Connection timed-out. Check your serial connection to the device! Sleeping for 20sec.");
      erreur();
      delay(20000);
      break;
    case PayloadSizeError:
      debugSerial.println("The size of the payload is greater than allowed. Transmission failed!");
      assistance();
      break;
    case InternalError:
      debugSerial.println("Oh No! This shouldn't happen. Something is really wrong! Try restarting the device!\r\nThe program will now halt.");
      assistance();
      while (1) {};
      break;
    case Busy:
      debugSerial.println("The device is busy. Sleeping for 10 extra seconds.");
      erreur();
      delay(10000);
      break;
    case NetworkFatalError:
      debugSerial.println("There is a non-recoverable error with the network connection. You should re-connect.\r\nThe program will now halt.");
      assistance();
      while (1) {};
      break;
    case NotConnected:
      debugSerial.println("The device is not connected to the network. Please connect to the network before attempting to send data.\r\nThe program will now halt.");
      assistance();
      while (1) {};
      break;
    case NoAcknowledgment:
      debugSerial.println("There was no acknowledgment sent back!");
      break;
    default:
      break;
  }
  //on récupère le downlink (solde et code PIN)
  if (LoRaBee.receive(receiveData, 5)) {
    uint32_t PIN = ((uint32_t)receiveData[1] << 8) | receiveData[2];
    uint32_t solde = ((uint32_t)receiveData[3] << 8) | receiveData[4];
    if(receiveData[0]==0x00){
    //processus d'explication pour l'utilisateur pour entrer le solde qu'il désire sur cette table
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Compte trouve!");
    delay(1000);
    lcd.clear();
    lcd.print("Solde : ");
    lcd.setCursor(0, 1);
    lcd.print(solde);
    lcd.print(" euros");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("choix mise table");
    lcd.setCursor(0, 1);
    //on ne peut pas avoir plus de 1000€ sur la table
    lcd.print("max 1000 euros");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("choisir votre");
    lcd.setCursor(0,1);
    lcd.print("mise :");
    //button accueille la touche temporaire sur laquelle l'utilisateur appuie
    char button = 0;
    //mise récupère la somme de tous les chiffres pour former la mise totale qu'il désire
    int mise = 0;
    while (1) {
      button = customKeypad.getKey();
      //Aucune fonction associée aux touches A, B, C et *
      if (button) {
        if (button == 'A' || button == 'B' || button == 'C' || button == '*')
          ;
        //D permet de valider le choix
        else if (button == 'D') {
          lcd.clear();
          break;
        //# reset l'affichage et le choix de la mise en cours, pour permettre à l'utilisateur de se corriger
        } else if (button == '#') {
          lcd.setCursor(6, 1);
          lcd.print("      ");
          lcd.setCursor(6, 1);
          mise = 0;
        } else {
          if (mise * 10 + (button - '0') > 1000 || mise * 10 + (button - '0') > solde)
            ;
          else {
            //ajout de la dernière touche si c'est un chiffre
            mise = mise * 10 + (button - '0');
            lcd.print(button);
          }
        }
      }
    }
    //Maintenant, on explique la procédure à l'utilisateur pour taper son code pin
    lcd.setCursor(0, 0);
    lcd.print("envoi de");
    lcd.setCursor(0, 1);
    lcd.print("votre mise...");
    delay(3000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("mise:");
    lcd.print(mise);
    lcd.print(" euros");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Entrez code");
    lcd.setCursor(0, 1);
    lcd.print("PIN pour valider");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Code PIN:");
    //La variable PIN accueille tous les chiffres entrés dans la variable button à chaque évenement clavier
    int pin = 0;

    while (1) {
      button = customKeypad.getKey();
      if (button) {
        if (button == 'A' || button == 'B' || button == 'C' || button == '*')
          ;
        else if (button == 'D') {
          if (pin < 1000 || pin > 9999)
            ;
          else if (pin != PIN) {
            lcd.clear();
            pin = 0;
            lcd.setCursor(0, 0);
            lcd.print("BAD PIN CODE");
            lcd.setCursor(0, 1);
            lcd.print("Try Again");
            delay(2000);
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Code PIN:");
          } else {
            lcd.clear();
            break;
          }
        } else if (button == '#') {
          lcd.setCursor(9, 0);
          lcd.print("    ");
          lcd.setCursor(9, 0);
          pin = 0;
        } else {
          //le code PIN et sur 4 chiffres, pas plus pas moins, on ne peut pas dépasser 9999
          if (pin * 10 + (button - '0') > 9999)
            ;
          else {
            pin = pin * 10 + (button - '0');
            lcd.print("*");
          }
        }
      }
    }
    //on notifie de l'envoi des informations
    lcd.setCursor(0, 0);
    lcd.print("Envoi mise...");
    //préparation de l'uplink
    payload[0] = 0x09;
    for (byte i = 1; i < 4; i++) {
      payload[i] = rfid.uid.uidByte[i - 1];
    }
    payload[5] = (mise >> 8);  // MSB (Most Significant Byte)
    payload[6] = mise & 0xFF;
    payload[7] = (pin >> 8);  // MSB (Most Significant Byte)
    payload[8] = pin & 0xFF;
    LoRaBee.send(1, payload, 9);
    //réception du downlink pour valider la discussion
    while (LoRaBee.receive(receiveData, 5)) {
      if (receiveData[0] == 0x01) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Succes");
        break;
      }
    }
  }
  else if(receiveData[0]==0x02){
    uint32_t solde = ((uint32_t)receiveData[1] << 8) | receiveData[2];
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Compte trouve!");
    delay(1000);
    lcd.clear();
    lcd.print("Solde : ");
    lcd.setCursor(0, 1);
    lcd.print(solde);
    lcd.print(" euros");
    delay(4000);
  } 
}
  //Ce else est pour le tout début de la boucle, si on scanne la carte et qu'aucun n'est visible dans la base de données
  else {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Aucun compte");
    lcd.setCursor(0, 1);
    lcd.print("present ou actif...");
  }
  delay(10000);
  attente = !attente;
  debugSerial.println("End!");
}

//Message à afficher si aucun message n'a encore été print au début de la boucle
void infoscan() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Scanner carte");
  lcd.setCursor(0, 1);
  lcd.print("pour jouer");
}

//indiquer une erreur système à l'utilisateur mais le faire patienter car le système va le résoudre
void erreur() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Erreur");
  lcd.setCursor(0, 1);
  lcd.print("patientiez...");
}

//indiquer sur le lcd qu'un problème réseau ou autre est présent
void assistance() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.println("  Contacter");
  lcd.setCursor(0, 1);
  lcd.println("  assistance");
}

//fonction servant pour afficher les infos en hexa pour le debug si besoin
void printHex(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

Credits

Louis Gabet
1 project • 0 followers
Nicolas DAILLY
44 projects • 32 followers
Associated Professor at UniLaSalle - Amiens / Head of the Computer Network Department / Teach Computer and Telecommunication Networks
Alexandre Létocart
12 projects • 7 followers
Eloi DILLIES
1 project • 0 followers

Comments