Smart Cone — IoT Cone Knockover Detection
Construction work zones have no way to detect when safety cones are knocked over or hit. Breaches go unnoticed, putting workers at risk and leaving zero documentation for incidents. Navigation apps like Waze only learn about closures when a driver manually reports it.
An ESP32-powered traffic cone that detects impact/knockover in real-time, sends push notifications, updates a live fleet dashboard, and auto-generates navigation system hazard feeds (Waze CIFS).
Place cones at a site using Setup Mode (phone GPS captures locations)
Knock a cone over → buzzer sounds, LED turns red
Phone buzzes with Ntfy push notification
Dashboard map marker goes red in real-time
CIFS feed auto-updates — ready for Waze/Google Maps integration
Stand cone back up → recovery event, marker returns to green
ESP32 + MPU6050 + Buzzer + LED
│
├──► WiFi (WiFiManager captive portal)
│
└──► MQTT over TLS (HiveMQ Cloud)
│
├──► smartcones/{id}/event → impact, knockover, recovery
├──► smartcones/{id}/status → online/offline (LWT)
├──► smartcones/{id}/telemetry → RSSI, uptime, heap, tilt
└──► smartcones/{id}/command → reset, identify
│
├──► Ntfy.sh ──► Phone push notification
│
└──► Cloudflare Workers Dashboard
├── Fleet Map (Leaflet.js)
├── Stats Bar (cones, online, alerts)
├── Event Log
├── Cone Detail Panel + Health
├── Public Hazard Map (/hazards.html)
├── CIFS Feed Viewer (/cifs-viewer.html)
├── CIFS XML (/api/feed/cifs.xml)
└── CIFS JSON (/api/feed/cifs.json)
Impact detection — acceleration spike > 3g triggers alert
Knockover detection — tilt > 45° sustained for 1 second
Recovery detection — publishes recovery event when cone returns upright
WiFiManager — captive portal for WiFi + Cone ID setup (no hardcoded credentials)
Dynamic Cone ID — configurable per device, stored in ESP32 Preferences
MQTT over TLS — publishes events, status (with LWT), and telemetry to HiveMQ Cloud
Ntfy push notifications — HTTP POST to ntfy.sh on impact/knockover/intrusion
Health telemetry — publishes WiFi RSSI, uptime, free heap, tilt every 30s
MQTT commands — listens for reset (wipe config + restart) and identify (flash LED)
LED status — red = initializing/offline, green = connected & ready, red = alert
Buzzer — continuous buzz on impact/knockover, 3 quick beeps on intrusion
Intrusion detection — PIR sensor support (HC-SR501, code ready, commented out until wired)
Dashboard (Cloudflare Workers + Hono)
Fleet Map — Leaflet.js map with colored cone markers (green/red/orange/gray)
Setup Mode — place cones using phone GPS, stored in Cloudflare KV
Cone Simulator — generates 4 fake cones near real cone for demo
Stats Bar — Total Cones, Online, Alerts Today, Last Incident
Latest Alert Card — shows last event with state, cone ID, timestamp
Event Log — last 50 events with type badges and icons (Font Awesome)
Detail Panel — slide-in panel on marker click: state, coordinates, health, event history
Fleet List — click Total Cones stat to see all cones, click row to pan map
Remove/Reset — remove cone from dashboard + send MQTT reset to device
Identify — flash LED on specific cone from dashboard
Health Monitoring — WiFi signal, uptime, free memory, current tilt (color-coded)
Public Hazard Map — read-only map at /hazards.html for drivers/public
CIFS XML Feed — Waze-compatible road closure feed at /api/feed/cifs.xml
CIFS JSON Feed — same data in JSON at /api/feed/cifs.json
CIFS Feed Viewer — polished page explaining Waze integration with copy buttons
Shortcut Pills — quick links to hazard map, CIFS viewer, feeds
Auto-deploy — GitHub Actions deploys on push to dashboard/**
#
Component
Est. Price (MYR)
GPIO
Links
1
ESP32 dev board (CH340, USB-C)
RM 15
—
MakerHub
2
MPU6050 module (GY-521)
RM 10
SDA=21, SCL=22
MakerHub
3
Active buzzer module (5V)
RM 3
GPIO 19
MakerHub
4
KY-016 RGB LED
RM 3
R=16, G=17
MakerHub
5
HC-SR501 PIR sensor
RM 3
GPIO 23
MakerHub
6
WS2812B 4x4 LED matrix (x2)
RM 10-16
GPIO 18
MakerHub
7
MB102 breadboard power supply
RM 7
—
MakerHub
8
USB to DC barrel jack cable
RM 3
—
—
9
Breadboard (400 holes, x2)
RM 10
—
MakerHub
10
Jumper wires (M-M + M-F)
RM 8
—
MakerHub
11
USB-C data cable
RM 3
—
—
12
Traffic cone (30")
RM 25
—
—
Total
~RM 100-115
GPIO
Function
Component
21
SDA (I2C)
MPU6050
22
SCL (I2C)
MPU6050
16
LED Red
KY-016
17
LED Green
KY-016
18
NeoPixel Data
WS2812B (future)
19
Buzzer
Active buzzer
23
PIR Motion
HC-SR501 (future)
ESP32 — powered via USB-C from laptop (flashing + serial monitor)
MB102 — powered via USB power bank → DC barrel jack
MB102 outputs — 5V rail for buzzer, WS2812B, PIR sensor
Two 400-hole breadboards joined (center rails removed), ESP32 straddles both
syuk/
├── firmware/
│ └── smart_cone/
│ ├── smart_cone.ino # Main sketch — state machine, sensor loop
│ ├── config.h # Pin defs, thresholds, MQTT topics
│ ├── connectivity.h # WiFiManager, MQTT, Ntfy, telemetry, commands
│ └── secrets.h # Generated from .env — NEVER commit
├── dashboard/
│ ├── src/
│ │ └── index.ts # Hono app — /api/config, /api/cones, /api/hazards, CIFS feeds
│ ├── public/
│ │ ├── index.html # Main dashboard
│ │ ├── app.js # MQTT client, map, fleet list, detail panel, simulator
│ │ ├── style.css # Dark theme styles
│ │ ├── hazards.html # Public hazard map (read-only)
│ │ └── cifs-viewer.html # CIFS feed viewer with copy + instructions
│ ├── wrangler.toml # Cloudflare Workers config + KV binding
│ └── package.json # Hono, wrangler deps
├── scripts/
│ └── gen_secrets.sh # Generates secrets.h from .env
├── .github/
│ └── workflows/
│ └── deploy-dashboard.yml # Auto-deploy on push
├── CLAUDE.md # Project instructions for Claude Code
├── AGENTS.md # bd (beads) issue tracking workflow
└── .env # MQTT + Ntfy + CF credentials (gitignored)
Topic
Direction
Payload
Description
smartcones/{id}/event
ESP32 → Cloud
{"cone_id","event","accel_g","tilt_deg","uptime_s"}
Impact, knockover, recovery, intrusion
smartcones/{id}/status
ESP32 → Cloud
{"status":"online|offline"}
Online on connect, offline via LWT
smartcones/{id}/telemetry
ESP32 → Cloud
{"cone_id","rssi","uptime_s","free_heap","tilt_deg"}
Health data every 30s
smartcones/{id}/command
Cloud → ESP32
{"action":"reset|identify"}
Remote reset or LED identify
Method
Path
Description
GET
/api/config
MQTT broker credentials for dashboard
GET
/api/cones
List all cone locations from KV
POST
/api/cones
Create/update cone location
DELETE
/api/cones/:id
Remove a cone
GET
/api/hazards
JSON hazard zones from cone positions
GET
/api/feed/cifs.xml
Waze-compatible CIFS XML feed
GET
/api/feed/cifs.json
CIFS data in JSON format
Event
Trigger
LED
Buzzer
Impact
Acceleration > 3g
Red
2s continuous
Knockover
Tilt > 45° for 1s
Red
2s continuous
Recovery
Tilt < 30° after knockover
Green
—
Intrusion
PIR motion detected
—
3 quick beeps
# Generate secrets from .env
bash scripts/gen_secrets.sh
# Compile
arduino-cli compile --fqbn esp32:esp32:esp32 firmware/smart_cone/
# Flash
arduino-cli upload --fqbn esp32:esp32:esp32 --port /dev/ttyUSB0 firmware/smart_cone/
# Monitor
arduino-cli monitor --port /dev/ttyUSB0 --config baudrate=115200
Dashboard (Cloudflare Workers)
cd dashboard
# Local dev
echo " MQTT_BROKER_WSS=wss://xxx.hivemq.cloud:8884/mqtt" > .dev.vars
echo " MQTT_USER=xxx" >> .dev.vars
echo " MQTT_PASSWORD=xxx" >> .dev.vars
npx wrangler dev
# Production deploy (first time)
npx wrangler kv namespace create CONE_LOCATIONS
# Update wrangler.toml with KV namespace ID
npx wrangler secret put MQTT_BROKER_WSS
npx wrangler secret put MQTT_USER
npx wrangler secret put MQTT_PASSWORD
npx wrangler deploy
# Subsequent deploys — automatic via GitHub Actions on push to dashboard/**
Power on ESP32 → opens "SmartCone-Setup" WiFi AP
Connect from phone → captive portal appears
Enter WiFi credentials + Cone ID (e.g. "cone-002")
ESP32 connects, starts publishing to MQTT
On dashboard, use Setup Mode to place cone at GPS location
File
Contains
Committed?
.env
MQTT, Ntfy, CF credentials
No (gitignored)
secrets.h
Generated from .env
No (gitignored)
.dev.vars
Dashboard local dev MQTT creds
No (gitignored)
GitHub Secrets
CF_ACCOUNT_ID, CF_TOKEN
N/A (GitHub)
Wrangler Secrets
MQTT_BROKER_WSS, MQTT_USER, MQTT_PASSWORD
N/A (Cloudflare)
Adafruit MPU6050
Adafruit Unified Sensor
WiFiManager
PubSubClient
ArduinoJson
Preferences (ESP32 built-in)
Hono (Cloudflare Workers framework)
Leaflet.js (maps)
MQTT.js (WebSocket MQTT client)
Font Awesome 6 (icons)
Uses bd (beads) for task management. See AGENTS.md for workflow.
bd ready # Find available work
bd show < id> # View issue details
bd update < id> --claim # Claim work
bd close < id> # Complete work
© Smart Cone 2026. All Rights Reserved.