A Go application for Single Board Computers that controls OLED and TFT displays, showing system stats and network information with rotating pages. Supports both I2C (OLED) and SPI (TFT colour) displays.
Works with any SBC that provides I2C or SPI devices:
- Raspberry Pi (all models)
- Radxa (Rock 3C, Rock 5B, Rock 4, etc.)
- Orange Pi (all models)
- Pine64 (all models)
- Banana Pi, Odroid, and any other Linux SBC
-
SSD1306 - 128x64, 128x32, or 96x16 monochrome OLED (I2C)
- Most common I2C OLED display
- Full support via periph.io
- Types:
ssd1306,ssd1306_128x64,ssd1306_128x32,ssd1306_96x16
-
ST7735 - Color TFT LCD (SPI)
- White-on-black rendering, RGB565 colour
- Types:
st7735/st7735_128x160(1.8"),st7735_128x128(1.44"),st7735_160x80(0.96" Waveshare)
-
UCTRONICS - 0.96" 160x80 colour TFT (I2C, Pi Rack Pro SKU_RM0004)
- Onboard MCU bridges I2C to the internal ST7735 — no SPI, DC or RST pins needed
- Fixed address
0x18; dimensions auto-set to 160x80 - Type:
uctronics_colour
- SH1106 - 128x64 monochrome (similar to SSD1306) — Types:
sh1106,sh1106_128x64 - SSD1327 - 128x128 / 96x96 4-bit grayscale OLED — Types:
ssd1327,ssd1327_128x128,ssd1327_96x96 - SSD1331 - 96x64 16-bit color OLED — Types:
ssd1331,ssd1331_96x64
These types are recognized and dimensions auto-set, but return a helpful error message explaining the driver is not yet implemented.
See DISPLAY_TYPES.md for detailed information and how to add new display drivers.
- System Monitoring: Display disk usage, RAM usage, and CPU temperature
- Network Information: Show IP addresses for configured network interfaces
- Rotating Pages: Automatically cycle through information pages
- Flexible Configuration: JSON-based configuration with multiple search paths and hot reload
- Systemd Integration: Run as a system service with automatic start
- Hardware Abstraction: Mock display for testing without physical hardware
- Comprehensive Testing: 75% test coverage with CI/CD support
- Structured Logging: JSON and console logging with configurable levels
- Prometheus Metrics: Optional metrics endpoint for monitoring
- Error Handling: Automatic retry with exponential backoff for I2C errors
- Health Monitoring: Component health tracking and status reporting
- Screen Saver: Dim or blank display on idle, with active-hours scheduling and manual wake
- Config Validation: Validate configuration without running the service
- Go 1.24 or later (for building from source)
- Supported display (see Supported Displays section)
- Any Linux-based SBC with I2C or SPI support
| SSD1306 Pin | SBC Pin | Description |
|---|---|---|
| VCC | 3.3V | Power |
| GND | GND | Ground |
| SCL | I2C SCL | Clock |
| SDA | I2C SDA | Data |
| ST7735 Pin | SBC Pin | Description |
|---|---|---|
| VCC | 3.3V | Power |
| GND | GND | Ground |
| SCL/SCK | SPI SCLK | SPI Clock |
| SDA/MOSI | SPI MOSI | SPI Data |
| CS | SPI CS0 (CE0) | Chip Select |
| DC/RS | Any GPIO (e.g. GPIO24) | Data/Command select |
| RST | Any GPIO (e.g. GPIO25) | Reset (optional but recommended) |
Raspberry Pi:
sudo raspi-config
# Select: Interface Options -> I2C -> Enable
sudo rebootRadxa (Rock 3C, Rock 5B, etc.):
# I2C is usually enabled by default in modern images
# Verify with: ls /dev/i2c-*Orange Pi / Pine64 / Other SBCs:
# Most modern Linux images have I2C enabled by default
# Check your board's documentation if I2C devices are not present
# Verify with: ls /dev/i2c-*Verify I2C is working:
# Install i2c-tools if not already installed
# For Debian/Ubuntu/Raspberry Pi OS:
sudo apt-get install -y i2c-tools
# For Fedora/RHEL/CentOS:
sudo dnf install -y i2c-tools
# or: sudo yum install -y i2c-tools
# Detect I2C devices (replace '1' with your I2C bus number if different)
sudo i2cdetect -y 1
# You should see your display address (typically 0x3C or 0x3D)Finding your I2C bus:
# List all I2C buses
ls /dev/i2c-*
# Common values: /dev/i2c-0, /dev/i2c-1, /dev/i2c-3, etc.
# Use the bus number in your config.jsoni2c-display is available directly from the Fedora repositories:
sudo dnf install i2c-display
# Enable and start
sudo systemctl enable --now i2c-display.serviceThis installs everything you need — binary, config, and systemd service. No building required.
Build and install packages locally on your SBC (Raspberry Pi, Radxa, Orange Pi, Pine64, etc.):
Building packages locally on ARM is fast and integrates better with your system's package manager.
Debian/Ubuntu/Raspberry Pi OS (DEB Package)
# Install build dependencies
sudo apt-get update
sudo apt-get install -y git golang debhelper devscripts dh-golang
# Clone and build
git clone https://github.com/ausil/i2c-display.git
cd i2c-display
make deb
# Install the package
sudo apt install ../i2c-display_*_arm64.deb # or *_armhf.deb for 32-bit
# Enable and start
sudo systemctl enable --now i2c-display.serviceThe package automatically:
- Installs the binary to
/usr/bin/i2c-displayd - Creates config at
/etc/i2c-display/config.json - Installs and enables the systemd service
- Can be removed with:
sudo apt remove i2c-display
Fedora/RHEL on ARM (RPM Package)
On Fedora, the easiest option is sudo dnf install i2c-display (see above). To build from source instead:
# Install build dependencies
sudo dnf install -y git golang rpm-build systemd-rpm-macros
# Clone and build
git clone https://github.com/ausil/i2c-display.git
cd i2c-display
make rpm
# Install the package
sudo dnf install rpm-build/RPMS/*/i2c-display-*.aarch64.rpm
# Enable and start
sudo systemctl enable --now i2c-display.serviceThe package automatically:
- Installs the binary to
/usr/bin/i2c-displayd - Creates config at
/etc/i2c-display/config.json - Installs and enables the systemd service
- Can be removed with:
sudo dnf remove i2c-display
Alternative: Pre-built Binaries
If you prefer not to build packages, you can use pre-built binaries:
# Download for ARM64 (e.g., Raspberry Pi 4, Rock 5B)
wget https://github.com/ausil/i2c-display/releases/latest/download/i2c-displayd-linux-arm64
chmod +x i2c-displayd-linux-arm64
sudo mv i2c-displayd-linux-arm64 /usr/bin/i2c-displayd
# Download for ARMv7 (e.g., Raspberry Pi 2/3)
# wget https://github.com/ausil/i2c-display/releases/latest/download/i2c-displayd-linux-armv7
# chmod +x i2c-displayd-linux-armv7
# sudo mv i2c-displayd-linux-armv7 /usr/bin/i2c-displayd
# Download for RISC-V 64-bit
# wget https://github.com/ausil/i2c-display/releases/latest/download/i2c-displayd-linux-riscv64
# chmod +x i2c-displayd-linux-riscv64
# sudo mv i2c-displayd-linux-riscv64 /usr/bin/i2c-displayd
# Create config directory and download config
sudo mkdir -p /etc/i2c-display
sudo wget -O /etc/i2c-display/config.json \
https://raw.githubusercontent.com/ausil/i2c-display/main/configs/config.example.json
# Download and install systemd service
sudo wget -O /etc/systemd/system/i2c-display.service \
https://raw.githubusercontent.com/ausil/i2c-display/main/systemd/i2c-display.service
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now i2c-display.serviceNote: Using packages is recommended as they integrate with your system's package manager for easier updates and removal.
Fedora users can install directly from the repos: sudo dnf install i2c-display
Other distros — build packages locally or use the pre-built binary from GitHub Releases:
Build RPM locally (RHEL, Rocky Linux, AlmaLinux, CentOS)
# Install build dependencies
sudo dnf install -y git golang rpm-build systemd-rpm-macros
# Clone and build
git clone https://github.com/ausil/i2c-display.git
cd i2c-display
make rpm
# Install the package
sudo dnf install rpm-build/RPMS/*/i2c-display-*.x86_64.rpm
# Enable and start
sudo systemctl enable --now i2c-display.serviceBuild DEB locally (Debian, Ubuntu, Linux Mint, Pop!_OS)
# Install build dependencies
sudo apt-get update
sudo apt-get install -y git golang debhelper devscripts dh-golang
# Clone and build
git clone https://github.com/ausil/i2c-display.git
cd i2c-display
make deb
# Install the package
sudo apt install ../i2c-display_*_amd64.deb
# Enable and start
sudo systemctl enable --now i2c-display.serviceOr use the pre-built binary:
wget https://github.com/ausil/i2c-display/releases/latest/download/i2c-displayd-linux-amd64
chmod +x i2c-displayd-linux-amd64
sudo mv i2c-displayd-linux-amd64 /usr/bin/i2c-displayd
# Then follow manual setup steps from ARM binary installation aboveQuick Install:
git clone https://github.com/ausil/i2c-display.git
cd i2c-display
sudo ./scripts/install.shManual Build:
# Build the binary
make build
# Install
sudo make install
# Enable and start the service
sudo systemctl enable i2c-display.service
sudo systemctl start i2c-display.serviceThe configuration file is searched in the following order:
- Path specified with
-configflag $I2C_DISPLAY_CONFIG_PATHenvironment variable/etc/i2c-display/config.json(system-wide)$HOME/.config/i2c-display/config.json(user-specific)./config.json(current directory)
See configs/config.example.json for a complete example:
{
"display": {
"type": "ssd1306",
"i2c_bus": "/dev/i2c-1",
"i2c_address": "0x3C",
"rotation": 0
},
"pages": {
"rotation_interval": "5s",
"refresh_interval": "1s"
},
"system_info": {
"hostname_display": "short",
"disk_path": "/",
"temperature_source": "/sys/class/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
},
"network": {
"auto_detect": true,
"interface_filter": {
"include": ["eth0", "wlan0", "usb0"],
"exclude": ["lo", "docker*", "veth*"]
},
"show_ipv4": true,
"show_ipv6": false,
"max_interfaces_per_page": 3
},
"logging": {
"level": "info",
"output": "stdout",
"json": false
},
"metrics": {
"enabled": false,
"address": ":9090"
}
}type: Display controller type (default:ssd1306)ssd1306orssd1306_128x64- Standard 128x64 OLED (I2C)ssd1306_128x32- Compact 128x32 OLED (I2C)ssd1306_96x16- Small 96x16 OLED (I2C)st7735/st7735_128x160- 1.8" 128x160 TFT (SPI)st7735_128x128- 1.44" 128x128 TFT (SPI)st7735_160x80- 0.96" 160x80 TFT (SPI, e.g. Waveshare)uctronics_colour- 0.96" 160x80 colour TFT on UCTRONICS Pi Rack Pro (I2C, address0x18auto-set)- See DISPLAY_TYPES.md for all supported types
I2C displays only:
-
i2c_bus: I2C bus device path (default:/dev/i2c-1)- Find your bus:
ls /dev/i2c-* - Common values:
/dev/i2c-0,/dev/i2c-1,/dev/i2c-3
- Find your bus:
-
i2c_address: I2C device address in hexadecimal (default:0x3C)- Detect with:
sudo i2cdetect -y 1 - Common addresses:
0x3Cor0x3D
- Detect with:
SPI displays only:
-
spi_bus: SPI bus identifier (e.g.SPI0.0)- Find your bus:
ls /dev/spidev* SPI0.0corresponds to/dev/spidev0.0
- Find your bus:
-
dc_pin: GPIO pin name for the Data/Command line (required)- Example:
GPIO24
- Example:
-
rst_pin: GPIO pin name for the hardware reset line (optional but recommended)- Example:
GPIO25
- Example:
-
rotation: Display rotation in 90° increments (default:0)0- Normal orientation1- Rotated 90° clockwise2- Rotated 180° (upside down)3- Rotated 270° clockwise (90° counter-clockwise)
-
lines: Content line mode for 128×32 displays (default:0/ auto)0or2— standard mode: hostname header + separator + one metric per rotating page4— compact mode: mirrors the 128×64 layout (header + separator + 3 content lines + load graph) using a 5×7 font so all information fits in the 32 pixel height- Ignored on displays taller than 32 pixels
-
width/height: Display dimensions in pixels (optional)- Automatically set based on display type - no need to specify
- Only needed for custom/unsupported displays
-
rotation_interval: How often to rotate between pages- Format: Duration string (e.g.,
"5s","30s","2m") - Default:
"5s"
- Format: Duration string (e.g.,
-
refresh_interval: How often to update data on current page- Format: Duration string (e.g.,
"1s","500ms") - Default:
"1s"
- Format: Duration string (e.g.,
-
hostname_display: How to display the hostname"short"- Only hostname (e.g.,raspberrypi)"full"- Full FQDN (e.g.,raspberrypi.local)
-
disk_path: Filesystem path to monitor (default:"/")- Examples:
"/","/home","/mnt/data"
- Examples:
-
temperature_source: Path to CPU temperature sensor- Raspberry Pi:
/sys/class/thermal/thermal_zone0/temp - Radxa Rock 5B:
/sys/class/thermal/thermal_zone0/temp - Orange Pi:
/sys/class/thermal/thermal_zone0/tempor/sys/devices/virtual/thermal/thermal_zone0/temp - Pine64: Check
ls /sys/class/thermal/thermal_zone*/temp - Leave empty (
"") to disable temperature display
- Raspberry Pi:
-
temperature_unit: Display unit for temperature"celsius"- Display in °C"fahrenheit"- Display in °F
Finding your temperature sensor:
# List all thermal zones
for zone in /sys/class/thermal/thermal_zone*/temp; do
echo "$zone: $(cat $zone)"
done-
auto_detect: Automatically find network interfaces (default:true)- Set to
falseto manually specify interfaces
- Set to
-
interface_filter.include: Patterns for interfaces to show- Supports wildcards:
["eth*", "wlan*", "usb*"] - Default:
["eth0", "wlan0", "usb0"]
- Supports wildcards:
-
interface_filter.exclude: Patterns for interfaces to hide- Supports wildcards:
["lo", "docker*", "veth*"] - Useful to hide virtual interfaces
- Default:
["lo", "docker*", "veth*"]
- Supports wildcards:
-
show_ipv4: Display IPv4 addresses (default:true) -
show_ipv6: Display IPv6 addresses (default:false) -
max_interfaces_per_page: Maximum network interfaces per page (default:3)
Example interface configurations:
Show only Ethernet
"network": {
"auto_detect": true,
"interface_filter": {
"include": ["eth*", "en*"],
"exclude": ["lo", "wlan*", "docker*", "veth*"]
}
}Show WiFi and Ethernet
"network": {
"auto_detect": true,
"interface_filter": {
"include": ["eth*", "wlan*", "en*", "wl*"],
"exclude": ["lo", "docker*", "veth*"]
}
}Power saving feature to dim or blank the display after inactivity or outside configured hours.
-
enabled: Enable screen saver (default:false) -
mode: Screen saver behavior"dim"- Reduce brightness"blank"- Turn off display completely"off"- No screen saver
-
idle_timeout: Time before activating screen saver (ignored whenactive_hours.enabledistrue)- Format: Duration string (e.g.,
"5m","30m","1h") - Default:
"5m"
- Format: Duration string (e.g.,
-
dim_brightness: Brightness level when dimmed (0-255)- Default:
50
- Default:
-
normal_brightness: Normal operating brightness (0-255)- Default:
255
- Default:
-
wake_duration: How long a manual wake keeps the display on- Format: Duration string (e.g.,
"30s","2m") - Default:
"30s"
- Format: Duration string (e.g.,
-
active_hours: Time window during which the display is always kept onenabled: Enable active hours (default:false)start: Start of active window inHH:MM24-hour format (e.g.,"08:00")end: End of active window inHH:MM24-hour format (e.g.,"22:00")- Overnight ranges are supported (e.g.,
"22:00"to"06:00") - When active hours are enabled,
idle_timeoutis not required
Waking the display manually:
When the screensaver is active you can wake the display for wake_duration via:
# Via HTTP (requires metrics server enabled)
curl -X POST http://localhost:9090/wake
# Via signal
systemctl kill -s SIGUSR1 i2c-display.service
# or: kill -USR1 $(pidof i2c-displayd)Example — dim at night, always on during the day:
"screensaver": {
"enabled": true,
"mode": "dim",
"dim_brightness": 30,
"normal_brightness": 255,
"wake_duration": "30s",
"active_hours": {
"enabled": true,
"start": "08:00",
"end": "22:00"
}
}Example — idle timeout with manual wake:
"screensaver": {
"enabled": true,
"mode": "blank",
"idle_timeout": "10m",
"dim_brightness": 0,
"normal_brightness": 255,
"wake_duration": "60s"
}-
level: Log level verbosity"debug"- Very verbose, includes all details"info"- Normal operation information"warn"- Warnings only"error"- Errors only- Default:
"info"
-
output: Where to send logs"stdout"- Standard output"stderr"- Standard error- Default:
"stdout"
-
json: Log formattrue- JSON format (good for log aggregation)false- Human-readable console format- Default:
false
Prometheus-compatible metrics endpoint for monitoring.
-
enabled: Enable metrics endpoint (default:false) -
address: HTTP server address and port- Format:
"host:port"or":port" - Examples:
":9090","127.0.0.1:9090","0.0.0.0:9090" - Default:
":9090"
- Format:
When enabled, metrics are available at http://address/metrics
Example metrics:
- Display update count and errors
- I2C communication metrics
- Page rotation statistics
- System resource usage
Raspberry Pi with SSD1306 128x64
{
"display": {
"type": "ssd1306_128x64",
"i2c_bus": "/dev/i2c-1",
"i2c_address": "0x3C",
"rotation": 0
},
"system_info": {
"temperature_source": "/sys/class/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
},
"network": {
"auto_detect": true,
"interface_filter": {
"include": ["eth0", "wlan0"],
"exclude": ["lo", "docker*"]
}
}
}Radxa Rock 5B
{
"display": {
"type": "ssd1306_128x64",
"i2c_bus": "/dev/i2c-7",
"i2c_address": "0x3C",
"rotation": 0
},
"system_info": {
"temperature_source": "/sys/class/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
},
"network": {
"auto_detect": true,
"interface_filter": {
"include": ["eth*", "wlan*", "en*"],
"exclude": ["lo", "docker*", "veth*"]
}
}
}Orange Pi with Smaller Display (128x32)
{
"display": {
"type": "ssd1306_128x32",
"i2c_bus": "/dev/i2c-0",
"i2c_address": "0x3C",
"rotation": 0,
"lines": 2
},
"pages": {
"rotation_interval": "10s",
"refresh_interval": "2s"
},
"system_info": {
"temperature_source": "/sys/devices/virtual/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
}
}Use "lines": 4 to mirror the 128×64 layout with all metrics on one pass using a compact 5×7 font.
Pine64 with Screen Saver
{
"display": {
"type": "ssd1306_128x64",
"i2c_bus": "/dev/i2c-1",
"i2c_address": "0x3C",
"rotation": 0
},
"screensaver": {
"enabled": true,
"mode": "dim",
"idle_timeout": "5m",
"dim_brightness": 30,
"normal_brightness": 255
}
}ST7735 0.96" TFT (160x80, Waveshare) on Raspberry Pi
{
"display": {
"type": "st7735_160x80",
"spi_bus": "SPI0.0",
"dc_pin": "GPIO24",
"rst_pin": "GPIO25",
"rotation": 0
},
"system_info": {
"temperature_source": "/sys/class/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
}
}Enable SPI on Raspberry Pi: raspi-config → Interface Options → SPI → Enable
ST7735 1.44" TFT (128x128) on Raspberry Pi
{
"display": {
"type": "st7735_128x128",
"spi_bus": "SPI0.0",
"dc_pin": "GPIO24",
"rst_pin": "GPIO25",
"rotation": 0
},
"system_info": {
"temperature_source": "/sys/class/thermal/thermal_zone0/temp",
"temperature_unit": "celsius"
}
}Server/Headless Setup with Metrics
{
"display": {
"type": "ssd1306_128x64",
"i2c_bus": "/dev/i2c-1",
"i2c_address": "0x3C",
"rotation": 0
},
"logging": {
"level": "info",
"output": "stdout",
"json": true
},
"metrics": {
"enabled": true,
"address": ":9090"
}
}Access metrics: curl http://localhost:9090/metrics
# Start service
sudo systemctl start i2c-display.service
# Stop service
sudo systemctl stop i2c-display.service
# Restart service
sudo systemctl restart i2c-display.service
# Check status
sudo systemctl status i2c-display.service
# View logs
sudo journalctl -u i2c-display.service -f# With default config search
./bin/i2c-displayd
# With specific config
./bin/i2c-displayd -config /path/to/config.json
# With mock display (for testing)
./bin/i2c-displayd -mock -config configs/config.example.json
# Validate configuration without running
./bin/i2c-displayd -validate-config -config /path/to/config.json
# Reload configuration (send SIGHUP to running process)
sudo systemctl reload i2c-display.service
# Or: sudo kill -HUP $(pidof i2c-displayd)
# Wake the display (if screensaver is active)
sudo systemctl kill -s SIGUSR1 i2c-display.service
# Or: sudo kill -USR1 $(pidof i2c-displayd)# Build for current architecture
make build
# Build for Raspberry Pi (32-bit ARM)
make build-arm7
# Build for Raspberry Pi 4 / Rock 3C (64-bit ARM)
make build-arm64
# Build for RISC-V 64-bit
make build-riscv64
# Build all architectures (amd64, arm7, arm64, riscv64)
make build-all# Run unit tests
make test
# Run with mock display (no hardware needed)
make run-mock
# Run hardware tests (requires actual display)
make test-hardwarei2c-display/
├── cmd/
│ └── i2c-displayd/ # Main application entry point
├── internal/
│ ├── config/ # Configuration loading and validation
│ ├── display/ # Display abstraction layer and drivers
│ │ ├── ssd1306.go # SSD1306 I2C OLED driver
│ │ ├── st7735.go # ST7735 SPI TFT driver
│ │ ├── uctronics.go # UCTRONICS colour TFT driver
│ │ ├── factory.go # Display factory
│ │ └── mock.go # Mock display for testing
│ ├── renderer/ # Page rendering and layout
│ │ ├── layout.go # Adaptive layout for different display sizes
│ │ ├── system_page.go # System stats page (disk, RAM, CPU temp)
│ │ ├── network_page.go # Network interfaces page
│ │ ├── load_graph_page.go # Rolling load average graph page
│ │ ├── icons.go # Bitmap icons for metrics
│ │ ├── text.go # Text drawing helpers and color functions
│ │ └── smallfont.go # Compact 5×7 bitmap font for 128×32 lines=4 mode
│ ├── stats/ # System statistics collectors
│ ├── rotation/ # Page rotation manager
│ ├── screensaver/ # Screen saver (dim/blank on idle)
│ ├── health/ # Component health tracking
│ ├── metrics/ # Prometheus metrics endpoint
│ ├── logger/ # Structured logging (zerolog)
│ └── retry/ # Retry with exponential backoff
├── configs/ # Example configurations per display type
├── systemd/ # Systemd service file
├── scripts/ # Installation/uninstallation scripts
├── testdata/ # Test fixtures
├── Makefile
└── README.md
All pages show the hostname centered at the top, separated from content by a horizontal rule. Metric text is color-coded green/yellow/red based on usage thresholds (on colour displays).
┌──────────────────────────┐
│ hostname │ ← centered header
├──────────────────────────┤
│ [disk] 45.2% (12.5/27.6GB) │
│ [mem] 62.8% (2.5/4.0GB) │
│ [cpu] 45.2C │
└──────────────────────────┘
┌──────────────────────────┐
│ hostname │
├──────────────────────────┤
│ 1m:0.42 5m:0.38 15m:0.31│
│ ▂▃▄▃▂▁▂▃▅▄▃▂▁▂▃▄▃▂▁▂ │ ← rolling bar graph
│ ░░░░░░░░░░░░░░░░░░░░░ │ color: green/yellow/red
└──────────────────────────┘
┌──────────────────────────┐
│ hostname │
├──────────────────────────┤
│ eth0: 192.168.1.100 │
│ wlan0: 10.0.0.50 │
│ usb0: 172.16.0.1 │
│ Page 3/4 │
└──────────────────────────┘
128×32 displays have two layout modes controlled by display.lines:
lines=2 (default) — one metric per rotating page, full 32 px text:
┌──────────────────────────┐
│ hostname │
├──────────────────────────┤
│ [disk] 45.2% (12.5/27.6GB) │
└──────────────────────────┘
(separate pages for disk, RAM, CPU temp, load avg, network)
lines=4 — mirrors the 128×64 layout using a compact 5×7 font:
┌──────────────────────────┐
│ hostname │ ← 7 px header
├──────────────────────────┤ ← separator at row 7
│ D:45% 12.5/27.6G │ ← row 8
│ R:63% 2.5/4.0G │ ← row 16
│ C:45.2C │ ← row 24
└──────────────────────────┘
(same page rotation as 128×64: system → load graph → network)
-
Check I2C is enabled:
ls /dev/i2c-* -
Check I2C address:
sudo i2cdetect -y 1
-
Verify permissions:
sudo usermod -a -G i2c $USER # Log out and back in
-
Check service logs:
sudo journalctl -u i2c-display.service -n 50
-
Ensure SPI is enabled:
ls /dev/spidev* # Should show /dev/spidev0.0 (or similar)
-
On Raspberry Pi, enable SPI via
raspi-config→ Interface Options → SPI -
Verify GPIO pin numbers match your wiring:
# Check available GPIO names gpio readall # if wiringpi installed # or check /sys/class/gpio/
-
Check permissions:
sudo usermod -a -G spi $USER # Log out and back in
Different SBCs have different temperature sensor paths:
- Raspberry Pi:
/sys/class/thermal/thermal_zone0/temp - Rock 3C:
/sys/class/thermal/thermal_zone0/tempor/sys/devices/virtual/thermal/thermal_zone0/temp
Update temperature_source in your config accordingly.
Check your interface filter settings in the config. Use:
ip link showto see available interfaces and adjust the include patterns.
Enable metrics in your configuration:
{
"metrics": {
"enabled": true,
"address": "127.0.0.1:9090"
}
}Available metrics:
i2c_display_refresh_total- Total display refreshesi2c_display_refresh_errors_total- Display errors by typei2c_display_refresh_latency_seconds- Refresh latency histogrami2c_display_i2c_errors_total- I2C communication errorsi2c_display_cpu_temperature_celsius- Current CPU temperaturei2c_display_memory_used_percent- Memory usage percentagei2c_display_disk_used_percent- Disk usage percentagei2c_display_network_interfaces_count- Number of network interfacesi2c_display_current_page- Current page numberi2c_display_page_rotation_total- Total page rotations
Access metrics: curl http://127.0.0.1:9090/metrics
Wake endpoint:
When the screensaver is enabled, a POST to /wake temporarily wakes the display:
curl -X POST http://127.0.0.1:9090/wakeStructured logging with contextual information:
# Console output (human-readable)
{"level":"info","time":"2026-02-09T12:00:00Z","message":"Display service running"}
# JSON output (for log aggregation)
{
"level":"info",
"display_type":"ssd1306",
"bus":"/dev/i2c-1",
"time":"2026-02-09T12:00:00Z",
"message":"Initializing display hardware"
}sudo ./scripts/uninstall.shOr manually:
sudo systemctl stop i2c-display.service
sudo systemctl disable i2c-display.service
sudo make uninstallBSD 3-Clause License. See LICENSE for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
make test - Submit a pull request
- Built with periph.io for hardware abstraction
- Uses basicfont for standard text rendering
- Compact 5×7 bitmap font derived from the Adafruit GFX classic glyph table
For issues, questions, or contributions, please visit: https://github.com/ausil/i2c-display