The SENTRICK™ device is not a concept — it is a fully operational embedded system running on real hardware, tested in the field, and streaming live GPS coordinates and camera frames to Firebase every two seconds. This article documents the complete technical architecture of SENTRICK™ MASTER CORE v2.3: the firmware, the hardware stack, the design decisions, and the specific engineering challenges we solved along the way.
Whether you are building a Med-Tech IoT device, a livestock security system, or a personal safety tracker, the patterns described here — non-blocking GPS handlers, background Firebase writes, and secure HTTP route definitions — apply directly to any production-grade ESP32 project.
System Status — Live Hardware
WiFi
Connected
Camera
Streaming
GPS Fix
12 Satellites
Firebase
Syncing
HTTP Server
Running
OTA Updates
Ready
The Hardware Stack: Three Components, One System
The SENTRICK™ core hardware is deliberately minimal yet powerful. Three primary components form the foundation of the entire system, each chosen for its precision, reliability, and suitability for wearable and field-deployable IoT applications.
Hardware Component Matrix
ESP32-S3-EYE
Main MCU + Camera
Dual-core Xtensa LX7 @ 240 MHz, 8 MB PSRAM, OV2640 camera, WiFi 802.11 b/g/n
SparkFun NEO-M9N GPS
Satellite Positioning
u-blox M9N chip, 25 Hz update rate, 2.5m CEP accuracy, multi-constellation (GPS/GLONASS/Galileo/BeiDou)
Firebase Realtime DB
Cloud Data Layer
REST API over HTTPS, JSON tree at /Sentrick/devices/SENTRICK-BAND-001, sub-100ms latency
The ESP32-S3-EYE development board integrates the camera module directly on the PCB, eliminating the ribbon cable fragility common in other ESP32-CAM variants. Its 8 MB PSRAM is critical for buffering JPEG frames without exhausting the main heap — a common failure point in camera-enabled IoT systems.
The SparkFun NEO-M9N GPS communicates over UART at 9600 baud by default. Its multi-constellation support means the device achieves a GPS fix even in challenging environments — indoors near windows, in dense urban canyons, and across all 190+ countries where SENTRICK™ operates.
The Non-Blocking GPS Architecture: Why It Matters
The most critical architectural decision in the SENTRICK™ v2.3 firmware is the separation of the GPS handler from the Firebase write path. In earlier versions, the gpsHandler function blocked the HTTP server thread while waiting for Firebase to acknowledge the write. This caused the camera stream to stutter and the HTTP server to time out under load.
Anti-Pattern — Blocking GPS Handler (v2.2 and earlier)
// ❌ WRONG: Firebase write blocks the HTTP handler thread
esp_err_t gpsHandler(httpd_req_t *req) {
// Parse GPS data...
String lat = getLatitude();
String lng = getLongitude();
// This blocks for 200-800ms — kills camera stream
firebaseClient.setString("/gps/lat", lat);
firebaseClient.setString("/gps/lng", lng);
httpd_resp_send(req, "OK", 2);
return ESP_OK;
}Correct Pattern — Non-Blocking GPS Handler (v2.3)
// ✅ CORRECT: Handler responds immediately, Firebase write in loop()
// Global state — updated by gpsHandler, consumed by loop()
volatile bool gpsDataPending = false;
String pendingLat = "";
String pendingLng = "";
unsigned long lastGpsWrite = 0;
// HTTP handler — returns immediately, no blocking
esp_err_t gpsHandler(httpd_req_t *req) {
char buf[256];
int ret = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (ret <= 0) return ESP_FAIL;
buf[ret] = '\0';
// Parse JSON body: {"lat":"29.7604","lng":"-95.3698"}
StaticJsonDocument<256> doc;
deserializeJson(doc, buf);
pendingLat = doc["lat"].as<String>();
pendingLng = doc["lng"].as<String>();
gpsDataPending = true; // Signal loop() to write
// Respond immediately — no Firebase blocking
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, "{\"status\":\"queued\"}", -1);
return ESP_OK;
}
// loop() — handles Firebase writes asynchronously
void loop() {
// Camera stream handler runs here (non-blocking)
// ...
// Background GPS → Firebase write (every 2 seconds)
if (gpsDataPending && millis() - lastGpsWrite > 2000) {
Firebase.setString(fbdo, "/Sentrick/devices/SENTRICK-BAND-001/gps/lat", pendingLat);
Firebase.setString(fbdo, "/Sentrick/devices/SENTRICK-BAND-001/gps/lng", pendingLng);
gpsDataPending = false;
lastGpsWrite = millis();
}
}The result is immediate: the HTTP server responds to the GPS bridge script in under 5ms, the camera stream runs at full frame rate, and Firebase receives clean, throttled updates every two seconds — exactly the behavior required for a production safety device.
The esp_camera Library: SCCB Pin Naming Fix
One of the most common compile errors when working with the esp_camera library on newer ESP-IDF versions is a field name mismatch in the camera configuration struct. The SCCB (Serial Camera Control Bus) pin fields were renamed between library versions, and the compiler error is not always obvious.
Compile Error — Incorrect Field Names
// ❌ Old field names — causes compile error on newer esp_camera versions config.pin_sscb_sda = SIOD_GPIO_NUM; // "sscb" → wrong config.pin_sscb_scl = SIOC_GPIO_NUM; // "sscb" → wrong // Error: 'camera_config_t' has no member named 'pin_sscb_sda'
Correct Field Names — SENTRICK™ v2.3
// ✅ Correct field names for esp_camera >= 2.0.x config.pin_sccb_sda = SIOD_GPIO_NUM; // "sccb" (double 'c') → correct config.pin_sccb_scl = SIOC_GPIO_NUM; // "sccb" (double 'c') → correct // Full camera config for ESP32-S3-EYE camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sccb_sda = SIOD_GPIO_NUM; // ← note: sccb not sscb config.pin_sccb_scl = SIOC_GPIO_NUM; // ← note: sccb not sscb config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_VGA; config.jpeg_quality = 12; config.fb_count = 2;
This single character difference — sscb vs sccb — has caused hours of debugging for countless ESP32 developers. The SENTRICK™ firmware standardizes on the correct sccb naming throughout.
Secure HTTP Route Definitions in Arduino ESP32
The ESP-IDF HTTP server (httpd) requires explicit initialization of httpd_uri_t structures. A common mistake is using C99 designated initializer syntax, which can produce unexpected behavior with certain ESP-IDF versions and compilers. The SENTRICK™ firmware uses explicit field-by-field initialization for maximum compatibility and clarity.
SENTRICK™ HTTP Route Registration Pattern
// Explicit struct initialization — safe across all ESP-IDF versions // GPS data endpoint httpd_uri_t gpsUri; gpsUri.uri = "/gps"; gpsUri.method = HTTP_POST; gpsUri.handler = gpsHandler; gpsUri.user_ctx = NULL; httpd_register_uri_handler(server, &gpsUri); // Camera stream endpoint httpd_uri_t streamUri; streamUri.uri = "/stream"; streamUri.method = HTTP_GET; streamUri.handler = streamHandler; streamUri.user_ctx = NULL; httpd_register_uri_handler(server, &streamUri); // Status / health check endpoint httpd_uri_t statusUri; statusUri.uri = "/status"; statusUri.method = HTTP_GET; statusUri.handler = statusHandler; statusUri.user_ctx = NULL; httpd_register_uri_handler(server, &statusUri); // OTA firmware update endpoint httpd_uri_t otaUri; otaUri.uri = "/ota"; otaUri.method = HTTP_POST; otaUri.handler = otaHandler; otaUri.user_ctx = NULL; httpd_register_uri_handler(server, &otaUri);
Each route is registered individually with its own httpd_uri_t struct. This pattern makes it trivial to add authentication middleware, rate limiting, or request logging to individual endpoints without touching the others — a critical requirement for a production safety device that may be accessed over public networks.
The GPS Bridge: Mac → ESP32 → Firebase Data Flow
During development, the SparkFun NEO-M9N GPS module connects to a Mac over USB-serial (appearing as /dev/cu.usbmodem1201). A lightweight Python bridge script reads the NMEA sentences, parses the GPS fix, and forwards the coordinates to the ESP32 HTTP server every two seconds.
Data Flow Architecture
NEO-M9N GPS
UART @ 9600 baud
Python Bridge
pyserial + requests
ESP32-S3
HTTP POST /gps
Firebase
REST API HTTPS
GPS Bridge Script — Python 3 (gps.py)
#!/usr/bin/env python3
"""
SENTRICK™ GPS Bridge — Mac → ESP32 → Firebase
Reads SparkFun NEO-M9N GPS via USB serial,
forwards coordinates to ESP32 HTTP server every 2 seconds.
"""
import serial
import requests
import time
import json
GPS_PORT = "/dev/cu.usbmodem1201" # SparkFun NEO-M9N on Mac
ESP32_IP = "192.168.1.70" # ESP32-S3-EYE local IP
ESP32_URL = f"http://{ESP32_IP}/gps"
INTERVAL = 2.0 # seconds between updates
TIMEOUT = 8.0 # HTTP request timeout
def parse_gga(sentence: str):
"""Parse NMEA GGA sentence → (lat, lng) or None."""
parts = sentence.split(",")
if len(parts) < 6 or parts[2] == "" or parts[4] == "":
return None
# Convert NMEA ddmm.mmmm → decimal degrees
raw_lat, lat_dir = parts[2], parts[3]
raw_lng, lng_dir = parts[4], parts[5]
lat = int(raw_lat[:2]) + float(raw_lat[2:]) / 60
lng = int(raw_lng[:3]) + float(raw_lng[3:]) / 60
if lat_dir == "S": lat = -lat
if lng_dir == "W": lng = -lng
return round(lat, 6), round(lng, 6)
def main():
ser = serial.Serial(GPS_PORT, 9600, timeout=1)
print(f"[GPS Bridge] Connected to {GPS_PORT}")
last_send = 0
while True:
line = ser.readline().decode("ascii", errors="ignore").strip()
if not line.startswith("$GPGGA") and not line.startswith("$GNGGA"):
continue
result = parse_gga(line)
if result is None:
continue
lat, lng = result
if time.time() - last_send < INTERVAL:
continue
payload = {"lat": str(lat), "lng": str(lng)}
try:
r = requests.post(ESP32_URL, json=payload, timeout=TIMEOUT)
print(f"[GPS] {lat}, {lng} → ESP32: {r.status_code}")
last_send = time.time()
except requests.exceptions.RequestException as e:
print(f"[GPS] Send failed: {e}")
if __name__ == "__main__":
main()The bridge script is intentionally stateless and fault-tolerant. If the ESP32 is unreachable (rebooting, OTA update in progress), the script simply logs the failure and retries on the next interval. This design mirrors the resilience requirements of a real-world safety device that must continue operating even when individual subsystems temporarily fail.
Firebase Realtime Database: Data Structure & Live Tracking
The SENTRICK™ Firebase tree is organized for both per-device historical data and a global live-tracking feed. This dual-path structure allows the mobile app and web dashboard to subscribe to either the device-specific stream or the aggregated live view without additional server logic.
Firebase JSON Tree — /Sentrick
{
"Sentrick": {
"devices": {
"SENTRICK-BAND-001": {
"gps": {
"lat": "29.760427",
"lng": "-95.369804",
"timestamp": 1747008000000,
"accuracy": "2.5",
"satellites": 12
},
"status": {
"online": true,
"battery": 87,
"firmware": "v2.3",
"lastSeen": 1747008000000
},
"alerts": {
"fall_detected": false,
"geofence_breach": false,
"sos_triggered": false
}
}
},
"live": {
"SENTRICK-BAND-001": {
"lat": "29.760427",
"lng": "-95.369804",
"ts": 1747008000000
}
}
}
}The /devices/live path is a lightweight mirror of the current GPS position, updated every two seconds. The mobile app subscribes to this path for the real-time map view, while the full device path contains the complete state including battery, firmware version, and alert flags.
Real-World Applications: From Elderly Safety to Livestock Security
The same hardware and firmware stack that powers the SENTRICK™ development prototype is the foundation for every product vertical in the SENTRICK™ ecosystem. The architecture is designed to scale from a single wearable to a fleet of thousands of devices, all reporting to the same Firebase tree with per-device namespacing.
Elderly Safety & Fall Detection
Real-time GPS tracking for seniors with Alzheimer's or mobility challenges. Instant fall detection alerts sent to family members via the SENTRICK™ app.
Livestock Security
GPS collars for cattle, horses, and camels across large farms and ranches. Geofence alerts when animals leave designated zones.
Child Safety
GPS smartwatches for children with real-time location sharing, SOS button, and school geofence notifications.
Vehicle Tracking
Magnetic GPS trackers for cars, trucks, and motorcycles. Live location, route history, and tamper alerts.
Industrial IoT
Asset tracking for factories, warehouses, and logistics chains. Real-time inventory location and movement history.
Wildlife & Falcon Tracking
Ultra-lightweight GPS trackers for falcons and wildlife. Trusted by Gulf royalty and conservation programs.
Key Engineering Lessons from SENTRICK™ v2.3
Never block HTTP handlers with network I/O
Firebase writes, MQTT publishes, and any network call must be deferred to the main loop or a background task. HTTP handlers should parse, store, and return immediately.
Verify library field names against your exact version
The esp_camera sccb/sscb naming issue is a perfect example of how a single character difference can cause hours of debugging. Always check the library header files directly.
Use explicit struct initialization for ESP-IDF types
Designated initializer syntax (.field = value) can behave differently across C/C++ standards and compiler versions. Explicit field-by-field assignment is always safe.
Throttle cloud writes to prevent rate limiting
Firebase free tier has write limits. A 2-second throttle on GPS writes reduces Firebase operations by 50x compared to writing on every GPS sentence, while maintaining real-time UX.
Design for resilience from day one
Every subsystem (WiFi, GPS, Firebase, camera) should fail independently without crashing the others. The SENTRICK™ firmware uses separate error handling for each subsystem.
Experience SENTRICK™ in Action
The technology described in this article powers every SENTRICK™ device — from the elderly safety band to the livestock tracker. Join thousands of early adopters worldwide and be among the first to experience real-time GPS + camera safety.
Tags
