How do I show live data on a small OLED?
Wire an SSD1306 OLED, render text, update once a minute from a sensor read
Small SSD1306 OLEDs (128×64 or 128×32) are common, cheap, and run on I2C. The bundled SSD1306 helper handles the init sequence and text rendering for you.
Wiring
The OLED uses the same I2C pins as the BME280 recipe — both can share the bus.
| OLED | Pico W |
|---|---|
| VCC | 3V3 |
| GND | GND |
| SDA | GP0 |
| SCL | GP1 |
devicesdk.ts
import { defineConfig } from "@devicesdk/cli";
export default defineConfig({
projectId: "oled-display",
devices: {
main: {
className: "OledDisplay",
main: "./src/devices/main.ts",
deviceType: "pico-w",
wifi: { ssid: "YOUR_WIFI_SSID", password: "YOUR_WIFI_PASSWORD" },
},
},
});
src/devices/main.ts
import { DeviceEntrypoint, type DeviceResponse } from "@devicesdk/core";
import { SSD1306 } from "@devicesdk/core/i2c";
import { Pico } from "@devicesdk/core/devices/pico";
const display = new SSD1306({
bus: 0,
address: "0x3C", // some panels are at 0x3D — run i2cScan if unsure
width: 128,
height: 64,
});
export class OledDisplay extends DeviceEntrypoint {
crons = { update: "* * * * *" };
async onDeviceConnect() {
await this.env.DEVICE.sendCommand(
Pico.i2c({ bus: 0, sda_pin: 0, scl_pin: 1 }),
);
await display.init(this.env.DEVICE);
await display.text(this.env.DEVICE, "Booting...", { x: 0, y: 0 });
}
async onCron() {
await this.env.DEVICE.getTemperature();
}
async onMessage(message: DeviceResponse) {
if (message.type !== "temperature_result") return;
const c = message.payload.celsius;
await display.clear(this.env.DEVICE);
await display.text(this.env.DEVICE, "Temp", { x: 0, y: 0 });
await display.text(this.env.DEVICE, `${c.toFixed(1)}°C`, { x: 0, y: 16, scale: 2 });
await display.text(
this.env.DEVICE,
new Date().toISOString().slice(11, 16) + " UTC",
{ x: 0, y: 48 },
);
}
}
What this demonstrates
- The
SSD1306helper bundles the init sequence and a simple text-drawing API. - Sharing the I2C bus with other sensors works fine — addresses are independent.
- Updating the screen on
temperature_resultkeeps the redraws scheduled by the firmware ack, not a fragile script-side timer.
Common gotchas
- 0.42" 72×40 panels. These have a column offset of 28 in controller RAM. The
SSD1306helper handles common offsets, but for non-standard glass sizes you may need to senddisplay_updatedirectly with a customcolumnOffset. - Address scan. If
initlooks like it succeeded but the screen is dark, runawait this.env.DEVICE.i2cScan(0)once and check the address — some boards default to0x3D, not0x3C.
Going further
- Combine with the BME280 recipe to display real humidity instead of the chip temperature.
- Use
emitStateso the dashboard mirrors what’s on the OLED.