← → to advance  ·  Ctrl+P for PDF
Small IoT Research ProjectMITE Cohort 19  ·  RUPP

DrowsyGuard

A USD 27 retrofit device that watches a driver's eyes, mouth and head on the ESP32‑S3 itself, and speaks a warning — with no cloud anywhere in the alert path.

Project leader
Seng Phirum
Members
Seang Chanviwath · Suon Mesa · Theng Rathrongroeung · Lov Kimheng
Lecturer
Dr. Chey Chan Oeurn
IoT: Technology and Application
Status
Prototype running on hardware
Detection accuracy in progress
02/08
The problem

Why one frame is not enough

A closed eye is not drowsiness.
A closed eye for long enough is.

NORMAL BLINK 0.24 s closed — under 0.40 s, ignore no alert MICROSLEEP ≥ 1.00 s closed — speak now 1.00 s ALERT FIRES 0 s 0.5 s 1.0 s 1.5 s
Same eye state, opposite meaning. Only the duration separates them — which is why the device decides over time, never on one frame.
  • 01

    Single-frame alarms cry wolf

    Normal blinking, looking down at the gear stick, glasses glare and a moment of lost face all read as “eyes closed” to a per-frame classifier.

  • 02

    Appearance is not eyelid state

    Our own whole-face network scored 0.81 in validation but 0.57 on drivers it had never seen — it had learned who the driver was, not whether their eyes were shut. It was removed.

  • 03

    Cambodia's older fleet has no monitoring

    Driver monitoring ships only in new vehicles. A USD 27 retrofit that runs offline is the only version of this that reaches an existing car.

03/08
Concept — cues

Four signs, each with its own clock

What the camera measures

FACE BOX · score 1.00 EYES CLOSED ≥ 1.00 s LONG BLINK > 0.40 s YAWN ≥ 1.20 s HEAD NOD 0.3–1.5 s 5 landmarks eyes · nose mouth corners 240×240 RGB565 · 15 fps · rolling per-driver baseline
Yawn and nod are geometry from those same five points, so they cost no extra model — and the baseline is per-driver, so anatomy can never become the signal.
PERCLOS3.0 s

Share of the last three seconds with the eyes closed. The best-established fatigue measure, so it carries most of the weight.

weight 0.55
Long blink>0.40 s

Slower than a blink, shorter than a microsleep. Blink speed drops before a driver knows they are tired.

weight 0.20
Yawn≥1.20 s

Mouth corners drop and pull inward. Anything faster is speech, not a yawn.

weight 0.15
Head nod0.3–1.5 s

Down and back. Two independent pitch channels must agree, so an open mouth cannot fake it.

weight 0.10
04/08
Concept — decision

From four numbers to one spoken word

Risk has to be held, not touched

risk 1.0 0.0 TRIGGER 0.55 above for 0.17 s → no alert SPEAK “Microsleep” then 4.0 s cooldown held 0.55 s = 8 frames @ 15 fps 0 s 2 s 4 s 6 s
One accumulator, one threshold, one hold. The identical logic exists twice on purpose — C++ on the device, Python on the bench — so a threshold tuned in the dashboard transfers unchanged.
  • 01

    Fuse, weighted

    risk = 0.55 PERCLOS + 0.20 blink + 0.15 yawn + 0.10 nod. The three minor cues corroborate; none of them can raise an alarm alone.

  • 02

    Hold, then fire

    Risk must stay above 0.55 for 0.55 s. A single noisy frame decrements the streak instead of resetting it, so one dropout inside a real closure cannot end it.

  • 03

    Name the cause out loud

    “Drowsy”, “Microsleep”, “Yawning”, “HeadNod” — a named warning is actionable in a way a chime is not. Three announcements per episode, then quiet.

  • 04

    Wi-Fi is never load-bearing

    The alert path never touches the network. A dropped dashboard costs diagnostics, never safety.

05/08
Process flow

One safety path, two support paths

Everything that matters happens on the board

ON THE ESP32‑S3 — NO NETWORK — ~23 ms PER FRAME Capture OV5640 · 240² Find face every 3rd frame track between Read cues eye model + landmark geometry Fuse risk 0.55/0.20/ 0.15/0.10 Hold 0.55 s temporal filter + reflex guard Speak “Microsleep” I2S · 3 W speaker frame 5 pts cues risk fire Workstation · offline train & export the eye model flashed once · 46 KB Phone dashboard · SoftAP preview · risk · logs · self-test copy of one frame, dropped if nobody is watching Solid = the safety path. Dashed = support only; cut either one and the warning still fires.
Capture → find → read → fuse → hold → speak. Six stages inside a 23 ms budget, and nothing in that line needs the internet.
06/08
Equipment

