⚙ HARDWARE HACKING UNLOCKED

GPIO & hardware hacking
on your uConsole.

From blinking an LED to reading environmental sensors over I2C — everything you need to turn your uConsole into a portable hardware hacking station.

20 min readUpdated Mar 2026CM4 module3.3V logicFree guide
01 — GPIO Pinout

GPIO pinout for the uConsole.

easy
Impact

The ClockworkPi uConsole exposes a standard 40-pin GPIO header via the CM4 extension board. This is the same pinout as a Raspberry Pi — meaning most Pi-compatible HATs, sensors, and breakout boards will work. The header sits on the back of the device, accessible when the rear panel is removed.

⚠ CRITICAL: All GPIO pins operate at 3.3V logic levels. Applying 5V to any GPIO pin will permanently damage the CM4 module. There is no 5V tolerance — use a logic level converter if interfacing with 5V devices.

The uConsole uses BCM numbering (Broadcom SoC channel) for GPIO identification, not the physical pin numbers on the header. Both are shown in the diagram below. When writing code with libgpiod, you'll reference the BCM numbers.

uConsole 40-PIN GPIO HEADER (CM4)
3.3V Power125V Power
GPIO 2 (SDA1)345V Power
GPIO 3 (SCL1)56GND
GPIO 4 (GPCLK0)78GPIO 14 (TXD)
GND910GPIO 15 (RXD)
GPIO 171112GPIO 18 (PCM_CLK)
GPIO 271314GND
GPIO 221516GPIO 23
3.3V Power1718GPIO 24
GPIO 10 (MOSI)1920GND
GPIO 9 (MISO)2122GPIO 25
GPIO 11 (SCLK)2324GPIO 8 (CE0)
GND2526GPIO 7 (CE1)
GPIO 0 (ID_SD)2728GPIO 1 (ID_SC)
GPIO 52930GND
GPIO 63132GPIO 12 (PWM0)
GPIO 13 (PWM1)3334GND
GPIO 19 (MISO1)3536GPIO 16
GPIO 263738GPIO 20 (MOSI1)
GND3940GPIO 21 (SCLK1)
Power (3.3V / 5V)
General GPIO
Special (I2C/SPI/UART)
Ground
Reserved (EEPROM)

Safe-to-use pins:

Not all 26 GPIO pins are free to use. The uConsole's internal display and keyboard controller may reserve certain lines depending on your firmware version. Here's what's generally safe:

Safe for general use

GPIO 4, 5, 6, 12, 13, 16, 17, 22, 23, 24, 25, 26, 27

I2C bus (shared, usable)

GPIO 2 (SDA), GPIO 3 (SCL)

SPI bus (enable via overlay)

GPIO 8, 9, 10, 11 (SPI0) — GPIO 7 (CE1)

Reserved / avoid

GPIO 0, 1 (EEPROM), GPIO 14, 15 (UART — console)

GPIO 14/15 are typically used for the serial console on the uConsole. If you disable console output on UART, they become available — but you'll lose serial debugging. Pins 0 and 1 are reserved for HAT EEPROM identification and should never be used for general I/O.

02 — Setup

Enabling GPIO on the uConsole.

easy
Impact

Before you can read or write GPIO pins, you need to ensure the right kernel overlays are loaded and your user has the correct permissions. The uConsole ships with most GPIO support built into the kernel, but some bus interfaces (I2C, SPI) require explicit activation.

raspi-gpio vs libgpiod:

There are two main approaches to GPIO on Linux:

✗ Legacy: raspi-gpio / sysfs

The old /sys/class/gpio interface and RPi.GPIO Python library. Deprecated since Linux 4.8. Still works, but has race conditions, no event handling, and is being removed from newer kernels.

✓ Modern: libgpiod

The character device interface /dev/gpiochip*. Proper kernel-level locking, edge detection, event timestamps, and a clean Python API. This is what you should use.

Step-by-step setup:

1

Install libgpiod and Python bindings

terminal — install packages
$ sudo apt update
$ sudo apt install -y gpiod libgpiod-dev python3-libgpiod

# Verify installation
$ gpiodetect
gpiochip0 [pinctrl-bcm2711] (58 lines)
gpiochip1 [raspberrypi-exp-gpio] (8 lines)
2

Add your user to the gpio group

terminal — permissions
$ sudo usermod -aG gpio $USER

# Log out and back in, then verify
$ groups
cpi adm dialout cdrom sudo audio video plugdev games users gpio i2c spi

Without this, you'll need sudo for every GPIO operation. Adding yourself to the gpio, i2c, and spi groups is recommended.

3

Configure kernel overlays

