/*
 * Serrure à badge — Kaihatsu Lab
 * RC522 (SPI) + écran OLED SSD1306 (I2C) + servomoteur sur ESP32.
 * Un badge autorisé affiche « ACCES OK » et ouvre le servo trois secondes.
 *
 * Bibliothèques : MFRC522, Adafruit GFX, Adafruit SSD1306, ESP32Servo.
 */
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP32Servo.h>

// RC522 (SPI) : SCK 18, MISO 19, MOSI 23
const uint8_t PIN_SS  = 5;
const uint8_t PIN_RST = 27;             // pas 22 : le GPIO 22 est déjà le SCL de l'écran

// OLED (I2C) : SDA 21, SCL 22
#define SCREEN_W  128
#define SCREEN_H  64
#define OLED_ADDR 0x3C

// Servo
const uint8_t  PIN_SERVO    = 13;
const int      ANGLE_CLOSED = 0;
const int      ANGLE_OPEN   = 90;
const uint32_t OPEN_MS      = 3000;

// Badges autorisés (UID de 4 octets) : ajoute une ligne par badge
const byte AUTHORIZED[][4] = {
  {0x04, 0xA3, 0x7B, 0x2C},
};
const uint8_t AUTHORIZED_COUNT = sizeof(AUTHORIZED) / sizeof(AUTHORIZED[0]);

MFRC522 rfid(PIN_SS, PIN_RST);
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, -1);
Servo door;

bool isAuthorized() {
  if (rfid.uid.size != 4) return false;
  for (uint8_t i = 0; i < AUTHORIZED_COUNT; i++) {
    if (memcmp(rfid.uid.uidByte, AUTHORIZED[i], 4) == 0) return true;
  }
  return false;
}

String uidToString() {
  String s;
  for (byte i = 0; i < rfid.uid.size; i++) {
    if (rfid.uid.uidByte[i] < 0x10) s += '0';
    s += String(rfid.uid.uidByte[i], HEX);
    if (i < rfid.uid.size - 1) s += ' ';
  }
  s.toUpperCase();
  return s;
}

void screen(const char* title, const String& uid, const char* result) {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(title);
  display.setCursor(0, 14);
  display.print(uid);
  display.setTextSize(2);
  display.setCursor(0, 40);
  display.print(result);
  display.display();
}

void setup() {
  Serial.begin(115200);
  SPI.begin(18, 19, 23, PIN_SS);
  rfid.PCD_Init();

  Wire.begin(21, 22);
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);

  door.setPeriodHertz(50);
  door.attach(PIN_SERVO, 500, 2400);    // impulsions min et max en microsecondes
  door.write(ANGLE_CLOSED);

  screen("BADGE", "", "");
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  String uid = uidToString();
  bool ok = isAuthorized();
  screen("BADGE", uid, ok ? "ACCES OK" : "REFUSE");

  if (ok) {
    door.write(ANGLE_OPEN);
    delay(OPEN_MS);
    door.write(ANGLE_CLOSED);
  } else {
    delay(1500);
  }

  screen("BADGE", "", "");
  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();
}
