Display Systems and Visual Output
Welcome, maker — let's give your robot a face!
My OLED display is what makes me look alive — those two glowing oval eyes you see are drawn by code. This chapter teaches you how to light me up with color LEDs, draw anything you want on my screen, and show live sensor data as animated charts. Visual output is your robot's way of talking back to you.
Summary
This chapter gives the robot a visual voice. Students program NeoPixel RGB LED strips for color sequences and animations, then dive into the 128×64 OLED display: driver chip, framebuffer model, and drawing primitives (text, lines, circles, rectangles). Practical projects include bar charts of live sensor data, a scrolling distance meter, an animated robot face, and a servo position display — all combining the display API with the sensors and motors from prior chapters.
Concepts Covered
This chapter covers the following 22 concepts from the learning graph:
- NeoPixel LEDs
- WS2816 LED Strip
- RGB Color Values
- NeoPixel Library
- LED Animation
- LED Status Indicators
- OLED Display Overview
- SSD1306 Driver Chip
- I2C Display Mode
- SPI Display Mode
- Display Resolution
- Framebuffer
- Blit Operation
- Display Text Output
- Drawing Lines
- Drawing Circles
- Drawing Rectangles
- Bar Chart on Display
- Live Sensor on Display
- Animated Faces on OLED
- Distance Meter Display
- Servo Meter Display
Prerequisites
This chapter builds on concepts from:
- Chapter 4: Control Flow, Functions, and Exception Handling
- Chapter 5: Data Structures, Modular Programming, and Version Control
- Chapter 6: Electronics, DC Motors, and Communication Protocols
- Chapter 7: PWM, Motor Speed Control, and Actuators
- Chapter 8: Sensors and Data Input
NeoPixel LEDs
A NeoPixel LED is a special type of RGB LED that contains a tiny controller chip inside the LED package itself. Unlike a standard LED that you control with voltage, a NeoPixel receives color and brightness commands through a single data wire. This means you can chain many NeoPixels together and control them all from a single GPIO pin.
The Cytron Maker Pi RP2040 has two built-in NeoPixel LEDs connected to GPIO pin 18. You can also attach external NeoPixel strips.
WS2812B — The Chip Inside
The chip inside each NeoPixel is the WS2812B (sometimes listed as WS2816 for certain variants). It receives a serial data stream at a specific timing protocol. You don't need to implement this protocol yourself — the MicroPython neopixel library handles it. But knowing the chip name helps when reading datasheets and looking up compatible LED strips.
RGB Color Values
Each NeoPixel LED has three color channels: R (red), G (green), and B (blue). Each channel takes a value from 0 (off) to 255 (full brightness). Mixing these three channels produces any color.
Before the table, here is the idea: (255, 0, 0) means full red, zero green, zero blue — a pure red. (0, 0, 255) is pure blue. (255, 255, 255) is white (all channels full). (0, 0, 0) is off.
| Color | R | G | B |
|---|---|---|---|
| Red | 255 | 0 | 0 |
| Green | 0 | 255 | 0 |
| Blue | 0 | 0 | 255 |
| Yellow | 255 | 200 | 0 |
| White | 255 | 255 | 255 |
| Off | 0 | 0 | 0 |
| Orange | 255 | 80 | 0 |
NeoPixel Library
The neopixel module is built into MicroPython. Before the code, here is what the parameters mean: Pin(18) selects GPIO 18, and 2 is the number of LEDs in the strip.
1 2 3 4 5 6 7 8 9 10 | |
Always call np.write() after setting colors. The colors don't update until you write them.
LED Animation
LED animation means changing the LEDs over time — fading, cycling colors, chasing patterns. A simple color cycle loops through hues:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
LED Status Indicators
LED status indicators use specific colors to communicate robot state — a traffic light pattern that anyone can read at a glance.
A common convention:
- Green = running normally, path clear
- Yellow/orange = caution, obstacle getting close
- Red = stop, obstacle very close
- Blue = standby / idle
- White flash = sensor reading in progress
1 2 3 4 5 6 7 8 | |
The OLED Display
Your robot's "face" is a small screen called an OLED display. OLED stands for Organic Light-Emitting Diode. Unlike LCD screens, each pixel in an OLED display emits its own light — there is no backlight. This gives OLED displays high contrast and sharp images, even at small sizes.
The display on this robot is 128 pixels wide and 64 pixels tall — a 128×64 resolution. It looks small (about 0.96 inches diagonal), but it is plenty of space for text, graphs, and animated faces.
SSD1306 Driver Chip
The SSD1306 is the controller chip built into most small OLED modules. It handles the low-level task of refreshing each pixel. You communicate with the SSD1306 over I2C (or SPI) using the ssd1306.py driver library. Copy ssd1306.py to your board's flash storage before using the display.
I2C Display Mode and SPI Display Mode
The SSD1306 supports both I2C and SPI connections. The module sold with this course uses I2C — four wires: VCC, GND, SDA, SCL. This is the simplest wiring.
SPI display mode uses six wires (VCC, GND, SCK, MOSI, DC, CS) but updates the screen faster — important for high frame-rate animations. For this course, I2C is sufficient.
Display Resolution and the Framebuffer
The display resolution is 128×64 — meaning 128 columns and 64 rows of pixels. Each pixel is either ON (white) or OFF (black). There is no color.
The framebuffer is an array in the microcontroller's memory that mirrors the display. When you draw text or shapes, you write to the framebuffer first. Nothing appears on the screen until you call show(), which copies the entire framebuffer to the display at once. This prevents flickering — instead of updating pixels one at a time, the whole frame updates in one fast transfer.
Before the code, here is what the parameters mean: I2C(0, ...) creates the I2C bus. SSD1306_I2C(128, 64, i2c) creates the display object with 128-column, 64-row resolution on that I2C bus.
1 2 3 4 5 6 7 8 9 | |
Blit Operation
A blit (block transfer) copies a rectangular region of pixels from one framebuffer to another. You use it to draw sprites — pre-drawn images — onto the display at specific positions. For simple robot programs, direct drawing functions are more common than blitting, but it's useful for displaying icons or custom fonts.
Drawing on the OLED
The ssd1306 library provides drawing functions. Let's learn each one before putting them together in projects.
The pattern for every drawing operation: call the drawing function to update the framebuffer, then call display.show() to push the framebuffer to the screen.
Display Text Output
Before the code, here is what the parameters mean: text(string, x, y) draws a string starting at column x, row y. The origin (0, 0) is the top-left corner. Each character is 8 pixels wide and 8 pixels tall (the built-in font).
1 2 3 4 | |
Drawing Lines, Circles, and Rectangles
The display library also draws shapes. Before the code, here is the parameter order for each: line(x1, y1, x2, y2, color), ellipse(x, y, xr, yr, color), rect(x, y, w, h, color).
color=1draws white (ON pixels)color=0draws black (erases pixels)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
fill(0) before drawing
Always call display.fill(0) before redrawing the screen in a loop. Without it, new content overlaps old content and the display turns into a muddy mess. Clear the framebuffer first, draw everything fresh, then call show() — that's the standard pattern for smooth-looking displays.
Practical Display Projects
Now let's build four practical projects that combine the drawing API with sensors and motors.
Bar Chart of Live Sensor Data
A bar chart on display shows the ToF sensor distance as a vertical bar that grows and shrinks in real time. This is a live data visualization — a simple version of the charts in scientific instruments.
Before the code, here is the math: if the max expected distance is 200 cm, we scale the current distance to fit in 50 pixels of bar height (0–50). bar_height = int(distance_cm / 200 * 50).
1 2 3 4 5 6 7 8 9 10 11 12 | |
Distance Meter Display
A distance meter display shows the current distance as text in large format, updating every loop iteration:
1 2 3 4 5 6 7 | |
Animated Faces on OLED
Animated faces on OLED use ellipses and rectangles to draw eyes and a mouth, then change their shape to show different robot states — happy, thinking, surprised. This is the same kind of code that makes Sparky's face change expression on the screen!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Diagram: OLED Coordinate System Explorer
Run OLED Coordinate System Explorer Fullscreen
Interactive MicroSim showing the OLED pixel coordinate system and drawing primitives
Type: MicroSim
sim-id: oled-coordinate-explorer
Library: p5.js
Status: Specified
Create a p5.js MicroSim with a 700 × 400 canvas. Show a scaled-up representation of the 128×64 OLED display (rendered 4× actual size = 512×256 pixels on canvas).
Features: - A black background rectangle representing the OLED screen. - A grid overlay (toggle button "Show grid") showing pixel positions every 8 pixels. - Mouse hover shows a tooltip: "Pixel: (x, y)" at the cursor position (in OLED coordinates, 0–127 x, 0–63 y). - Four buttons: "Draw Text", "Draw Line", "Draw Circle", "Draw Rect". - Each button draws the corresponding element at a random position and displays the corresponding ssd1306 Python code in a code box below the display. - A "Clear" button resets the display.
Learning objective (Bloom's Taxonomy — Applying): students practice placing drawing commands at specific coordinates and reading back the code equivalent.
Responsive: redraw on window resize.
Servo Meter Display
A servo meter display shows the current servo angle as a gauge — a horizontal bar from left (0°) to right (180°) with a moving indicator. This makes it easy to see the physical servo position on the screen without watching the servo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Putting It All Together — Live Sensor Dashboard
Here is a complete program that reads the ToF sensor and displays a live bar chart while the NeoPixels show status color. This combines displays, sensors, and LEDs in one program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
Frame rate matters
The sleep(0.05) gives 20 frames per second — smooth enough for a live chart. You could go faster, but the ToF sensor only updates reliably at about 50 Hz. Going faster than the sensor just wastes CPU time reading the same stale value. Always match your display refresh rate to your sensor's actual update rate.
Key Takeaways
- NeoPixel LEDs use a single data wire to control color (RGB, 0–255 per channel) — call
np.write()to update - LED status indicators use color to communicate robot state (green=clear, yellow=caution, red=stop)
- The OLED display is 128×64 pixels, controlled by the SSD1306 chip over I2C
- The framebuffer stores the image in RAM — draw to the framebuffer, then call
show()to update the screen display.text()draws 8×8 characters;display.line(),.ellipse(),.rect(),.fill_rect()draw shapes- Always call
display.fill(0)before redrawing to clear the previous frame - Live sensor dashboards combine sensors, displays, and LEDs for real-time data visualization
Your robot has a face, a voice, and now a dashboard!
Double thumbs-up, engineer! NeoPixels, OLED drawing, live data visualization — you now have every output tool in the course. In Chapter 10, we wire sensors and motors together and the robot starts navigating on its own. That's the milestone the whole course has been building toward!