Edit /boot/config.txt to enable the bus interfaces you need. GPIO pins work out of the box, but I2C and SPI require overlays.

/boot/config.txt
# Enable I2C bus 1 (GPIO 2/3)
dtparam=i2c_arm=on

# Enable SPI bus 0 (GPIO 7-11)
dtparam=spi=on

# Optional: enable hardware PWM on GPIO 12/13
dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4

# Optional: disable serial console to free GPIO 14/15
# enable_uart=0
4

Reboot and verify

terminal — verify gpio
$ sudo reboot

# After reboot, check available GPIO chips
$ gpiodetect
gpiochip0 [pinctrl-bcm2711] (58 lines)
gpiochip1 [raspberrypi-exp-gpio] (8 lines)

# List all lines and their current state
$ gpioinfo gpiochip0 | head -20
line   0:     "ID_SDA"       unused   input  active-high
line   1:     "ID_SCL"       unused   input  active-high
line   2:     "SDA1"         unused   input  active-high
line   3:     "SCL1"         unused   input  active-high
line   4:     "GPIO_GCLK"    unused   input  active-high
line   5:     "GPIO5"        unused   input  active-high
...

# Quick test: read a pin value
$ gpioget gpiochip0 17
0

✓ TIP: If gpiodetect shows gpiochip0 with 58 lines, your GPIO is working. If you see fewer lines or errors, double-check your kernel version and config.txt overlays.

04 — I2C Sensors

Reading sensors over I2C.

moderate
Impact

I2C (Inter-Integrated Circuit) is a two-wire serial bus that lets you connect multiple sensors, displays, and peripherals using just two GPIO pins: SDA (data) and SCL (clock). It's the easiest way to add environmental sensors to your uConsole — and the most popular protocol for hobbyist electronics.

We'll use the BME280 as our example sensor — it reads temperature, humidity, and barometric pressure in a single tiny package. It's cheap (~$3-5), well-documented, and runs at 3.3V. Perfect for the uConsole.

Enable I2C:

1

Add the I2C overlay to config.txt

/boot/config.txt
# Enable I2C bus 1
dtparam=i2c_arm=on

# Optional: increase bus speed from 100kHz to 400kHz
# dtparam=i2c_arm_baudrate=400000
2

Install I2C tools and reboot

terminal
$ sudo apt install -y i2c-tools python3-smbus2
$ sudo reboot

Wiring the BME280:

BME280 → uConsole wiring
BME280 Sensor          uConsole GPIO Header
┌──────────┐          ┌──────────────────┐
│ VCC      ├──────────┤ Pin 1  (3.3V)    │
│ GND      ├──────────┤ Pin 9  (GND)     │
│ SDA      ├──────────┤ Pin 3  (GPIO 2)  │
│ SCL      ├──────────┤ Pin 5  (GPIO 3)  │
│ CSB      ├── NC     │                  │
│ SDO      ├── GND*   │                  │
└──────────┘          └──────────────────┘

* SDO pin sets I2C address:
  - SDO → GND  = address 0x76 (default)
  - SDO → VCC  = address 0x77

Only 4 wires needed: VCC, GND, SDA, SCL
3

Detect the sensor with i2cdetect

terminal — scan I2C bus
$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --

# 0x76 = BME280 detected! ✓

If you see -- everywhere, check your wiring. The most common mistake is swapping SDA and SCL.

4

Read sensor data with Python

terminal — install bme280 library
$ pip3 install smbus2 RPi.bme280
read_bme280.py
#!/usr/bin/env python3
"""Read BME280 sensor data on uConsole via I2C."""

import smbus2
import bme280
import time

# I2C bus 1, BME280 at address 0x76
I2C_PORT = 1
BME280_ADDR = 0x76

def main():
    bus = smbus2.SMBus(I2C_PORT)

    # Load calibration data
    calibration = bme280.load_calibration_params(bus, BME280_ADDR)

    print("BME280 Sensor — uConsole I2C Demo")
    print("─" * 40)
    print("Press Ctrl+C to stop.\n")

    try:
        while True:
            data = bme280.sample(bus, BME280_ADDR, calibration)

            print(f"  Temperature:  {data.temperature:.1f} °C")
            print(f"  Humidity:     {data.humidity:.1f} %")
            print(f"  Pressure:     {data.pressure:.1f} hPa")
            print(f"  Timestamp:    {data.timestamp}")
            print("─" * 40)

            time.sleep(2)

    except KeyboardInterrupt:
        print("\nDone.")
    finally:
        bus.close()

if __name__ == "__main__":
    main()
5

Run it

