All articles
Engineering

Why Hardware Agnosticism Matters for Neuro-Rehabilitation Middleware

Why Hardware Agnosticism Matters for Neuro-Rehabilitation Middleware

When we started building Ruten in 2023, one of the earliest design decisions was whether to build against specific hardware or to abstract the hardware boundary from day one. The answer seemed obvious from our experience: every neuro-rehabilitation device program we had seen eventually needed to integrate hardware combinations that were not contemplated at the start. An amplifier vendor discontinues a product line. A new stimulator with better charge control comes to market. A research partner specifies a different electrode array than the one used in initial prototyping. If your middleware is built on direct hardware calls, each of these events is a substantial re-engineering effort.

The adapter pattern we settled on is not novel in software architecture terms, but its application to neuro-rehabilitation middleware has specific constraints that shaped how we implemented it. This post describes those constraints and why we chose the adapter registry model over a unified driver model.

The Hardware Diversity Problem in Neuro-Rehab

Consider a realistic integration scenario for a team building an upper-limb FES rehabilitation system. They need a surface EMG amplifier with multi-channel differential input, a functional electrical stimulator with per-channel pulse width and amplitude control, and optionally a cortical or peripheral electrode array for intent decoding. Each of these component categories has multiple vendors, and within a single research or clinical device program, you may encounter two or three generations of the same vendor's hardware as product lines evolve.

The amplifier side alone illustrates the problem. Different amplifiers expose their sample data through different interfaces: some use USB bulk transfer with vendor-specific framing, others use SPI with a specific packet structure, others use streaming TCP over a lab-local network. The sampling rates differ, the gain configurations differ, the output word widths differ. An amplifier that outputs 16-bit samples at 2000 samples per second is not the same as one that outputs 24-bit samples at 1000 samples per second, even if both are "EEG amplifiers" in their product description.

On the stimulator side, the command interfaces are even more fragmented. Some stimulators accept per-pulse parameter updates over a serial interface with microsecond-precision timing. Others accept parameter tables that are loaded in advance and triggered by a separate digital line. Some provide safety interlock signals back to the host; others do not. Normalizing across these differences is not a matter of writing a compatibility shim. It requires a well-specified abstraction boundary that captures what the stimulation bus layer actually needs from a stimulator, independent of how any specific stimulator provides it.

The Unified Driver Model and Why We Rejected It

One alternative to the adapter pattern is a unified driver model: define a single low-level driver interface that all hardware must satisfy, and build the middleware layer directly on top of that interface. This is attractive because it seems simpler, and it is a recognizable pattern from operating system device driver models.

The problem in neuro-rehabilitation middleware is that the unified driver interface tends to be defined by the richest hardware feature set, and then every device that lacks those features needs stub implementations and workarounds. A stimulator that does not support per-pulse timing callbacks would need to implement a fake callback mechanism, which either introduces a timing fiction into the middleware or forces the caller to be aware of which devices are real and which are stubbed.

More fundamentally, the unified driver model couples the middleware architecture to the hardware abstraction boundary. If you want to run the same closed-loop algorithm on an embedded Cortex-M4 target and on an x86 host development machine with a high-bandwidth USB amplifier, you need the driver interface to work for both. Those two environments have incompatible timing models, incompatible DMA assumptions, and incompatible threading primitives. A single driver interface that genuinely captures both ends up being so general that it provides little guidance to the device implementer.

The Adapter Registry Model

The model we chose separates the device abstraction into two distinct layers. The first layer is the hardware adapter: a thin translation component that maps between a specific device's native interface and a normalized Ruten device interface. The second layer is the adapter registry: a runtime component that the middleware uses to look up and instantiate the correct adapter for a given device identifier.

The normalized device interface is deliberately narrow. For an acquisition device, it expresses three things: a sample delivery callback that provides a typed buffer of samples with a channel count and a timestamp, a configuration API for setting sampling rate and channel selection, and a fault notification interface. That is close to the minimal interface the decoder layer actually needs. It does not express DMA configuration, USB frame scheduling, or device-specific gain codes, because those are the adapter's concern.

For a stimulation device, the interface is similarly narrow: a command delivery function that takes a channel identifier, a pulse width, an amplitude, and a timing reference, and a fault state notification interface. The adapter translates that command into whatever the specific stimulator requires, whether that is a serial packet, a DMA-backed register write, or a parameter-table update with a trigger.

The adapter registry means that the middleware core never contains device-specific code. When a device engineer brings up a new amplifier, they implement the acquisition adapter interface and register it. The decoder layer picks it up automatically. When that amplifier is replaced by a different model six months later, only the adapter changes. The decoder, the stimulation command bus, and the closed-loop algorithm stay identical.

Tradeoffs We Made

The adapter model is not free. The normalized interface is, by construction, a common denominator. Some devices offer capabilities that the normalized interface does not expose, and using those capabilities requires either extending the interface or working around the abstraction boundary. We have had this conversation with teams evaluating the SDK: if you have a stimulator with hardware-enforced charge-balance verification that you want to surface as a real-time safety signal, and the normalized stimulator interface does not express that signal, you have a gap.

Our resolution is that the adapter can expose extended capabilities through a separate capability query mechanism. The middleware core ignores these extensions; higher-level application code can query them if needed. This keeps the normalized interface stable while allowing adapters to expose device-specific features for teams that want them.

A second tradeoff is performance. The adapter boundary introduces a function call and a buffer copy that would not exist in a direct hardware call path. For high-channel-count, high-sample-rate acquisition, this overhead is measurable. On a Cortex-M4 running at 168 MHz, a 64-channel acquisition adapter calling through the normalized interface at 2000 samples per second adds roughly 2 to 3 microseconds of overhead per sample delivery callback compared to a direct DMA callback. For most neuro-rehabilitation applications, that overhead is invisible in the latency budget. For very low-latency spike detection pipelines, it is worth profiling.

We are not arguing that hardware agnosticism is appropriate for every neuro-rehabilitation device program. If you know with certainty that your device will always use one specific amplifier and one specific stimulator, and your firmware team has deep familiarity with those specific hardware components, a direct driver model may be faster to get to a working prototype. What the adapter registry model provides is insurance against the hardware changes that, in our experience, happen in almost every program that reaches clinical evaluation.

A Concrete Integration: Three Electrode Arrays, Two Amplifier Generations

To make this concrete: we went through exactly this scenario during early Ruten development, working with a device engineering team building an upper-limb rehabilitation platform. They were using a first-generation amplifier for initial prototyping, knew a second-generation amplifier from the same vendor was releasing within six months, and needed to evaluate electrode arrays from two different suppliers to determine contact quality on their target anatomical sites.

With the adapter registry model, the transition from first-generation to second-generation amplifier required writing one new acquisition adapter, which took about a day of firmware work. The evaluation of the alternative electrode arrays required no firmware changes at all, because the electrode array differences manifested entirely at the physical and signal conditioning level, not at the acquisition interface level. The closed-loop algorithm and stimulation command logic ran unchanged across all four hardware combinations.

That is the practical test of hardware agnosticism: not whether it sounds like a good architecture, but whether it delivers on the promise when the hardware actually changes. In this case, it did.

More from the Ruten team

View all articles