Wearable firmware case study
Smart Insoles for Realtime Gait Analysis
I worked on the embedded firmware and electronics redesign for a pair of smart shoe insoles designed to capture foot pressure and movement data in real time. The product used sensorized insoles to measure how a person walks, runs, balances, and distributes pressure across each foot.
The product was built around movement intelligence, with use cases in sports performance, injury recovery, balance, healthy aging, and real-world gait analysis.
My role covered firmware architecture, sensor acquisition, BLE communication, left/right insole synchronization, local data storage, power optimization, DFU, device health, electronics redesign, test tooling, technical documentation, and embedded-mobile interface design.
Project Brief
- Work
- Embedded firmware, electronics redesign, BLE architecture, mobile integration
- Product
- BLE-connected smart insoles for gait and foot-pressure analysis
- Platform
- Nordic nRF52840 in each insole, running Zephyr through Nordic's nRF Connect SDK
- Sensors
- 8 FSRs per insole, 6-axis IMU, sensor fusion
- Sampling
- 200 Hz acquisition, 100 Hz analysis path, activity based sampling
- Power Target
- At least 14 hours from a 300 mAh battery
- Tooling
- nRF Power Profiler Kit 2, MCUboot, mcumgr over BLE, GitHub Actions, Cloudflare R2, Android test app
Challenges Faced
01 Build a Seamless Two-Insole Product Experience
The product consisted of two devices communicating over BLE, but the user experience could not feel like managing two individual devices.
The system had to behave like one wearable product, with pairing, connection state, battery reporting, data availability, firmware updates, and recovery behavior handled across both insoles without exposing that complexity to the user.
- Avoid a two-device setup and usage experience.
- Keep left and right insole state coordinated.
- Support reliable data collection from both devices.
- Handle disconnects, updates, and recovery without confusing the user.
02 Store Offline Sessions Within a Tight Power Budget
The firmware needed to support real-time BLE streaming when the app was connected, but recorded session data also had to survive phone unavailability, BLE interruptions, and app disconnects.
The difficult part was that flash storage was not a cheap fallback from a power perspective. In this system, an average BLE transmission was around 7 mA, while an average flash write was closer to 20 mA.
That meant offline recording had to be treated as a power-critical feature rather than just a reliability feature.
- Avoid lost sessions when the phone was not nearby.
- Minimize flash write frequency and duration.
- Keep streamed and deferred data formats consistent.
- Recover cleanly from interrupted sync flows.
- Preserve enough metadata for later synchronization.
03 Preserve Battery Life in a Wearable Form Factor
The insoles were battery-powered and worn inside footwear throughout the day, so power consumption was a primary design constraint. The target was a "full work-day", at least 14 hours of battery life from a 300 mAh battery. Any increment from the 14-hour minimum would be a great UX improvement.
Every firmware feature had to be checked against the power profile. Sampling rate, BLE transmissions, local processing, flash writes, sensor polls, synchronization, and sleep modes all affected the runtime budget.
04 Redesign Electronics Around Product Constraints
The original electronics limited firmware development, serviceability, user interaction, measurement quality, and battery capacity.
Several choices in the original revision made the product harder to debug, harder to recover in the field, and harder to optimize for battery life.
- USB charging existed without convenient serial access.
- Debug access required too much disassembly.
- Battery monitoring was not supported in hardware and user recovery controls were limited.
- The PCB consumed free space that could be shrunk to allow for a bigger battery.
- Fault states were not visible to the user.
05 Keep Firmware Testing Unblocked
Firmware development depended on BLE behavior that the production mobile app could not yet support on its side.
That was already delaying firmware testing and feature development, because several key BLE flows could not be exercised end to end with the production app.
To keep embedded development moving, I needed a BLE client that could support the full feature set immediately, including configuration, streaming, DFU, health reporting, and synchronization.
- Validate firmware features before production app support existed.
- Exercise edge cases that were difficult to trigger manually.
- Keep BLE protocol development moving independently.
- Reduce late-stage integration risk.
Technical Approach
01 Firmware Architecture
The firmware was designed around a continuous sensor and communication pipeline: pressure sensors and IMU, driver layer, acquisition and filtering, sensor fusion, step event detection, BLE data model or flash session store, and mobile app.
The architecture separated hardware-specific drivers from higher-level application logic so sensor handling, BLE communication, power management, session storage, DFU, and device-health features could evolve independently.
It also made the primary-secondary roles explicit: the primary insole acted as its own data source and as a gateway for the secondary insole.
The firmware was built on Zephyr through Nordic's nRF Connect SDK, giving the product a production-grade RTOS foundation for BLE, timers, storage, device drivers, and structured asynchronous work.
I used that foundation to build a hardware-watchdog-enforced, event-driven runtime model. Interrupt handlers stayed intentionally lean: they captured or registered time-sensitive events, then handed deferred processing to Zephyr work queues.
Heavier tasks such as sensor handling, BLE-side follow-up work, deferred state transitions, and other non-interrupt-safe processing were performed outside the ISR path. That kept latency-sensitive interrupt paths short, reduced timing jitter, and made the firmware easier to reason about as more subsystems were added.
The firmware was also organized around explicit operating modes rather than a single always-on behavior. Sleep, walking, high-activity streaming, deferred sync, and firmware-update flows each had different power, communication, and storage expectations.
- Hardware drivers isolated from application behavior.
- Sensor processing kept close to acquisition.
- BLE and flash paths treated as separate delivery modes.
- Well-engineered failure paths for graceful degradation.
- Hardware watchdog supervision for forward progress and recovery.
- Lean ISRs with deferred processing through Zephyr work queues.
- Mode-driven runtime control for sleep, walking, streaming, sync, and firmware update flows.
02 Sensor Acquisition and Fusion
Each insole used an array of 8 force sensing resistors to measure plantar pressure. These analog signals were scaled and filtered through op-amp circuitry before being sampled by the high-resolution external ADC. Each insole also included an ICM-42670 6-axis IMU for motion sensing.
Both pressure and motion data were sampled at 200 Hz and then decimated to 100 Hz for easier analysis. In low-activity mode, the sampling rate was reduced to conserve power.
The edge device did not perform every possible calculation. Speed, cadence, deeper gait analysis, and pressure-distribution analysis were handled by the mobile app because those calculations did not need to run on the embedded hardware itself. The firmware focused on the work that had to happen close to the sensors: acquisition, fusion, step-event detection, data integrity, and communication.
- Pressure matrix data plus IMU Z-axis detected and classified steps.
- Heel strike and toe-off were calculated on the device.
- Built-in IMU pedometer confirmed step count and reduced error.
- IMU X-axis and Y-axis measurements helped calculate pose.
03 BLE Topology and Protocol Design
I designed the BLE interface between the insoles and the mobile application, including GATT services and characteristics for sensor streaming, device configuration, device health, firmware updates, connection management, left/right coordination, and stored session synchronization.
The primary insole represented the pair to the app and acted as a gateway for the secondary insole. When the app needed raw data from both insoles, the primary insole tunneled the secondary insole data through to the phone. In other cases, the system did the required on-device analysis and sent processed results to the app.
BLE pairing alone was not enough to determine whether a new peer was the smartphone or the secondary insole, so I implemented an application-level authentication and identity layer on top of BLE using a secure key-based approach. Once identity was established, the connection manager could assign phone, primary, and secondary roles correctly even if peers connected in an unexpected order or were initially treated as the wrong type of peer, without tearing down the session and starting over.
The connection flow also included an authentication timeout. If a smartphone or the secondary insole connected but did not complete the expected identification and confirmation steps within the allowed window, the firmware disconnected the stalled session instead of leaving the system in a half-connected state.
This process was made easier by considering critical characteristics early on.
Sensor and derived-data packets carried timestamps and integrity checks so streamed or synchronized records could be interpreted consistently across firmware and mobile app paths.
The transport layer was also MTU-aware. Rather than assuming a fixed payload size, it adapted to the negotiated MTU of each BLE link so timestamped sensor data, derived metrics, and health records could move across the primary-secondary-app topology without avoidable fragmentation problems.
- Payload formats and binary data structures.
- Expected app behavior and data interpretation.
- Interaction sequences, error handling, and recovery flows.
- Application-level peer authentication and dynamic role resolution.
- Authentication timeout for stalled or invalid sessions.
- MTU-aware packet handling across multiple BLE links.
04 Flash-Backed Session Sync
The product could not depend on the mobile app always being connected. Recorded sessions needed to survive temporary app disconnection, phone unavailability, or BLE interruptions.
Because traditional flash writes were significantly more power-hungry than BLE transmission (~20 mA vs ~7 mA), flash storage had to be designed as a carefully managed recording path rather than a simple always-write fallback.
I evaluated low-power flash options, communicated with suppliers, and checked that selected parts were available long term, practical to source, and straightforward to develop against.
On the firmware side, RAM-to-flash writes were structured to reduce write frequency, batch data efficiently, and avoid unnecessary write cycles while still preserving session integrity.
- Low-power flash with ~4 mA active current selected with supplier and lifecycle considerations.
- RAM buffering used to reduce flash write cycles.
- Power-aware flash writes instead of continuous persistence.
- Live streaming and deferred synchronization coexisting.
- Metadata so the app could understand stored and pending data.
05 Electronics Redesign
The electronics redesign addressed measurement quality, serviceability, user interaction, fault visibility, debug access, and mechanical space for a larger battery.
The external ADC improved FSR measurement precision while also freeing the MCU's internal analog resources for battery measurement and power-monitoring needs.
The fault LED used a dead-man-switch style firmware behavior. The firmware had to regularly prevent the LED from turning on. If the firmware became stuck or stopped servicing that mechanism, the LED would turn on and give the user a visible fault indication instead of failing silently.
- USB serial access for development and service workflows.
- PCB size reduction for a larger battery.
- External ADC for more precise FSR measurements.
- User button for power, reset, pairing, unpairing, and recovery.
- Tag-Connect ECV3 6-pin castellated board-edge connectors.
06 Power Profiling and Runtime Control
Power profiling was done using the nRF Power Profiler Kit 2. I profiled individual peripherals and features, then compared those measurements with the complete system cycle profile.
Because each peripheral and feature had a measured power profile, I could estimate the battery-life impact of different algorithm and scheduling choices with much more confidence. Hardware and software decisions were evaluated against their effect on battery endurance.
Distinct operating modes turned power optimization into an architectural concern rather than a last-minute tuning pass. Sleep behavior, walking-mode flash recording, high-activity live streaming, sync behavior, and firmware-update flows could each be evaluated against the runtime budget they actually imposed.
Regular power profiling became part of regression testing. New features often had the potential to unexpectedly reduce battery life, and repeated profiling helped catch those regressions before they became product problems.
- Sampling and streaming rates checked against power cost.
- Peripheral enable and disable sequences optimized.
- Event frequency tuned based on measured current.
- BLE behavior evaluated through its power profile.
Details That Made the Product Robust
Firmware Updates and Rollback
OTA firmware update was built around MCUboot and mcumgr over BLE. Firmware images could be transferred over a dedicated BLE management channel that worked even if the main application did not start or crashed.
It followed the classic active-backup partition structure. A new firmware image could be downloaded and installed while keeping a known working firmware image available for rollback.
After an update, the newly booted firmware performed self-tests before confirming the update, including peripheral checks, sensor checks, and communication with the mobile app. If the checks failed, the device reverted to the last working version.
This reduced the risk of accepting a build that could boot but not run correctly.
- GitHub Actions builds firmware binaries, version manifests, and release metadata, then pushes them to Cloudflare R2 for over-the-air distribution.
Device Health and Fault Visibility
Device-health reporting exposed firmware state, connection status, synchronization state, battery level, DFU state, and operational health indicators to the app.
It also included a comprehensive error log. This allowed firmware bugs to be diagnosed over the air and later became the basis for cloud-backed firmware diagnostics.
The reporting path was not limited to generic fault flags. The firmware exposed structured diagnostic records containing an error code, source-file identifier, source line, contextual error value, and timestamp, with recent records available to the app for inspection. That made device-health reporting useful for debugging instead of acting as a generic status indicator, made field failures easier to classify, and later formed the basis for richer cloud-side firmware diagnostics.
A fault LED in a dead-man-switch configuration was also added as a local indication path when the device could not rely on the app to explain its state.
- Cloudflare R2 stored a per-release diagnostic map file for decoding device-health reports and crash dumps.
- Because every firmware version could shift diagnostic codes and stack trace locations, the map ensured each health report could be traced back to the correct source lines for the build that generated it.
- This was essential during rapid development when the firmware was changing daily.
Android Test App as Reference Client
I developed a dedicated Android test app because the production app was not keeping up with the BLE features the firmware needed to test.
It could exercise the full firmware feature set and became my main tool for validating BLE behavior, configuration, sensor streaming, DFU flows, health reporting, and data synchronization.
In practice it became an "admin panel" for the insoles. It let me inspect both devices directly, trigger commands, watch live state, and compare firmware behavior across versions without waiting for app-side support.
That kept embedded development moving, and it later gave the production app team a working reference for BLE behavior and sensor-data handling.
- Scan for nearby devices and identify the insoles by their characteristic UUIDs.
- Connect, disconnect, bond, unbond and authenticate devices.
- Negotiate and inspect MTU state.
- Send all supported commands and requests, then parse the device responses.
- Parse device health notifications, raise alerts when a device error was reported, and push health events to cloud for analytics.
- Show live state for both primary and secondary insoles, including connection state, authentication state, firmware versions, and MTU.
- Visualize live FSR and IMU data.
- Run OTA updates through mcumgr and MCUboot, including fetching both latest and older firmware builds from Cloudflare R2 for firmware A/B testing.
Embedded-Mobile Interface Documentation
I documented GATT services and characteristics, binary data structures, sensor data packet formats, device configuration commands, expected connection behavior, left/right data handling, stored session synchronization, DFU and rollback behavior, and error-recovery scenarios.
The interface also exposed enough runtime state for the app to reason about the device instead of guessing: current operating mode, battery level, firmware version, bonding or handshake status, enabled services, and connection-state flags for the paired topology.
Conclusion
The project turned a pair of constrained embedded devices into a coordinated wearable product. Sensor acquisition, BLE behavior, flash-backed recording, power control, firmware-update safety, electronics serviceability, and mobile integration all had to work as one system.
Thoughtful early engineering choices meant the product avoided problems that commonly sink first-generation IoT releases. Crash analytics, safe over-the-air updates, firmware compatibility, and graceful degradation under error conditions were designed in from the start, not patched in later.
Final Thoughts
01 Reliable Two-Insole Experience
The firmware made two physical BLE devices behave like one coordinated product. The primary/secondary topology simplified the mobile app experience while still allowing raw data tunneling, processed data reporting, battery reporting, DFU coordination, and recovery behavior across both insoles.
02 Power-Aware No-Loss Session Recording
The flash-backed storage path protected recorded session data when the app was not connected, while RAM buffering and carefully scheduled flash writes reduced the power cost of offline recording. The device could continue recording and later synchronize stored data back to the mobile app, reducing the risk of lost sessions during normal real-world use.
03 Power-Aware Firmware Architecture
The firmware was shaped by measured power profiles rather than assumptions. Peripheral behavior, sampling rates, BLE behavior, flash synchronization, low-activity modes, and explicit operating modes were evaluated against their runtime cost.
04 More Serviceable Hardware Platform
The electronics redesign improved measurement precision, debug access, user controls, fault visibility, and available battery space. USB serial and Tag-Connect debug access improved development and service workflows.
05 Faster Firmware Validation
The Android test app removed a major integration bottleneck. Firmware features could be validated against a working mobile client before the production app caught up, and the test app became a reference point for production BLE behavior and sensor data pre-processing.
