Technical

System architecture & data flow

The models, logic, and API behind the system. Reference material.

%%{init: { "flowchart": { "htmlLabels": false, "diagramPadding": 16 } } }%%
flowchart TD
    BS[Bell Schedule] --> CP[Class / Period]
    CP --> REG["Registration:
Student + Face + Computer + RFID Tag"]
    REG --> RFID["RFID System
Front door / Back door / Room"]
    REG --> CAM["Camera System
Spatial presence, face-scanning system"]
    RFID --> CORR[Correlation / Intelligence Engine]
    CAM --> CORR
    CP -. expected enrollment .-> CORR
    BS -. attendance window .-> CORR
    CORR --> PRES[Attendance / Presence State]

Target architecture: RFID and camera events meet in one correlation engine, gated by the bell schedule.

sequenceDiagram
    participant Bell as Bell Schedule
    participant Win as Attendance Window
    participant Door as Door Sensors (RFID)
    participant Cam as Camera System
    participant Eng as Correlation Engine

    Bell->>Win: Period starts -> window opens
    loop While window is open
        Door->>Eng: tag read (device event)
        Cam->>Eng: face/spatial event (student event)
        Eng->>Eng: correlate device + student signals
    end
    Bell->>Win: Period ends -> window closes
    Note over Eng: Any tag/camera event outside the window is logged but NOT counted as attendance

Attendance window: taps only count while the period's window is open.

%%{init: { "htmlLabels": false, "flowchart": { "htmlLabels": false } } }%%
stateDiagram-v2
    [*] --> ABSENT
    ABSENT --> PRESENT: device + student seen in window
    ABSENT --> TARDY: first seen after grace period
    PRESENT --> DEVICE_PRESENT_STUDENT_AWAY: student signal drops
    DEVICE_PRESENT_STUDENT_AWAY --> PRESENT: student signal returns
    PRESENT --> LEFT_EARLY: both signals stop before window close
    PRESENT --> DEVICE_ABSENT_STUDENT_PRESENT: device signal drops, student still seen
    TARDY --> PRESENT: settles once both signals confirmed
    [*] --> DEVICE_ONLY_NO_CONSENT: device seen, camera opted out
    [*] --> TAMPER_DETECTED: tag reports tamper
    [*] --> IGNORED_OUT_OF_WINDOW: read outside attendance window

Presence states: what each student can be in, each period.

%%{init: { "flowchart": { "htmlLabels": false, "diagramPadding": 16 } } }%%
flowchart LR
    A[New student enrolled] --> B["Registration:
capture Face ID,
assign Computer,
issue RFID Tag"]
    B --> C[Tag: ACTIVE]
    C -->|Tag lost or damaged| D[Tag: RETIRED]
    C -->|Tamper signal received| E[Tag: TAMPERED]
    D --> F[Reissue new tag]
    E --> F
    F --> C
    C -->|Student unenrolled/graduated| G["Tag: RETIRED
Computer unlinked"]

Registration and tag lifecycle: how a tag is issued, flagged, and replaced.

Technical details

About the technical design

How the RFID and camera signals get correlated into an attendance record, the most-developed of the three input tracks, for readers who want the mechanism and not just the pitch. QR follows the same event API once it's generalized.

System Architecture

For the RFID and camera pairing: two independent event sources feed a correlation engine that is also aware of the bell schedule and per-period enrollment. QR would feed the same engine as a third source once integrated.

RFID + Camera Target Architecture

This is the correlation design for the RFID and camera tracks specifically, the most-developed of the three inputs. QR follows the same event API once it's generalized in Cycle 1.

This is the target architecture the team is building toward across four phases; the current prototype implements the leftmost slice of it. Full detail, including the presence-state machine and data model, is on the Technical page. Design rationale and a literature review of comparable systems, confirming this approach against prior work, is on the Research page.

Attendance Window Logic

The bell schedule doesn't just define start and end times for tardy math. It defines a window during which reads count as attendance at all.

Presence State Model

For the RFID and camera pairing: two independent signals, device via RFID and student via camera, get correlated into one of these states per student per period.

StateDevice (RFID)Student (Camera)Meaning
PRESENTseenseenNormal attendance
TARDYfirst seen after window open plus grace periodseenArrived late
DEVICE_ONLY_NO_CONSENTseenopted outStudent declined face scanning by policy. Presence is based on the device signal alone and is not flagged as anomalous or incomplete.
DEVICE_PRESENT_STUDENT_AWAYseennot currently seenBathroom or temporary-exit scenario. Not counted as absent or left early.
LEFT_EARLYseen, then both signals stop before window closestopped before window closeExited before period ended
DEVICE_ABSENT_STUDENT_PRESENTnot seenseenStudent present without their registered device. Flagged for manual review (forgot laptop, borrowed one, or tag failure).
ABSENTnot seennot seenNo signal at all during the window
TAMPER_DETECTEDtag reports tamper/invalidn/aTag physically compromised. Excluded from attendance and flagged for tag reissue.
IGNORED_OUT_OF_WINDOWseenn/aRead occurred outside the attendance window. Logged, not counted.

Registration & Tag Lifecycle

Data the system needs to persist

No code yet for the full model. This is the entity list, not a schema.

  • Students: id, name, enrolled periods
  • Periods: id, name, start time, end time, expected enrollment
  • Computers: id, assigned student
  • RFID Tags: UID, linked computer, status (active/tampered/retired), issue date
  • Registrations: student, face ID, computer, and tag, the four-way binding
  • Doors: id, room, side (front/back)
  • RFID Events: tag UID, door, timestamp
  • Camera Events: student (via face match), timestamp, spatial zone; stub interface into the face-scanning system
  • Attendance Windows: derived per period from the bell schedule
  • Presence Records: computed state per student per period, the output of the correlation engine

API Endpoints (merged into OCS, PRs #96 and #97)

MethodPathAuthWhat it does
POST/api/presence/eventDevice keyRecord a tap (source rfid) or a camera result (source camera)
POST/api/rfid/scanDevice keyRoute the tap reader uses; forwards to /api/presence/event
GET/api/presence/rooms/{id}/taps/pendingDevice keyCamera asks for taps from the last 15 seconds
GET/api/presence/rooms/{id}/statusTeacher / AdminEach person's state now (the dashboard polls this every 2 s)
GET, POST/api/presence/roomsTeacher / AdminList or create rooms: a class, a club meeting, or an event
GET, PUT, DELETE/api/presence/rooms/{id}Teacher / AdminOne room: name, schedule, members
POST, DELETE/api/presence/rooms/{id}/members/{uid}Teacher / AdminAdd or remove a room member
GET, POST/api/presence/rooms/{id}/periodsTeacher / AdminCurrent period; start a manual period (overrides the schedule)
GET, POST/api/presence/schedulesTeacher / AdminSchedules stored in the database, editable on demand
GET, PUT, DELETE/api/presence/schedules/{id}Teacher / AdminOne schedule and its blocks
GET, POST, DELETE/api/presence/schedules/overridesTeacher / AdminSpecial days: another schedule, or no periods, on one date
GET, POST/api/rfid/tagsTeacher / AdminList cards; register a card to a person by uid
DELETE/api/rfid/tags/{tag_uid}Teacher / AdminRemove a card
GET/api/rfid/rooms/{id}/eventsTeacher / AdminLatest 200 taps in a room
GET, POST/api/export/presence, /api/export/import/presenceAdminBackup and restore of every presence table during OCS upgrades
POST/api/authenticatePasswordOCS login; sets the session cookie

Device key = the X-API-Key header the tap reader (and later the camera) sends (RFID_API_KEY on the server). Teacher / Admin = a normal OCS login. Example tap, the request the tap reader sends and what Flask returns:

POST /api/presence/event
    X-API-Key: (device key)

    {
      "event_id": "3f2b8c1e-6a1d-4f7e-9b0a-2d5c7e9f1a44",
      "source": "rfid",
      "room_id": 1,
      "occurred_at": "2026-10-12T15:44:07.123Z",
      "tag_uid": "1234567890",
      "device_id": "reader-1"
    }

    200 OK (trimmed)
    {
      "status": "accepted",
      "duplicate": false,
      "event": {
        "uid": "vibham",
        "user_name": "Vibha Mandayam",
        "type": "enter",
        "period": "Demo Period",
        "occurred_at": "2026-10-12T15:44:07.123Z",
        "received_at": "2026-10-12T15:44:07.410Z"
      }
    }

Errors: 401 bad or missing device key, 404 unregistered tag ("status": "unregistered"), 400 missing fields or a timestamp without a timezone. A resent event_id returns the original record with "duplicate": true.

Testing the API: api/presence.http

#MethodRequestDashboard shows
1POSTLog inTeacher session
2POSTStart demo periodEveryone: not yet arrived
3POSTTapPresent
4POSTTap againStepped Out
5POSTSame tap, resentNot counted twice
6POSTFake device key401 rejected
7GETStatusSame data as dashboard

The same sequence as our Postman collection, now as the REST Client file api/presence.http in the OCS backend, the new testing process for OCS. It runs against local or deployed OCS, and the dashboard updates live.

Current Software Stack (RFID Track)

  • Reader: Raspberry Pi 4 with a Waveshare PN532 NFC HAT (UART mode), reading S50 (MIFARE Classic 1K) cards at 13.56 MHz. Contact tap, not the UHF doorway design above. The same reader.py also runs an RC522 board, and card numbers match between the two.
  • Backend: the presence API is merged into the shared OCS Flask backend (PRs #96 and #97). Staff routes require an OCS Teacher or Admin login; readers use the device key. Deployment to the OCS server is pending.
  • Database: OCS's own database (MySQL in production); the presence tables are included in OCS's export/import, so upgrades that rebuild the database keep every card, period, and tap.
  • Frontend: the live dashboard works on our local OCS today and moves to OCS Pages after the deploy. Next: students register their own cards from a My card page (built, PR after deploy).
The full RFID and camera architecture above is the target design for that pairing. The current running system implements only the leftmost slice of it: RFID read, log, dashboard, with no camera correlation yet, and that gap is intentionally not being closed until the research cycles justify it. See Roadmap for what's paused and why.

Open Technical Questions

  • Bag-orientation reliability: a tagged laptop inside a closed backpack has no guaranteed tag-to-reader orientation, and UHF read reliability drops off-axis. Not yet solved. See Research for candidate antenna designs that address this directly.
  • Camera system interface: what the face-scanning system exposes (API, event stream, or file) still needs to be defined, including how it reports a student who has opted out.
  • Tag mounting standard: a single mounting spot on the laptop (for example, the underside near a plastic vent) so tag type (on-metal versus standard) can be standardized across devices.
Project TeamRuta Sirdeshmukh, Vibha Mandayam, Kush Shah
MentorDr. Douglas Fenner
Working prototype · UHF pilot in progress
View Repo