Logic Analyzers: Tapping Into Raspberry Pi Secrets
Today, I’d like to highlight a tool that brings your hacking skills to a whole new level, and does that without breaking the bank – in fact, given just how much debugging time you can save, how many fun pursuits you can unlock, and the numerous features you can add, this might be one of the cheapest tools you will get. Whether it’s debugging weird problems, optimizing your code, probing around a gadget you’re reverse-engineering, or maybe trying to understand someone’s open-source library, you are likely missing out a lot if you don’t have a logic analyzer on hand!
It’s heartbreaking to me that some hackers still don’t know the value that a logic analyzer brings. Over and over again, tactical application of a logic analyzer has helped me see an entirely different perspective on something I was hacking on, and that’s just the thing I’d like to demonstrate today.
A logic analyzer has a number of digital inputs, and it continuously reads the state of these digital inputs, sending them to your computer or showing them on a screen – it’s like a logic-level-only oscilloscope. If you have an I2C bus with one MCU controlling a sensor, connect a logic analyzer to the clock and data pins, wire up the ground, launch the logic analyzer software on your computer, and see what’s actually happening.
For instance, have you ever noticed the ID_SC and ID_SD pins on the Raspberry Pi GPIO connector? Are you wondering what they’re for? Don’t you want to check what actually happens on these pins? Let’s do that right now!
I’m using a $10 logic analyzer you can get off Aliexpress or Amazon, a laptop, and a Raspberry Pi with an SD card and a power supply. Here, it is wired up – you only need three female-female wires, two signals and one ground. “SD” and “SC” sounds like I2C – typical I2C frequency is usually either 100 kHz or 400 kHz. A good rule of thumb is to set your frequency to be three or four times larger than the clock frequency of the data stream you’re about to capture. As such, I plan to set my logic analyzer’s sample rate to 2 MHz. If it turns out to be too slow to catch up with the data being transferred, I can increase the sample rate and just do the sampling again.
The software I’m using is Pulseview – it’s a wonderful GUI for logic analyzers, and can interface to a large variety of logic analyzers. It’s open-source, Linux-friendly, hackable and has good UX, even if it’s not recently maintained. You can install it from your distro’s repository, or download the .exe if you’re on Windows. With the logic analyzer connected, I plug it into my USB port, launch Pulseview, set the sample rate and reading duration, which can be infinite, disable all channels except the two I’m interested in, press ‘Capture’ and plug the Pi into power.
After I plug the Pi in, logic levels on both of the pins go up as 3.3 V power appears – and, after a few seconds, there’s a short burst of activity on these two pins. Zooming in, the activity does indeed look like I2C – and in Pulseview, it’s quite easy to decode! Press the “Protocol decoders” button on the settings taskbar, type “I2C” on your keyboard, select the I2C decoder, then doubleclick the decoder tag on the left and select which channels are SCL and SDA – it’s easy to tell, SCL will look like a clock signal with equal highs and lows, while high and low intervals on SDA will vary; on the picture above, D1 is SCL and D0 is SDA. Zooming in on the I2C events decoded, we can see that this activity is I2C requests to read data from the address 0x50, and these requests are followed by NACK events (red marker), which means they don’t receive a response.
Now, if you’ve looked into Raspberry Pi HAT design, you might already guess that these I2C requests are coming from the Raspberry Pi bootloader, which is looking for the I2C EEPROM containing on-HAT device information, so that the Pi can load Device Tree overlay data from it and use that data to configure any hardware on the HAT connected. You don’t have to rely on whatever little information is available online about this process – with a logic analyzer, you can investigate what actually happens, find any hidden features and caveats, so that even proprietary hardware is as little of an obstacle to you as possible.
This is a simple example of what you can do with a logic analyzer and a Raspberry Pi with a 40-pin header. Let’s ramp it up! On the Pi board alone, I2C is used in a few different places – HDMI display configuration, Pi camera configuration, a GPIO expander on the Pi 3 that compensates for lack of IO on the CPU – there’s plenty. You can explore all of these for fun, but, let’s achieve an actual practical goal.
What you could do with all that I2C? Well, here’s a small but situational problem with the Raspberry Pi cameras – they’re not hotpluggable; and if there’s one thing we know for sure, it’s that you don’t always have to respect limitations of your technology. You might not always want to have your Pi camera dangling around on a cable, for instance – that’s a bit subpar, it’s fair to only want to have the camera plugged in if you actually want to take a picture. Well, now we can actually check what’s happening on the camera I2C bus, and if that’s the culprit, making it hotpluggable sounds exactly like the kind of thing we could pull off – plus, we’re going to be fighting against the proprietary firmware on the Pi while at it!
Technically, the Pi firmware will only recognize a Pi camera if it’s plugged in during boot – the closed-source bootloader checks camera presence during boot, which certainly implies communication over I2C, maybe it’s even setting some camera-specific I2C registers; if you don’t have a camera plugged in during boot, that won’t happen and you will need to reboot your Pi for the camera to work. But after boot you can unplug the camera and plug it back in, and it will work just fine!
Given this riddle, my guess is that there’s really no reason why a Pi camera couldn’t work if it’s plugged earlier, apart from the inflexible on-boot detection logic. The default camera FPC is also mechanically not hotplug-capable, so I’m going to work around this by modifying the FPC so that the GND pins make contact first when you hotplug it. The main problem stays, though – the Pi bootloader is closed-source and we can’t modify the detection logic, making it so that it only runs once software actually tries to access a camera. We can, however, hack it by wiring up our own MCU to that I2C bus and making it present as a camera on the I2C bus!
Here’s the roadmap – we wire the logic analyzer up to the Pi Camera I2C bus, see what kind of activity happens as the Pi boots up, then we add an MCU that taps into the camera I2C pins – a RP2040 will do nicely, given that it can function as an I2C peripheral. The hardware needed for such a hack is minimal – a microcontroller board and a few jumper wires, that’s it.
After soldering onto camera I2C bus pullup resistors on a Pi Zero, I captured the on-boot I2C communications with the v1 camera plugged in, and here’s my findings. There’s three I2C addresses queried on boot – 0x10, 0x1a and 0x36, my camera responds to 0x36. Apparently, 0x10 is used for the sensor on the Pi Camera v2, and I’d guess that 0x1a is the 12 MP camera.
With the v1 5 MP camera I used, nothing is written into the registers – it seems there’s only three transactions that read from the camera sensor’s registers, with each transaction first sending two write bytes, which is typically a register address in such transactions. Having two-byte register addresses does make these transactions look a bit weird! That said, being able to unplug the camera and plug it back in without problems does lead me to believe it shouldn’t be a problem.
Now that I know what’s happening under the hood, I’m going to unplug the three jumper wires from the logic analyzer, wire them up to a small RP2040 board I have, and get hacking on the software side of the project. If I can get I2C peripheral-emulating firmware going, I’ll have Pi camera v1 hotplug operational for all of my portable device building purposes! Just like this, with a logic analyzer and three wires, we have figured out a way to add hotplug capability to the Raspberry Pi camera – something known as impossible, until we decided to probe it.
This kind of hack is where a logic analyzer is the best tool for the job, and you might have noticed that a few crucial parts of this hack, namely, sniffing the I2C data being transmitted, aren’t feasible without a logic analyzer.
It also gives you insights into how proprietary things work – both in the HAT EEPROM detection example and the camera detection example, we can see that every failed transaction is repeated three times, and you wouldn’t be wrong to guess that the Raspberry Pi bootloader code has an I2C transaction wrapper that retries I2C transactions in case a transaction spuriously fails – a good practice through and through!
Thinking about getting a logic analyzer? The analyzers are great for what they are and will be enough for majority of the tasks, and they go for around $10 on typical online marketplaces – more than enough for all the things you might want to probe. You can also use a Pi Pico as a logic analyzer – though you will find that you likely have to compile Pulseview and the under-the-hood software yourself to include support for that, doable but less straightforward than using one of the FX2-based analyzers with stock Pulseview support.