terminal — sample output
$ python3 read_bme280.py
BME280 Sensor — uConsole I2C Demo
────────────────────────────────────────

  Temperature:  22.4 °C
  Humidity:     48.2 %
  Pressure:     1013.2 hPa
  Timestamp:    2026-03-07 14:32:18.442359
────────────────────────────────────────
  Temperature:  22.5 °C
  Humidity:     48.1 %
  Pressure:     1013.3 hPa
  ...

✓ MULTI-DEVICE: I2C supports up to 127 devices on the same two wires. You can connect a BME280, an OLED display, and a light sensor simultaneously — as long as each has a unique address. Check addresses with i2cdetect -y 1 after adding each device.

05 — SPI Peripherals

SPI displays & accessories.

moderate
Impact

SPI (Serial Peripheral Interface) is faster than I2C and ideal for displays, SD cards, and high-bandwidth peripherals. The uConsole's CM4 exposes SPI0 on the GPIO header with two chip-select lines (CE0 and CE1), letting you connect up to two SPI devices simultaneously.

Enable SPI:

/boot/config.txt
# Enable SPI bus 0
dtparam=spi=on

# For specific SPI overlays (e.g., SPI display):
# dtoverlay=spi0-1cs   # Only one chip-select
# dtoverlay=spi0-2cs   # Two chip-selects (default)

SPI pin mapping:

FunctionGPIO (BCM)Physical PinDirection
MOSI (Data Out)GPIO 1019Output
MISO (Data In)GPIO 921Input
SCLK (Clock)GPIO 1123Output
CE0 (Chip Select 0)GPIO 824Output
CE1 (Chip Select 1)GPIO 726Output

Example: SPI OLED display (SSD1306):

SSD1306 OLED → uConsole wiring
SSD1306 OLED (128x64)    uConsole GPIO Header
┌──────────────┐        ┌──────────────────┐
│ VCC          ├────────┤ Pin 1  (3.3V)    │
│ GND          ├────────┤ Pin 6  (GND)     │
│ DIN (MOSI)   ├────────┤ Pin 19 (GPIO 10) │
│ CLK (SCLK)   ├────────┤ Pin 23 (GPIO 11) │
│ CS           ├────────┤ Pin 24 (GPIO 8)  │
│ DC           ├────────┤ Pin 18 (GPIO 24) │
│ RST          ├────────┤ Pin 22 (GPIO 25) │
└──────────────┘        └──────────────────┘
terminal — install and test
$ pip3 install luma.oled

# Quick test script
$ python3 -c "
from luma.core.interface.serial import spi
from luma.oled.device import ssd1306
from luma.core.render import canvas

serial = spi(device=0, port=0, gpio_DC=24, gpio_RST=25)
device = ssd1306(serial)

with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline='white')
    draw.text((10, 20), 'uConsole GPIO!', fill='white')
print('Display updated!')
"

uConsole-specific pitfalls:

Cable length matters

SPI is sensitive to cable length. On the uConsole's compact form factor, keep jumper wires under 15cm. Longer cables introduce noise and signal degradation, especially at higher clock speeds.

SPI clock speed

The default SPI clock is often too fast for cheap displays. Start with 1MHz and increase. Some SSD1306 modules max out at 8MHz.

Power draw from multiple SPI devices

An SPI display + SD card reader can draw 80-150mA combined. This is near the safe limit of the 3.3V rail. See Section 06 for power budgeting.

DC and RST pins

SPI displays need Data/Command (DC) and Reset (RST) pins beyond the standard SPI bus. Choose GPIO pins that aren't reserved. GPIO 24 and 25 work well.

06 — Power Management

Powering external hardware safely.

advanced
Impact

The uConsole runs on two 18650 lithium cells (~7.4V nominal, regulated down to 5V and 3.3V). Tapping into GPIO power for external hardware is possible — but you need to understand the current limits to avoid brownouts, instability, or damage.

⚠ WARNING: Drawing too much current from the GPIO header can cause the uConsole to crash, reboot, or damage the power regulator. Always calculate your power budget before connecting external devices.

Power budget reference:

RailVoltageSafe DrawNotes
3.3V (Pin 1, 17)3.3V~50mA totalShared with CM4 peripherals
5V (Pin 2, 4)5.0V~300mA*Shared with USB ports
Individual GPIO3.3V~16mA per pinSource or sink
Total GPIO3.3V~50mA combinedAll active GPIO pins total

* The 5V rail shares the same supply as USB. If you have USB devices plugged in, your available current drops accordingly. The internal WiFi and display also draw from the 5V bus.

Sample power budget:

power-budget.txt — example calculation
uConsole GPIO Power Budget Calculator
═══════════════════════════════════════

