Skip to content

Instantly share code, notes, and snippets.

@Cheaterman
Created September 17, 2025 16:36
Show Gist options
  • Select an option

  • Save Cheaterman/19bd895873d3b494639bec52137e0d8d to your computer and use it in GitHub Desktop.

Select an option

Save Cheaterman/19bd895873d3b494639bec52137e0d8d to your computer and use it in GitHub Desktop.
aptar-rfid-firmware.ino
#include <SoftPWM.h>
#include <SPI.h>
#include "MFRC522.h"
#include "demux.h"
#define DEBUG false
//#define DEBUG true
#define MAX_UIDS 12
#define MAX_ATTEMPTS 3
#define MAX_MISSED_READS 4
#define NUM_READERS 6
#define LED_ANIMATION_DURATION 500
#define LED_MIN_BRIGHTNESS 0
#define LED_MAX_BRIGHTNESS 255
byte MUXER_PINS[] = {3, 4, 5, 6, 7};
byte LED_PINS[] = {14, 15, 16, 17};
MFRC522 const readers[NUM_READERS];
struct MissedRead {
String tag;
int reads;
};
template <unsigned int T> struct MissedReads {
MissedRead missed_read[T];
bool has(String tag) {
for (unsigned int i = 0; i < T; ++i) {
if (missed_read[i].tag == tag) {
return true;
}
}
return false;
}
int& operator[](const String& tag) {
for (unsigned int i = 0; i < T; ++i) {
if (missed_read[i].tag == tag) {
return missed_read[i].reads;
}
}
for (unsigned int i = 0; i < T; ++i) {
if (missed_read[i].tag == "") {
missed_read[i].tag = tag;
missed_read[i].reads = 0;
return missed_read[i].reads;
}
}
}
erase(const String& tag) {
for (unsigned int i = 0; i < T; ++i) {
if (missed_read[i].tag == tag) {
missed_read[i].tag = "";
missed_read[i].reads = 0;
break;
}
}
}
};
MissedReads<MAX_UIDS> missed_reads;
void setup() {
SoftPWMBegin();
for (
unsigned int led_id = 0;
led_id < (sizeof(LED_PINS) / sizeof(byte));
++led_id
) {
SoftPWMSet(LED_PINS[led_id], 0);
SoftPWMSetFadeTime(LED_PINS[led_id], LED_ANIMATION_DURATION, LED_ANIMATION_DURATION);
}
SoftPWMSet(LED_PINS[0], 255);
Serial.begin(115200);
while (!Serial);
demux_init(MUXER_PINS);
SPI.begin();
for (unsigned int reader_id = 0; reader_id < NUM_READERS; ++reader_id) {
readers[reader_id] = MFRC522(reader_id);
}
for (unsigned int reader_id = 0; reader_id < NUM_READERS; ++reader_id) {
const MFRC522& mfrc522 = readers[reader_id];
unsigned int attempts = 0;
byte version = 0;
while(true) {
mfrc522.PCD_Init();
mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
version = mfrc522.PCD_ReadRegister(MFRC522::VersionReg);
byte gain = mfrc522.PCD_GetAntennaGain();
if(
(0x88 <= version && version <= 0x92)
&& gain == mfrc522.RxGain_max
) {
Serial.print("Reader ");
Serial.print(reader_id);
Serial.print(": Version 0x");
Serial.println(version, HEX);
break;
}
else if(++attempts <= 10) {
continue;
}
else {
Serial.print("Reader ");
Serial.print(reader_id);
Serial.print(": Bad version 0x");
Serial.println(version, HEX);
break;
}
}
}
}
void loop() {
unsigned int found = 0;
String uids[MAX_UIDS];
for (unsigned int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
for (unsigned int reader_id = 0; reader_id < NUM_READERS; ++reader_id) {
const MFRC522& mfrc522 = readers[reader_id];
mfrc522.PCD_AntennaOn();
while (true) {
if (
!mfrc522.PICC_IsNewCardPresent()
|| !mfrc522.PICC_ReadCardSerial()
) {
break;
}
char uid[9];
for (unsigned int i = 0; i < mfrc522.uid.size; ++i) {
sprintf(&uid[i * 2], "%02X", mfrc522.uid.uidByte[i]);
}
uids[found] = uid;
#if DEBUG
Serial.print("Found tag ");
Serial.print(uids[found]);
Serial.print(" at index ");
Serial.print(found);
Serial.print(" on attempt ");
Serial.println(attempt);
#endif
if (!missed_reads.has(uids[found])) {
Serial.print("A:");
Serial.println(uids[found]);
}
// Will be incremented in the missed reads check below
missed_reads[uids[found]] = -1;
mfrc522.PICC_HaltA();
found += 1;
}
mfrc522.PCD_AntennaOff();
}
}
#if DEBUG
if (found == 0) {
Serial.println("No tags found");
}
#endif
for (unsigned int i = 0; i < MAX_UIDS; ++i) {
MissedRead& missed_read = missed_reads.missed_read[i];
if (missed_read.tag == "") {
continue;
}
++missed_read.reads;
if (missed_read.reads >= MAX_MISSED_READS) {
Serial.print("R:");
Serial.println(missed_read.tag);
missed_reads.erase(missed_read.tag);
}
}
updateLeds();
}
void updateLeds() {
static unsigned long last_millis = 0;
static unsigned long current_channel = 0;
static byte current_target = LED_MAX_BRIGHTNESS;
unsigned long time = millis() - last_millis;
if (time >= LED_ANIMATION_DURATION) {
last_millis += LED_ANIMATION_DURATION;
if (current_target == 255) {
current_target = LED_MIN_BRIGHTNESS;
}
else {
current_channel = (current_channel + 1) % (sizeof(LED_PINS) / sizeof(byte));
current_target = LED_MAX_BRIGHTNESS;
}
SoftPWMSet(LED_PINS[current_channel], current_target);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment