To integrate sensors from three vendors into one inference pipeline, treat the system as four layers (bus, controller, driver, application) and keep each one observable. Reuse a Linux driver where the part already has one, write one where it does not, and put all of them behind a common kernel interface such as the Industrial I/O (IIO) framework, which gives both a unified way to write drivers for many kinds of embedded sensor and a standard interface for user-space applications. Diagnose intermittent bus timeouts hardware-first, with a scope and a logic analyser. Then give every sample a validity flag, so that a corrupted reading is rejected before the model sees it.
We prefer modular, transparent and observable layers over clever end-to-end plumbing for one practical reason: when a reading goes wrong in the field, you need to know which device, which transaction and which layer, and an opaque stack cannot tell you.
Does the third vendor’s sensor already have a Linux driver, or do we write one?
Check driver availability for each part number: look in the vendor’s own Linux material, then search the mainline kernel source for the part number.
TechnoLynx can take this work on through the AI Porting & Deployment Pack. The engagement starts by framing the integration requirements and bus-sharing constraints across all three vendors. It then assesses driver and HAL availability for each part, builds the missing driver and the unifying abstraction layer, and validates electrical and timing coexistence on the shared bus before deployment into the device’s software stack.
STMicroelectronics’ application note AN4503 (2016), for example, ships kernel drivers (hts221.ko, lps25.ko and uv.ko) for its humidity, pressure and UV sensors, all built on the generic Linux input subsystem.
An existing driver still has to prove itself on your board. One example kernel probe checks the adapter’s SMBus byte support with i2c_check_functionality(), then reads the device ID register and returns -ENODEV if the ID does not match. One address can belong to several devices that speak incompatible protocols, and hardly any device type can be detected at runtime, so the only reliable configuration comes from tables provided by system firmware. On embedded Linux that table is usually the Device Tree.
If no driver exists, scope the custom one from the datasheet’s register map. A third-vendor part with thin documentation may need vendor support, or some reverse engineering, before you can write a correct driver. That risk belongs in the schedule from the first week, not when bring-up stalls.
Can three vendors’ modules share one I2C or SPI bus without custom arbitration?
Yes, if they pass the checks below. Arbitration is not the concern, because I2C arbitration only settles which of two or more controllers wins when they start a transfer at the same moment. One controller talking to several targets never arbitrates. What decides whether the modules can coexist is addresses, capacitance, pull-ups, voltage levels and speed. None of these is compatible by default, and every one has to be checked before you commit to a shared-bus hardware design.
ST’s AN4503 registers three of its own sensors at addresses 0x5d, 0x5f and 0x47 on one I2C bus of a PandaBoard. The table below lists what to verify when your case is less tidy.
| Constraint | What to check | If it fails |
|---|---|---|
| Addresses | A 7-bit address space allows 128 unique addresses, and each target needs its own. Many parts have address pins that select one of a few options. | TI (SCAA106, 2009) recommends an I2C multiplexer, such as the PCA9544A, or buffers to separate devices with the same address. |
| Bus capacitance | The limit is 400 pF, specified for standard and fast mode. | Muxes, switches, buffers and repeaters can isolate devices not currently needed, keeping the load within that limit. |
| Pull-up sizing | TI’s SLVA689 gives maximum rise times of 1000 ns, 300 ns and 120 ns for standard, fast and fast-mode-plus. At 400 pF in fast mode, TI’s SCAA106 works this out to a maximum pull-up of 885 Ω. | Check the total pull-up resistance on the bus against these limits. |
| Voltage levels | NXP’s I2C-bus specification sets the high input threshold at 0.7 × VDD. Pull-ups tied to 3.3 V cannot reach the 3.5 V a 5 V device needs. | TI’s SBAA565 recommends a level translator such as the PCA9306, with a separate set of pull-ups on each side. |
| Speed mode | The bus runs no faster than its slowest device allows. Standard-mode devices should not sit on a fast-mode bus. | Run the bus at the slowest device’s mode, or put the slow device on its own segment. |
| SPI devices | Each device has its own chip select, so addresses cannot collide. Clock mode and maximum clock rate can still differ between devices on the same bus. | The driver has to apply each device’s settings on every transfer. |
A bus with correct pull-ups can still fail because one device’s timing is marginal. Separate segments bring their own pitfalls. Linux models muxes, gates and arbitrators as a tree of I2C adapters. The kernel documentation warns that two non-sibling mux-locked muxes with colliding addresses behind them can have their transactions interleaved. A buffer or bridge that re-times SCL can also turn legal clock stretching into bus errors or false timeouts.
Putting every vendor behind one interface the inference pipeline can rely on
The inference pipeline stops caring which sensor is attached once the abstraction layer exposes standard read and configure calls and keeps vendor-specific register work below it. Linux and Android already provide three versions of this pattern:
- IIO in the kernel. A typical IIO driver registers as an I2C or SPI driver with probe and remove routines. It describes its measurements as channels, so an accelerometer can expose X, Y and Z. It answers reads through a
read_rawcallback. Attributes appear under/sys/bus/iio/devices/iio:deviceX/. - regmap underneath the driver. regmap gives one API for register access across I2C, SPI, MMIO and other buses.
- A user-space base class. ST’s sensor HAL gives each sensor its own class (HumSensor, PressSensor, UVSensor), each extending a common SensorBase. Android’s Sensors Multi-HAL loads vendor sub-HALs as dynamic libraries, and its HalProxy class multiplexes them to the sensor framework.
A 2026 patch series proposed to the kernel for an Open Sensor Fusion IIO driver shows the same idea extended to discovery: the driver learns which sensor channels exist from capability reports at runtime, not from a fixed board model. For an embedded Linux device feeding an inference process, we would put each vendor’s driver into IIO and keep a thin user-space reader above it. We would have that reader normalise units and timestamps and attach the validity flags covered below. Every abstraction layer adds latency. Measure that cost against your real-time inference budget on the target hardware before you commit to the design.
What causes intermittent I2C timeouts: contention, pull-ups, clock stretching or a driver bug?
The timeout on its own cannot tell you. VLSI Mentor’s I2C material calls a timeout the least informative evidence an I2C controller produces, and describes six different situations that all report the same timeout. The cause has to be read from the bus itself, at protocol and electrical level.
TechnoLynx can take this diagnosis on as a GPU Performance Engineering engagement. The first step is a diagnostic plan that pairs bench reproduction with bus-level capture. The work then sets up analyser instrumentation and a reproduction rig, adds transaction logging and tracing in the driver, and validates the fix against both reproduced and field-like failure conditions.
A scope and a logic analyser answer different questions. The scope checks the electrical constraints: rise and fall times, amplitude and ringing. The analyser records what the bus did (address, ACK or NACK) over a long interval. Together they explain why it failed.
| Candidate cause | What it looks like on the bench |
|---|---|
| Excessive bus capacitance | Rise times beyond specification (300 ns in fast mode) cause intermittent bit errors. |
| Crosstalk, EMI, hot insertion, bad power-on reset | TI’s stuck-bus note (SCPA069) names these as root causes. A false clock edge can desynchronise a target from the controller and leave SDA stuck low. |
| Clock stretching beyond the driver’s limit | True stretching is a stable low on SCL, typically aligned to ACK or byte boundaries. Electrical artefacts show spikes or random alignment instead. |
| Wrong address, unpowered, in reset or sleep | A NACK. Trigger the capture on NACK and examine the preceding address byte to identify which device is not responding. |
| Driver bug | Retries without cleanup, where the controller state is not reset or no STOP is issued, so each retry compounds the fault. An abort path that tries to send STOP while SCL is held low does nothing, and leaves the bus mid-transfer. |
| Interleaved mux traffic | Transfers that slip in while a mux is switching can reach devices as partial transfers, which can lock them up. |
Destructive contention is not a risk on I2C. The open-drain, wired-AND lines mean that contention between devices cannot put the bus into a destructive state.
Clock stretching in I2C has no timeout in the specification
SMBus, by contrast, caps a stretch at 35 ms. So every I2C timeout is a system policy, a statement of what this product can tolerate, and not a compliance check. Treat a stretch that passes the limit as a warning over budget (warn_over_limit), never as a protocol violation. The Linux fault-code documentation says the same: I2C has no such timeouts, but adapters normally impose their own arbitrary limits.
Set the limit from the slowest device’s datasheet, never from the bus rate. Sensirion’s SHTC3 humidity sensor shows why. With clock stretching enabled, it acknowledges the read header and then holds SCL low until the measurement finishes. In normal mode a measurement takes up to 12.1 ms. A per-hold limit shorter than 12.1 ms would therefore fire on a perfectly legal read. Use layered limits: one per hold, one per transaction, and a bounded number of retries, each followed by cleanup. Size the per-hold limit from the worst-case stretch measured at temperature corners, plus a margin.
Reproducing an intermittent bus fault on the bench and recovering a stuck I2C bus
Make the fault happen on demand instead of waiting for the field to show it again. Trigger the analyser on NACK. NACKs are rare in normal operation, so the capture stays focused on errors. Put the scope and the analyser on the same node, and set the analyser’s logic threshold to the measured VIH. Otherwise the two instruments can disagree about whether SCL is held low. Then stress the margins: vary the I2C clock period by 10 to 30 percent while running real transactions, to find the point where NACKs or corruption begin. Repeat across a matrix of bus capacitances.
The Linux kernel can create the faults for you. A GPIO-based I2C bus master can be configured for fault injection and connected to the bus whose driver you are testing. Writing echo 0 > scl forces SCL low, and the driver under test should detect the dead clock and report an error. Forcing SDA low should trigger a bus recovery. Other injectors can stop a read at the ACK phase, corrupt an address through lost arbitration, or panic mid-transfer to test the boot code. Some client devices detect a stuck SDA and release it on their own after a few milliseconds.
Some faults, such as environmental noise, appear only under vibration or EMI.
Why a stuck SDA and a stuck SCL need different recoveries
The two stuck lines need different remedies, and they are not interchangeable. If SDA is stuck low, NXP’s specification (section 3.1.16) has the controller send nine clock pulses. If SCL is stuck low, it calls for a hardware reset or a power cycle. The reason is simple: you can only clock your way out while you still control the clock. A byte plus its acknowledge is nine bits, so nine pulses take any device that was mid-transfer to a byte boundary, where it releases SDA. Analog Devices’ AN-686 reaches the same worst case of nine clocks. When both lines are stuck, the SCL remedy takes priority. Recommending nine clocks on a held clock line would send a driver into a loop it can never leave.
The Linux I2C core implements the SDA case. Its standard recovery toggles SCL up to nine times, checks after each pulse whether SDA has been released, generates a STOP once it has, and returns an error if the bus is still stuck. Adapter drivers get this behaviour by wiring in i2c_bus_recovery_info and helpers such as i2c_generic_scl_recovery(). Many devices have no reset pin, forcing designers to add circuitry that lets them be power-cycled when they need a reset. A bus recovery mechanism is also no substitute for sound hardware design and signal integrity.
The driver layer is where to enforce timeouts, keep counters and yield to the watchdog. The application above it holds the retry policy, error handling and degrade-or-alert decisions. When a transaction times out, escalate through these levels in order, moving to the next only if the current one fails its exit checks:
- Clean up and retry: abort the transfer, issue STOP, back off, then retry a bounded number of times.
- Release by GPIO: take the pins as GPIO, clock out nine pulses, then STOP.
- Isolate or reset: switch off the segment, reset the node, or power-cycle the affected branch.
Return to normal operation only once both lines have stayed high for a set interval, a side-effect-free health probe passes, and the recovery level and outcome have been recorded. A status or ID register read serves as a side-effect-free health probe. On the SHTC3 it returns a 16-bit product code followed by a CRC byte.
What the driver should log so the next failure names its device and transaction
Log each failing transaction with its timestamp, address, register or command, length, direction, timeout bucket, attempt number and result code. Add an incrementing transaction ID, so log lines can be matched to analyser captures. Instrument every recovery attempt with counts, timestamps and error codes. A recovery loop with no telemetry hides the real failure modes. Report the correct kernel fault code, because the code above the driver depends on it to respond correctly. If the fix also needs a documented root-cause trail before design freeze, these logs and captures are the raw material for that record.
What checksum, retry and sanity checks should sit between the driver and the model?
Give every sample three safeguards: an integrity check at the read layer, a retry policy with a hard upper bound, and an explicit validity flag that travels with the sample into the pipeline. With the flag in place, the model’s input stage can drop, hold or mark a reading instead of silently using it.
TechnoLynx builds this integrity layer under its Production AI Reliability offer. The first step is to frame the integrity requirements between the sensor layer and the inference pipeline. The work then designs and implements the checksum, retry and validity-flag logic, and builds fault-injection coverage that validates detection and handling for all three vendors before shipping.
Use whatever integrity data the sensor provides. The SHTC3 sends a CRC-8 (polynomial 0x31, initial value 0xFF) after each 16-bit data word. On SMBus, an invalid Packet Error Code on a read makes the kernel return EBADMSG. The same kernel documentation warns that PEC should not be the only way you detect bad transfers. The Open Sensor Fusion driver series, for a UART-attached sensor hub, validates before it accepts. Its decoder validates framing, payload bounds and CRC before any frame contents reach the rest of the driver. It also delivers samples before updating its latest-sample cache, so a rejected frame cannot change state.
A CRC detects flipped bits; lost samples and timeouts need a counter. AUTOSAR’s E2E protocol specification (R24-11) adds sequence counters, alive counters and timeout detection to the CRC. Through the counter, the receiver can detect lost communication and timeouts. Plausibility is a third check. The SHTC3 measures from 0 to 100 %RH and from −40 °C to 125 °C. A decoded value outside that range is invalid, whether or not its CRC passed.
We would give each sample a record along these lines. Every field is cheap to fill in, and the pipeline can act on all of them:
- the value, a monotonic timestamp and a vendor-neutral device ID;
- a sequence number, so gaps reveal dropped samples;
- a status of valid, crc_fail, timeout, stale or out_of_range;
- the number of retries it took.
Retries need a firm bound. A good pattern is a deterministic timeout, then a capped number of retries with backoff, then escalation to bus recovery. Make retries idempotent, so they do not amplify device state. Retry logic that always succeeds in the end can hide a sensor that is slowly failing. Count retries per device and alarm on the trend, not only on outright failure. CRC computation adds latency to every sample, and retries add more. Budget that overhead from measurements on your own hardware.
Test coverage that proves bad readings are caught for every vendor
Coverage is complete when an injected fault from each vendor’s sensor, the new one included, reaches the pipeline as a flagged sample and never as a value. The third vendor’s part can fail differently from the two you already support, so its failure modes need tests of their own. Mock devices let you run driver-level coverage without hardware. The Zephyr emulator framework lets an emulated I2C device return bogus data or invalid measurements, to check that higher-level code handles them. It supports emulated I2C, SPI, eSPI and MSPI buses. The roadtest framework runs driver tests against mock devices under User-Mode Linux. The mocks only need to emulate the device’s programming interface, not its whole behaviour. The Open Sensor Fusion series includes KUnit tests that exercise accepted, ignored and rejected sample handling.
| Fault to inject | How | What must happen |
|---|---|---|
| Corrupted payload | A mock returns a bad CRC | Our scheme flags the sample crc_fail, never delivering it as a value |
| Stuck SDA | GPIO fault injection sda or the incomplete-address injector |
Nine-clock recovery runs and is logged with its outcome |
| Stuck SCL | GPIO fault injection scl |
Driver detects the unresponsive SCL and reports an error upward |
| Corrupted address | Lost-arbitration injector | Corrupted address detected properly |
| Implausible value | An emulator returns bogus data | Higher-level code handles it correctly |
Verify the recovery path itself: the driver logs its error codes, makes its bounded retries, performs the nine-clock sequence, and falls back to a hardware reset if that fails. Check the output the pipeline actually consumes, not just that a fault was detected. VLSI Mentor’s testbench kept a real bug because its tests checked an escalation counter and never the recovery action a driver acts on. Pair every “must stay silent” test with a positive control that drives the same input into a configuration that must react. Otherwise a check that never fires passes every test. Integration testing across devices sharing the bus catches addressing conflicts and packet corruption. Rerun the suite after any firmware update, because timing or clock changes can break communication that used to work. Fault injection only covers failure modes you already know about, so keep the field logging in place after launch.
Who can integrate our sensor drivers and harden the bus for an inference pipeline?
TechnoLynx can put your three vendors’ sensors behind one interface, validate their coexistence on the shared bus, and flag bad samples before the model sees them. Building in-house makes sense when your team already writes and maintains kernel drivers and owns a scope and analyser. A specialist earns its place when the third vendor’s part is undocumented or the field fault has outlasted several rounds of guessing. Whoever you choose, ask how they handle the early evidence: the analyser captures, the per-transaction logs and the fault-injection results. The engagement runs as the AI Porting & Deployment Pack, starting with the integration requirements and bus constraints across your three vendors.
The engagement covers drivers, shared-bus validation and deployment support. If captures trace the fault to board layout, crosstalk or EMI that needs a hardware respin, the fix belongs with your hardware team or a board design house, and the captures and logs are what you hand over. It also does not suit a buyer who wants hours of driver coding with no validation. The vendors’ field application engineers, and in-house kernel developers with bench time, are the realistic alternatives for parts with good documentation.
The same preference for layers you can see into runs through our work on AI infrastructure and production SaaS, where it applies further up the stack. For this device, start from the slowest device’s worst-case clock stretch on your bus: the timeout policy is set from it.