I see this all the time in Deviant Ollams videos and i wanted to try my hand at making it.
Parts
Case - https://www.thingiverse.com/thing:6823704
HID R10 Multi-class reader - Find these on Ebay for around 20-50$ used
USB-C Header breakout cable - Amazon.com: 15PCS Type-C Female 2Pin USB Connector Type-C Waterproof with Female Socket Fast Charging Jack Charging Port USB-C Charger Plug Socket : Cell Phones & Accessories
Adafruit Metro Mini (or Arduino Uno) - Amazon.com: HiLetgo 3pcs Nano 3.0 ATmega328P Type-C USB CH340 Controller Board Compatible to Arduino Nano CH340 USB Driver Nano V3.0 ATmega328 : Electronics
Arduino Breakout Board - Amazon.com: Teyleten Robot Nano V3.0 Adapter Expansion Board Module Nano IO Shield Nano IO Expansion Board for Arduino Nano 5pcs : Electronics
I2c 128x64 Oled (Made for Adafruit Stemma Oled) - Amazon.com: Hosyond 5 Pcs 0.96 Inch OLED I2C IIC Display Module 12864 128x64 Pixel SSD1306 Mini Self-Luminous OLED Screen Board Compatible with Arduino Raspberry Pi(Blue and Yellow) : Electronics
5v dc-dc Stepdown module - Amazon.com: ACEIRMC 6pcs High Efficiency Output 5V 5A mini560 Step Down DC-DC Converter Voltage Regulator Buck Stabilized Power Supply Module : Electronics
Secure R10 to top of case with screw and nut
Secure arduino to breakout board and breakout board to bottom case. Secure Stepdown module to bottom case
Connect USB-C Power to Stepdown input, then jumper stepdown output to Arduino at USB in and Ground.
Wire up i2c screen on 5v of arduino and ground as well as the rest of the I2c Pins.
Wire HID R10 to arduino. Secure with screws.
Code
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wiegand.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WIEGAND wg;
// Define the allowed card number
const uint32_t allowedCardNumber = 12345678;
void setup() {
delay(500);
Serial.begin(115200);
while (!Serial) delay(10);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
// Initialize Wiegand reader
wg.begin();
displayData("Ready to Read", "", "", "");
}
//void(* resetFunc) (void) = 0;
void loop() {
delay(500);
if (wg.available()) {
//resetFunc();
uint32_t cardData = wg.getCode(); // Get Wiegand code
// Try to get bit length, set to 0 if unavailable
uint8_t bitLength = 24;
#ifdef wg_getBitCount
bitLength = wg.getBitCount();
#endif
bool pass = validateCard(cardData); // Validate card
String cardType = detectCardType(bitLength); // Determine card type
displayData(String(cardData), String(bitLength), pass ? "Pass" : "Fail", cardType);
// Print to Serial Monitor
Serial.print("Data: ");
Serial.println(cardData);
Serial.print("Bit Length: ");
Serial.println(bitLength);
Serial.print("Pass/Fail: ");
Serial.println(pass ? "Pass" : "Fail");
Serial.print("Card Type: ");
Serial.println(cardType);
}
}
// Function to validate card based on specific allowed card number
bool validateCard(uint32_t data) {
// Check if the card data matches the allowed card number
return data == allowedCardNumber;
}
// Function to detect card type based on bit length
String detectCardType(uint8_t bitLength) {
if (bitLength == 26) {
return "Standard 26-bit";
} else if (bitLength == 34) {
return "Corporate 1000";
} else {
return "Unknown Type";
}
}
// Function to display data on OLED screen
void displayData(String data, String bitLength, String passFail, String cardType) {
delay(50);
display.clearDisplay();
display.setCursor(0, 0);
display.print("Wiegand Data:");
display.setCursor(0, 10);
display.print("Data: ");
display.print(data);
display.setCursor(0, 20);
display.print("Bit Length: ");
display.print(bitLength);
display.setCursor(0, 30);
display.print("Pass/Fail: ");
display.print(passFail);
display.setCursor(0, 40);
display.print("Card Type: ");
display.print(cardType);
display.display();
}
I want to change the code to more reflect how @DeviantOllam does his. With different modes of use (theres a card read mode, and then an access controller mode that makes it function like a door reader. However i want to actually make it a door reader with a mag striker and relay), however i am not THAT great at arduino code. if anyone is please reach out id love to bounce my ideas off of you. I wish his code was opensourced but i dont think he wants to release that IP.
I wanted to get this out to everyone because it really is a cool project and it didnt take too long to spin up. You can turn it wireless by adding a ESPKey to it and then just use a powerbank to power it off of the port.
If there is interest i will do a full write-up and pinout.
R15 to Stepdown Module To Arduino Pinout
HID Reader to Arduino Uno
Black => Gnd
Green => D2
White => D3
Brown => D5
Orange => D6
Yellow => A3
OLED to Arduino
VCC => 5v
GND => Ground
SCL => A5
SDA => A4
POWER IN TO STEPDOWN
BLACK => - IN
RED => + IN
STEPDOWN to Arduino
- out => GND
+ out => VIN
this needed to be seperate than the above, since the above was using an Adafruit Metro with a Stemma connector to OLED.
Current Arduino Code
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wiegand.h>
#include <EEPROM.h>
#define SIGNAL_PIN 13
#define BEEPER_PIN A3
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
#define MAX_CARDS 20
#define EEPROM_CARD_COUNT_ADDR 0 // EEPROM address to store the card count
#define EEPROM_CARD_DATA_ADDR 4 // Starting address for card data
#define CARD_SIZE sizeof(uint32_t) // Size of each card entry (4 bytes for uint32_t)
#define RED_LED_PIN 5 // Define red LED pin
#define GREEN_LED_PIN 6 // Define green LED pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WIEGAND wg;
uint32_t authorizedCards[MAX_CARDS];
uint8_t cardCount = 0;
const uint32_t modeCards[4] = {1001, 1002, 1003, 1004};
uint8_t currentMode = 1;
// Function to count total bits in card data
uint8_t countBits(uint32_t data) {
uint8_t count = 0;
while (data) {
data >>= 1;
count++;
}
return count;
}
// Extract facility code (assuming 26-bit format)
uint8_t getFacilityCode(uint32_t data) {
return (data >> 16) & 0xFF;
}
// Extract card number (assuming 26-bit format)
uint16_t getCardNumber(uint32_t data) {
return data & 0xFFFF;
}
// Load authorized cards from EEPROM with validation
void loadAuthorizedCardsFromEEPROM() {
cardCount = EEPROM.read(EEPROM_CARD_COUNT_ADDR);
// Check if the card count is valid; if not, assume no cards are stored
if (cardCount > MAX_CARDS) {
cardCount = 0; // Reset to 0 if invalid count
return; // Skip loading if there are no valid cards
}
// Load each card from EEPROM into the authorizedCards array
for (uint8_t i = 0; i < cardCount; i++) {
uint32_t cardData = 0;
for (uint8_t j = 0; j < CARD_SIZE; j++) {
cardData |= EEPROM.read(EEPROM_CARD_DATA_ADDR + i * CARD_SIZE + j) << (8 * j);
}
authorizedCards[i] = cardData;
}
}
// Save authorized cards to EEPROM
void saveAuthorizedCardsToEEPROM() {
EEPROM.write(EEPROM_CARD_COUNT_ADDR, cardCount);
for (uint8_t i = 0; i < cardCount; i++) {
uint32_t cardData = authorizedCards[i];
for (uint8_t j = 0; j < CARD_SIZE; j++) {
EEPROM.write(EEPROM_CARD_DATA_ADDR + i * CARD_SIZE + j, (cardData >> (8 * j)) & 0xFF);
}
}
}
void setup() {
delay(50);
Serial.begin(9600);
// Load cards from EEPROM
loadAuthorizedCardsFromEEPROM();
// Initialize OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, OLED_RESET)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.display(); // Ensure display is clear
display.setTextSize(2);
display.setTextColor(WHITE);
wg.begin();
pinMode(SIGNAL_PIN, OUTPUT);
digitalWrite(SIGNAL_PIN, LOW);
pinMode(BEEPER_PIN, OUTPUT);
digitalWrite(BEEPER_PIN, HIGH); // Ensure beeper is off initially
// Set LED pins as output and turn them off initially
pinMode(RED_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
digitalWrite(RED_LED_PIN, HIGH); // Turn off red LED initially
digitalWrite(GREEN_LED_PIN, HIGH); // Turn off green LED initially
// Display "Read Mode" directly on startup
display.clearDisplay();
display.setCursor(13, 0);
display.print("Read Mode");
display.display();
}
void loop() {
if (wg.available()) {
uint32_t cardData = wg.getCode();
uint8_t bitLength = countBits(cardData); // Manually calculate bit length
uint8_t facilityCode = getFacilityCode(cardData); // Get facility code
uint16_t cardNumber = getCardNumber(cardData); // Get card number
// Debugging: Print card data, bit length, and facility code to Serial Monitor
Serial.print("Card Data: ");
Serial.println(cardData);
Serial.print("Bit Length: ");
Serial.println(bitLength);
Serial.print("Facility Code: ");
Serial.println(facilityCode);
Serial.print("Card Number: ");
Serial.println(cardNumber);
// Beep to indicate card read
beep();
// Check if the scanned card is a mode-changing card
for (uint8_t i = 0; i < 4; i++) {
if (cardData == modeCards[i]) {
currentMode = i + 1;
displayMode();
return;
}
}
// Perform actions based on the current mode
switch (currentMode) {
case 1: // Read Mode
display.clearDisplay();
display.setCursor(13, 0);
display.setTextSize(2);
display.print("Read Mode");
display.setTextSize(1);
display.setCursor(0, 22);
display.print("Card #: ");
display.print(cardNumber); // Show only the card number part
display.setCursor(0, 32);
display.print("FC: ");
display.print(facilityCode);
display.setCursor(0, 42);
display.print("Bit Len: ");
display.print(bitLength);
display.setCursor(0, 52);
display.print("Raw Data: ");
display.print(cardData);
display.display();
break;
case 2: // Door Mode
if (isAuthorized(cardData)) {
digitalWrite(SIGNAL_PIN, LOW);
delay(50);
digitalWrite(SIGNAL_PIN, HIGH);
displayData("Door Mode", "Authorized", "", "");
} else {
displayData("Door Mode", "Denied", "", "");
}
break;
case 3: // Add Card Mode
addCard(cardData);
displayData("Add Card Mode", "Card Added", "Card Data: " + String(cardData), "");
break;
case 4: // Remove Card Mode
removeCard(cardData);
displayData("Remove Card Mode", "Card Removed", "Card Data: " + String(cardData), "");
break;
}
}
}
// Function to produce a short beep sound
void beep() {
digitalWrite(BEEPER_PIN, LOW);
delay(50);
digitalWrite(BEEPER_PIN, HIGH);
}
// Function to display the current mode on the OLED screen and control LEDs
void displayMode() {
String modeText = "";
// Set the LED state based on current mode
if (currentMode == 3) { // Add Card Mode
digitalWrite(GREEN_LED_PIN, LOW); // Turn on green LED
digitalWrite(RED_LED_PIN, HIGH); // Turn off red LED
modeText = "Add Card Mode";
} else if (currentMode == 4) { // Remove Card Mode
digitalWrite(RED_LED_PIN, LOW); // Turn on red LED
digitalWrite(GREEN_LED_PIN, HIGH); // Turn off green LED
modeText = "Remove Card Mode";
} else {
// Turn off both LEDs for other modes
digitalWrite(GREEN_LED_PIN, HIGH); // Turn off green LED
digitalWrite(RED_LED_PIN, HIGH); // Turn off red LED
// Set mode text
switch (currentMode) {
case 1: modeText = "Read Mode"; break;
case 2: modeText = "Door Mode"; break;
}
}
displayData(modeText, "", "", "");
}
// Function to display text lines on the OLED
void displayData(String mode, String line1, String line2, String line3) {
display.clearDisplay(); // Clear display buffer
display.setTextSize(2);
display.setCursor(13, 0);
display.setTextColor(WHITE);
display.print(mode);
display.setTextSize(1);
display.setCursor(0, 20);
display.print(line1);
display.setCursor(0, 30);
display.print(line2);
display.setCursor(0, 40);
display.print(line3);
display.display(); // Refresh display to show updated content
}
// Function to check if a card is authorized
bool isAuthorized(uint32_t data) {
for (uint8_t i = 0; i < cardCount; i++) {
if (authorizedCards[i] == data) {
return true;
}
}
return false;
}
// Function to add a card to the authorized list
void addCard(uint32_t data) {
if (cardCount < MAX_CARDS && !isAuthorized(data)) {
authorizedCards[cardCount] = data;
cardCount++;
saveAuthorizedCardsToEEPROM(); // Save changes to EEPROM
}
}
// Function to remove a card from the authorized list
void removeCard(uint32_t data) {
for (uint8_t i = 0; i < cardCount; i++) {
if (authorizedCards[i] == data) {
for (uint8_t j = i; j < cardCount - 1; j++) {
authorizedCards[j] = authorizedCards[j + 1];
}
cardCount--;
saveAuthorizedCardsToEEPROM(); // Save changes to EEPROM
break;
}
}
}