4 · Displays & buses
OLED: plotting a curve
Expert30 min
Goal
Display the A0 value live as a waveform.
Why it matters
You turn the board into a standalone measuring instrument.
Steps
- 01Start from the OLED sketch.
- 02Store 128 samples in a circular buffer.
- 03Convert each value into a pixel height (0–63).
- 04Redraw the frame on every refresh.
Starter sketch
// OLED Hello — SSD1306 128x64 over I2C (SDA = A4, SCL = A5).
// The cloud toolchain ships only <Wire.h>, so this sketch includes a tiny
// built-in SSD1306 driver: no external libraries needed.
#include <Wire.h>
#define OLED_ADDR 0x3C
static const uint8_t FONT5X7[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7f, 0x14, 0x7f, 0x14, 0x24, 0x2a, 0x7f, 0x2a, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1c, 0x00, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, 0x08, 0x08, 0x3e, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x42, 0x7f, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4d, 0x33, 0x18, 0x14, 0x12, 0x7f, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39, 0x3c, 0x4a, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1e, 0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3e, 0x41, 0x5d, 0x59, 0x4e,
0x7c, 0x12, 0x11, 0x12, 0x7c, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x3e, 0x41, 0x41, 0x41, 0x22,
0x7f, 0x41, 0x41, 0x41, 0x3e, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x7f, 0x09, 0x09, 0x09, 0x01,
0x3e, 0x41, 0x41, 0x51, 0x73, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x41, 0x7f, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3f, 0x01, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x7f, 0x40, 0x40, 0x40, 0x40,
0x7f, 0x02, 0x1c, 0x02, 0x7f, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x3e, 0x41, 0x41, 0x41, 0x3e,
0x7f, 0x09, 0x09, 0x09, 0x06, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x7f, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7f, 0x01, 0x03, 0x3f, 0x40, 0x40, 0x40, 0x3f,
0x1f, 0x20, 0x40, 0x20, 0x1f, 0x3f, 0x40, 0x38, 0x40, 0x3f, 0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4d, 0x43, 0x00, 0x7f, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7f, 0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40,
0x7f, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7f,
0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7e, 0x09, 0x02, 0x18, 0xa4, 0xa4, 0x9c, 0x78,
0x7f, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7d, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3d, 0x00,
0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7f, 0x40, 0x00, 0x7c, 0x04, 0x78, 0x04, 0x78,
0x7c, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xfc, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xfc, 0x7c, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3f, 0x44, 0x24, 0x3c, 0x40, 0x40, 0x20, 0x7c, 0x1c, 0x20, 0x40, 0x20, 0x1c,
0x3c, 0x40, 0x30, 0x40, 0x3c, 0x44, 0x28, 0x10, 0x28, 0x44, 0x4c, 0x90, 0x90, 0x90, 0x7c,
0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02
};
void oledCmd(uint8_t c) {
Wire.beginTransmission(OLED_ADDR);
Wire.write(0x00);
Wire.write(c);
Wire.endTransmission();
}
void oledInit() {
Wire.begin();
const uint8_t init[] = {
0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40,
0x8D, 0x14, 0x20, 0x00, 0xA1, 0xC8, 0xDA, 0x12,
0x81, 0xCF, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0xAF
};
for (uint8_t i = 0; i < sizeof(init); i++) oledCmd(init[i]);
}
void oledWindow(uint8_t col, uint8_t page, uint8_t width) {
oledCmd(0x21); oledCmd(col); oledCmd(col + width - 1);
oledCmd(0x22); oledCmd(page); oledCmd(page);
}
void oledClear() {
oledCmd(0x21); oledCmd(0); oledCmd(127);
oledCmd(0x22); oledCmd(0); oledCmd(7);
for (uint16_t i = 0; i < 1024; i += 16) {
Wire.beginTransmission(OLED_ADDR);
Wire.write(0x40);
for (uint8_t j = 0; j < 16; j++) Wire.write(0x00);
Wire.endTransmission();
}
}
// Prints text at a character cell. scale = 1 (5x7) or 2 (10x14).
void oledText(uint8_t col, uint8_t page, const char *text, uint8_t scale) {
for (uint8_t row = 0; row < scale; row++) {
uint8_t x = col;
oledWindow(col, page + row, 128 - col);
Wire.beginTransmission(OLED_ADDR);
Wire.write(0x40);
uint8_t sent = 0;
for (const char *p = text; *p && x < 128; p++) {
for (uint8_t i = 0; i < 6; i++) {
uint8_t bits = (i < 5 && *p >= 32) ? pgm_read_byte(&FONT5X7[(*p - 32) * 5 + i]) : 0x00;
uint8_t slice = 0;
for (uint8_t b = 0; b < 8; b++) {
uint8_t src = (row * 8 + b) / scale;
if (src < 8 && (bits >> src) & 1) slice |= (1 << b);
}
for (uint8_t s = 0; s < scale && x < 128; s++) {
Wire.write(slice);
x++;
if (++sent >= 16) { Wire.endTransmission(); Wire.beginTransmission(OLED_ADDR); Wire.write(0x40); sent = 0; }
}
}
}
Wire.endTransmission();
}
}
int counter = 0;
void setup() {
Serial.begin(9600);
oledInit();
oledClear();
oledText(4, 0, "Circuitly", 2);
oledText(4, 3, "SSD1306 over I2C", 1);
Serial.println("OLED ready");
}
void loop() {
char line[24];
snprintf(line, sizeof(line), "uptime: %lu s", millis() / 1000);
oledText(4, 5, line, 1);
snprintf(line, sizeof(line), "frames: %d ", counter++);
oledText(4, 6, line, 1);
delay(500);
}
This tutorial runs inside the Circuitly simulator: your wiring and your code are checked automatically at every step.