Skip to main content

2. Program

2.1 My contribution: RFID module programming

I handled RFID module programming, read/write testing, and PC backend dashboard development and integration. Built the full tag identification and data reporting pipeline using Wio Terminal with Grove RFID Reader.

Wio Terminal

RFID Reader

2.2 Collaboration

Servo side: Used Guannan's servo routines and gate-angle parameters, validated on the actual STS3215 motor to ensure repeatable motion (no double-release, no column stall).

RFID side: Aligned with Dolphin on card data format and read flow, so Wio Terminal outputs clear "authorized / unrecognized" results rather than raw UID noise.

Hardware handoff: After integrated logic passed bench testing, Henry Yu carried the same pin assignments and bus configuration into the final cabinet harness.

System Design

2.3 Implementation Process (Step by Step)

System Flow

2.3.1 Subsystem validation

  • Verified Wio LCD and Grove ports functional

  • Confirmed RFID reads via serial logging

  • Tested servo closed→open→closed cycle with safe delays

2.3.1 Merged into single firmware

  • Integrated RFID event handler and servo control into one state machine (idle → card detected → validate → dispense → return to idle)

  • Added on-screen status text for real-time bench test visibility

2.3.2 Timing & robustness tuning

  • Added debounce/ignore window after each read to prevent multiple triggers from one tap

  • Retested with various tag placement speeds for consistent response

2.3.3 Integration test checklist

  • Logged pass/fail for "RFID only," "servo only," and "end-to-end dispense" to provide Meia a known-good firmware baseline for later prototype testing

2.3.4 Issue logging

  • When issues occurred (e.g., servo missed latch position, RFID misread under vibration), attributed root causes to electrical interference, mechanical slack, or code ordering, so the mechanical team could adjust the column or arm in parallel.

2.4 Integration Testing

2.5 Source Code

All Arduino sketches and supporting libraries used in this project are tracked in the site repository and can be browsed or downloaded directly from GitLab.

2.5.1 Repository Layout

FolderPurpose
code_RFID/Standalone RFID reader / writer sketches and the Emakefun_RFID driver
code_RFID/rfid-main/reading_rfid.ino and writing_rfid.ino — read/write a TRUE token to MIFARE block 4
code_servo/scservo-main/Waveshare SCServo library (SCSCL + SMS/STS) used by every servo sketch
code_servo/STS3215_change_id/One-off sketch to assign IDs (1, 2, …) to each STS3215 on the bus
code_servo/STS3215_demo_code/Minimal demo to verify a single servo on the daisy-chain
code_servo/STS3215_test_code/Multi-servo SyncWritePosEx test used for the gate motion
test_for_assemble/First integrated sketch (test_for_assemble.ino) — RFID + LCD + servo on the Wio Terminal
vending_machine_final_code/Final firmware (vending_machine_final_code.ino) running on the prototype in the videos above

2.5.2 Final Firmware — vending_machine_final_code.ino

#include <Wire.h>
#include <TFT_eSPI.h>
#include "Emakefun_RFID.h"
#include "SCServo.h"

TFT_eSPI tft;

#define RFID_ADDR 0x28
MFRC522 mfrc522(RFID_ADDR);
bool rfidReady = false;

#define SERVO_NUM 2
SMS_STS st;

byte ID[SERVO_NUM] = {1, 2};
u16 Speed[SERVO_NUM] = {1500, 1500};
byte ACC[SERVO_NUM] = {50, 50};
s16 Pos[SERVO_NUM] = {1024, 1024};

const s16 HOME_POS = 1024;
const s16 TRIGGER_POS = 0;
const byte DISPENSE_CYCLES = 2;
const unsigned long DISPENSE_FORWARD_DELAY_MS = 1800;
const unsigned long DISPENSE_RETURN_DELAY_MS = 1200;

void moveBothTo(s16 target) {
for (int i = 0; i < SERVO_NUM; i++) Pos[i] = target;
st.SyncWritePosEx(ID, SERVO_NUM, Pos, Speed, ACC);
}

void runDispenseCycles(byte cycles) {
for (byte cycle = 0; cycle < cycles; cycle++) {
moveBothTo(TRIGGER_POS);
delay(DISPENSE_FORWARD_DELAY_MS);
moveBothTo(HOME_POS);
delay(DISPENSE_RETURN_DELAY_MS);
}
}

void loop() {
if (!rfidReady) {
if (digitalRead(WIO_KEY_A) == LOW) { initRFID(); delay(500); }
return;
}
if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;

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

byte status = mfrc522.PCD_Authenticate(
MFRC522::PICC_CMD_MF_AUTH_KEY_A, 7, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) { stopRFID(); return; }

byte buffer[18]; byte size = sizeof(buffer);
if (mfrc522.MIFARE_Read(4, buffer, &size) == MFRC522::STATUS_OK) {
bool matched = (buffer[0] == 'T' && buffer[1] == 'R' &&
buffer[2] == 'U' && buffer[3] == 'E');
if (matched) runDispenseCycles(DISPENSE_CYCLES);
}
stopRFID();
}

2.6 Code & References