Ramblings of an aging IT geek
← Ramblings of an aging IT geek
hardware

finally seeing the bus, one logic analyser later

How a cheap eight-channel logic analyser and sigrok turned an I2C sensor that "didn't work" into an obvious off-by-one in the register address.

A soldering iron and electronics on a bench

I had a sensor that didn't work, and no way to tell why. It was an I2C temperature and humidity part on a breakout, wired to a microcontroller, and the readings came back as nonsense or as a hung bus. The code looked right. The wiring looked right. The datasheet looked right. Everything looked right, which is the worst possible state to debug from, because "looks right" means you've run out of things to check by reading.

The problem with a serial bus is that it's invisible. You can stare at two wires all day and they tell you nothing. So I finally bought the tool I'd been meaning to buy for years: a cheap eight-channel logic analyser, the small clone of the Saleae kind, the sort that costs less than a takeaway and clips onto your bus with little grabber leads. The good ones cost real money. This is not one of the good ones. It was entirely good enough.

the software is the actual product

The hardware is a USB widget. The thing that makes it useful is sigrok, and specifically PulseView, its GUI. You plug the analyser in, point PulseView at it, set a sample rate, arm a trigger, and capture. Then you get the thing you've been missing: a picture of the wire over time.

$ sigrok-cli --version
sigrok-cli 0.7.1
$ sigrok-cli --scan
The following devices were found:
fx2lafw - Saleae Logic with 8 channels: D0 D1 D2 ... D7

For a generic clone the driver you want is fx2lafw, which speaks to the Cypress FX2 chip these things are built around. On Linux you may need a udev rule so you can talk to it without root, but otherwise it's plug and capture. I wired the analyser's D0 to SDA, D1 to SCL, and a common ground, set it sampling at 4 MHz against a 100 kHz bus (you want comfortably more than ten times your bus clock so the edges are crisp), and hit capture.

A close-up of a circuit board

the decoder does the reading for you

Raw square waves are progress, but you don't want to be counting clock edges by eye to reconstruct bytes. sigrok ships protocol decoders, and that's where it earns its keep. You stack an i2c decoder onto the two channels, tell it which is SCL and which is SDA, and PulseView annotates the waveform with the actual transaction: start condition, the address byte, the read/write bit, each data byte, the ACK or NAK after each one, and the stop.

So instead of a wall of edges I had something I could read like a sentence:

START
ADDR WRITE  0x40   ACK
DATA WRITE   0xF4   ACK
START (repeated)
ADDR READ   0x40   ACK
DATA READ    0x61   ACK
DATA READ    0x8C   NAK
STOP

And there it was, plain as anything. The address byte going out was 0x40, which was correct. But the register I was writing before the read, 0xF4, was not the register I thought I was selecting. I'd transcribed the measurement-trigger register address from the datasheet wrong: off by one row in a table, reading the next line down. The sensor was dutifully doing exactly what I asked, which was a measurement on the wrong register, and handing me back a stale or undefined value. No bug in the bus, no bad solder joint, no pull-up problem. An off-by-one in my own head, copied faithfully into code.

why this is worth the bench space

I could have found that eventually by re-reading the datasheet for the fortieth time and getting lucky. The logic analyser turned a guessing game into a five-minute observation, because it let me compare what I intended to send against what was actually on the wire. That gap is where almost all of these bugs live. The code says one thing, the silicon does another, and until you can see the wire you're arguing about which one is lying.

A few things I picked up that first session, now that the tool's earned a permanent spot on the bench:

  • Set your sample rate well above the bus clock. If your captures look ragged or the decoder gives nonsense, you're undersampling and aliasing the edges.
  • Check your pull-ups before you blame anything else. If SDA or SCL never gets cleanly back up to the rail, the analyser shows you a lazy, rounded rising edge instead of a sharp one, and that's a hardware story not a software one.
  • Trigger on the start condition (a falling SDA while SCL is high) so you capture the beginning of a transaction rather than landing in the middle of one.
  • The same trick works for SPI, UART and one-wire too. Swap the decoder, keep the workflow.

A macro shot of circuit traces

The broader lesson is the old one, freshly relearned: when you've checked everything by reading and it's still broken, stop reading and start measuring. A serial bus is invisible right up until you put a window on it, and then the bug is usually obvious and usually yours. Best twenty quid I've spent on the bench in a while. The sensor's been reading correctly ever since, and I now reach for the analyser the moment a bus does anything I didn't expect, rather than as a last resort after an afternoon of staring.