Bill of materials — all bought locally

Nine parts, one breadboard, no display

ESP32‑S3‑WROOM‑1 + OV5640

N16R8 · 8 MB PSRAM

Camera, on-device inference and Wi-Fi in one board. $7.50

MAX98357A amplifier

I2S · class D · filterless

Turns the spoken clip into something audible. $2.00

Speaker

4 Ω / 3 W · 40 × 22 mm

The only thing the driver actually perceives. $0.75

Breadboard MB102

830 point · solderless

Keeps the wiring repeatable between test sessions. $1.50

Jumper wires & headers

Dupont · 1 set

Seven connections: power, ground and three I2S lines. $2.00

DATA

USB‑C cable

data-capable · 5 V

Flashing and bench power. Charge-only cables will not do. $1.50

5V

5 V USB supply

regulated adapter

Prototype power; an automotive supply comes at pilot stage. $2.00

0.42A

Inline USB power meter

measurement instrument

Turns “power draw” from a claim into a number. $5.00

Enclosure & mount

prototype materials

Same camera distance and angle every session, or the data is not comparable. $5.00

Laptop and phone are excluded — the team uses existing devices. No display, by decision: a 1.8” panel cost five GPIOs, 150 KB of PSRAM and a per-frame blit to show 8-pixel text to one person. The phone browser shows strictly more, and makes the same numbers scriptable for the acceptance tests.

$27.25Bill of materials
07/08
Where we are

Measured on the bench, not estimated

The board runs. The model does not see well enough yet.

The assembled Drowsy Guard prototype: an ESP32-S3-WROOM board with its OV5640 camera on a mini breadboard, wired to a MAX98357A amplifier and a 3 watt speaker. ESP32-S3 MAX98357A OV5640 + ESP32‑S3 I2S AMPLIFIER 4 Ω / 3 W 3 × I2S 5 V + GND 7 connections · no display · USB powered
The bench build, 26 Aug 2026. Seven wires, no display, powered from USB — and the sensor turned out to be an OV5640, not the OV3660 the listing advertised.
Frame rate, idle / tracking a facetarget ≥ 10 fps 19.7 / 10.7–13.6 fps met
Face detection on a real person20 attempts, confidence 1.00 20 / 20 met
Cost per stage39 ms detect · 15.7 ms per eye within budget met
PERCLOS moving on live video0.00–0.22 at rest, eye model bound live met
Eye-state accuracy in visible lightbase model is infrared-trained; 95.8% in its own domain AUC 0.62 the gap
Alert end to end, and cues against a real yawnlogic and signs now correct; nobody has yawned at it yet not yet run next

What running it on hardware taught us: the sensor is mounted upside down, so every frame arrives mirrored. That reversed the eye pair and flipped the sign of every vertical measurement — jaw drop read −1.2 instead of +1.2 and fell as the mouth opened, so the yawn cue had never once been able to fire. No amount of desk testing would have found it.

08/08
Conclusion

Where the remaining effort goes

The hardware is done arguing.
The next win is in the model.

Our focus from here is training the lightweight on-device model — 11,250 parameters, 46 KB — on visible-light eye-state data, split by subject, so it reads eyelids instead of faces.

  • →

    Fine-tune in the target domain

    Infrared-trained, it does not transfer to 45 px visible-light eye crops: AUC 0.62. Grayscale, CLAHE, inversion and four patch scales did not recover it. The fix is labelled data in our own domain.

  • →

    Judge it per driver, never on average

    Subject-independent splits only. The 0.81-to-0.57 collapse is the standard any replacement has to clear.

  • →

    Then tune the thresholds on real video

    Every period on slide 3 is literature-informed and tested on synthetic traces. Labelled yawn and nod footage turns them from defaults into measurements.

Detection quality
F1 ≥ 0.80and event recall ≥ 0.85
False warnings
≤ 1 / hourper awake control hour
Response
≤ 2 safter sustained risk
Reliability
4 hcontinuous, no crash

Why not a large model: the most accurate public drowsiness networks are 70–343 MB at 224×224 and cannot run on an ESP32‑S3 at any frame rate. The eye-closure route is the one that fits. Research prototype only — not a certified safety device.