3.3V Rail (max ~50mA available for GPIO):
  BME280 sensor        :   0.3 mA (sleep) / 1.0 mA (active)
  SSD1306 OLED (I2C)   :  10.0 mA
  Status LED (w/ 330Ω) :  10.0 mA
  ─────────────────────────────────
  Total 3.3V draw      :  21.0 mA  ✓ SAFE (42% of budget)

5V Rail (max ~300mA available via GPIO):
  Servo motor (SG90)   : 150.0 mA (stall)
  NeoPixel strip (5px) :  60.0 mA (full white)
  ─────────────────────────────────
  Total 5V draw        : 210.0 mA  ⚠ CAUTION (70% of budget)

Rules of thumb:
  ✓ Under 50% = comfortable headroom
  ⚠ 50-80%    = works but monitor for brownouts
  ✗ Over 80%  = use external power supply

When to use external power:

✓ Safe from GPIO header
  • • Single I2C sensor (BME280, MPU6050)
  • • Small OLED display (SSD1306)
  • • A few LEDs with resistors
  • • Low-power logic ICs
  • • GPS module (idle mode)
✗ Needs external power
  • • Servo motors / stepper motors
  • • LED strips (NeoPixel, WS2812B)
  • • TFT displays larger than 1.8"
  • • Relay modules
  • • Multiple sensors simultaneously

Protection circuits:

When powering external hardware, add these protection components to avoid damaging your uConsole:

Decoupling capacitor (100nF)

Place a 100nF ceramic capacitor between VCC and GND near each sensor/IC. Filters high-frequency noise that can cause I2C/SPI errors.

Bulk capacitor (10-100µF)

A larger electrolytic cap on the power rail absorbs current spikes from motors and displays during startup or mode changes.

Schottky diode (1N5817)

Place inline on external power supply to prevent reverse current flow into the uConsole if both power sources are connected simultaneously.

Resettable fuse (100mA polyfuse)

Inline on the 3.3V line — if something shorts, the fuse trips and resets when the fault is cleared. Much cheaper than a new CM4 module.

07 — Recommended Gear

Recommended accessories.

easy

You don't need much to start hardware hacking with the uConsole. Here's the gear we recommend — tested for compatibility and sized for the uConsole's portable form factor.

~$12-15

Breakout & Breadboard

  • 40-pin GPIO ribbon cable + T-cobbler breakout
  • Half-size breadboard (fits in uConsole carry case)
  • Male-to-female jumper wires (20cm, 40-pack)
Adafruit, Amazon
~$3-8

Logic Level Converters

  • Bi-directional 3.3V ↔ 5V converter (4-channel)
  • BSS138-based modules (I2C safe)
  • TXB0108 for SPI (8-channel, auto-direction)
SparkFun, Adafruit
~$12-15

USB-to-GPIO Alternatives

  • Adafruit FT232H — USB to GPIO/SPI/I2C
  • MCP2221A — USB-C to GPIO/I2C/ADC
  • Great when you don't want to open the case
Adafruit, Mouser
~$2-5 each

Beginner Sensors

  • BME280 — temp, humidity, pressure (I2C)
  • MPU6050 — accelerometer + gyroscope (I2C)
  • BH1750 — ambient light sensor (I2C)
AliExpress, Amazon
~$5-20

Displays

  • SSD1306 0.96" OLED (I2C or SPI)
  • ST7789 1.3" IPS TFT (SPI, 240×240)
  • E-ink 2.13" (SPI — great for low-power status)
Waveshare, Adafruit
~$5-10

Protection & Power

  • 100nF + 10µF capacitor assortment
  • 1N5817 Schottky diodes (10-pack)
  • Resettable polyfuses (100mA, 250mA)
Mouser, DigiKey

✓ STARTER KIT TIP: If you just want to get going, grab a BME280 sensor, a half-size breadboard, and a jumper wire pack. Total cost under $10 — and you'll have everything you need for the I2C project in Section 04.

08 — Skip the Setup

Want GPIO libraries pre-installed?

The Pocket Forge Toolkit includes libgpiod, Python bindings, I2C/SPI tools, and example scripts — all pre-configured and tested on the uConsole. Plus 30+ curated packages, an optimized desktop, and hardware-specific tuning. Flash and go in 15 minutes.

libgpiod readyI2C / SPI enabledExample scriptsPython 3.11+30+ packages

Not ready to buy?.

Join the waitlist for Pocket Forge updates — new hardware hacking guides, GPIO project templates, and early pricing. No spam, just signal.

>

Your email is stored securely. Unsubscribe anytime.