Gridspin Sensors is a productized iOS SDK + cloud pipeline that pulls camera, mic, IMU, GPS, BLE and NFC into one typed, AI-grade data stream. Drop it into clinical capture, retail floor walks, field-service workflows, or IoT cross-validation — and ship structured features your model can actually act on.
The thesis
Camera, mic, IMU, GPS, BLE, NFC — every modern handset carries seven instrument-grade transducers. The hard part isn't reading them. It's turning raw frames and samples into typed, time-aligned features your AI can act on. That's what Gridspin Sensors ships: the SDK, the pipeline, and the audit trail.
The four sensor lanes
Each lane is a Swift module you can adopt independently — or wire all four into a single capture session.
CoreMotion accelerometer, gyroscope, Euler angles and quaternion at 30 Hz, plus CoreLocation with horizontalAccuracy gating — not the liveLatitude != 0 anti-pattern. Lat = 0 is valid; absence of fix is what you actually care about.
Photoplethysmography from the rear camera with the flash as the illuminant — finger over the lens, torch enabled after startRunning() (preserves AVFoundation ordering). Heart-rate and HRV-grade beat trace, no extra hardware.
Forehead PPG plus facial action units (VNDetectFaceLandmarksRequest) folded into a PSPI pain score. Computed synchronously on the session queue via PainIndicators — no main-queue races, no stale snapshots.
SFSpeechRecognitionTask continuous dictation with the right finalization semantics — finalize only on stopVoiceRecording(), not on every segment-final mid-stream. Long-form notes survive the whole press-and-hold.
Production audit · proof points
Every lane in the SDK came out of a real iOS application, with a written audit that found real bugs and shipped the fixes. Four representative finds — the kind that bite anyone who tries to do this from scratch:
Original downloadFile only handled .ready; on no-route or refused, the completion never fired → hung UI, ambiguous state. We added .failed handling, routed every completion through one finish closure, and replaced parts.last! with safe Int parsing of OK:FILE_SIZE.
Landmark extraction dispatched AU values to the main queue, then computePainScore() ran on the video-processing queue reading @Published fields before the async update landed. We introduced a synchronous PainIndicators struct, computed PSPI on-queue, then published UI + painHistory in one main-queue block.
SFSpeechRecognitionTask emits isFinal == true for segment finals mid-stream; the original code finalized on each one, clearing state while the user was still holding the button. We update liveTranscript from callbacks and finalize only at stop — long dictations survive.
Code that inferred a GPS fix from liveLatitude != 0 drops on the equator and lies in the lab. Apple documents negative horizontalAccuracy as invalid. We expose hasLikelyGPSFix (accuracy ≥ 0 and < 500 m) and consolidate GPS append + UI on the main queue.
Cross-vertical
Phones are everywhere your customers are. The same four lanes power three very different products in the Gridspin family.
Rear-camera PPG cross-checks an ESP32 wearable EMG/PPG band. Voice annotation streams patient intake straight into PROAgent. Pain proxy on the front camera is a third opinion when both signals disagree.
Operator walks the salon or medspa floor; the phone's rear camera proposes SKU + quantity via vision; the IoT smart-shelf FSR pad confirms what actually left the surface. Two independent signals, one cross-validated inventory event.
Voice-annotated photos for every fix; IMU segments the visit into stages (drive · approach · work · pack out); GPS with proper accuracy gating proves the tech was on-site. The capture is structured before it ever leaves the phone.
SDK + Cloud pipeline
Capture on-device. Compress for the wire. Extract on the server. Your AI consumes the typed output.
Drop the SDK into your iOS target. Subscribe to whichever lanes you need; each emits typed Swift values at its native rate. CoreMotion, AVFoundation, Speech, and CoreLocation, wired correctly the first time.
Photos pass through jpegData(compressionQuality:) with a quality target you set. Continuous samples are batched into a packed binary log on disk and shipped as application/octet-stream — no envelope overhead per record.
Per-device token auth, HTTPS multipart for media, octet-stream for binary samples. The cloud runs feature extraction (HR/IBI from PPG, action units from frames, transcript chunks from voice) and exposes typed features under one session id.
# Push a batch of IMU samples from a capture session curl -X POST https://api.gridspin.xyz/capture/imu \ -H "X-Device-Token: $DEVICE_TOKEN" \ -H "X-Session-Id: s-2026-06-20-a1f9" \ -H "Content-Type: application/octet-stream" \ --data-binary @/tmp/imu_batch.bin # → 200 OK {"records": 487, "session_id": "s-2026-06-20-a1f9", "features_ready": true} # Then pull typed features for your model curl https://api.gridspin.xyz/features/s-2026-06-20-a1f9 \ -H "Authorization: Bearer $API_KEY" # → { "imu": {...}, "ppg": {"hr_bpm": 72.4, "hrv_rmssd": 38.1}, "voice": {...}, "pain": {...} }
API surface
The SDK calls these for you, but the surface is small enough you can hit it from any client — Python, Node, a custom firmware.
{
"session_id": "s-2026-06-20-a1f9",
"imu": {
"rate_hz": 30,
"stages": ["approach", "work", "pack_out"],
"steps": 412
},
"ppg": {
"hr_bpm": 72.4,
"hrv_rmssd_ms": 38.1,
"confidence": 0.92
},
"pain": {
"pspi_mean": 0.34,
"pspi_peak": 0.81,
"forehead_ppg_hr": 71.2
},
"voice": {
"transcript": "two of the retinol, replace the empty",
"locale": "en-US"
},
"gps": { "lat": 37.7749, "lon": -122.4194, "h_acc_m": 6.4, "likely_fix": true }
}
Ready when you are
Clinical, retail, field, or a custom integration on top of your own product — we'll spec the lanes, drop in the Swift modules, and stand up the ingest. First captures within a week.