0.91 inch OLED Display 128X64

🎮 The Big Title
Module Goal: the big canvas four times the pixels of the mini-board, multiple text sizes, real drawing shapes, and true animation. It ends with the course's first video game: Rock-Paper-Scissors, robot vs. you, faces and all.
🔍 Detective note: if this screen stays dark with address 0x3c, some 0.96" modules use 0x3d swap the address in the setup block. Two OLEDs, two possible house numbers.
📦 CONCEPT BOX Four times the canvas
128 × 64 = 8192 glowing dots, arranged in a coordinate system:
- x grows to the right: 0 (left wall) → 127 (right wall)
- y grows DOWNWARD: 0 (top) → 63 (bottom) upside down from math class, standard for screens!
Three new powers arrive:
- Text sizes: size 1 is small, size 2 doubles it, size 3 fills the stage. Big text = headlines.
- Shapes:
rect(outline),fill_rect(solid block),line, pixelyou draw geometry. - Animation: redraw the scene at a new position, publish, repeat and the eye sees movement.
Goal:
your robot's name, huge and centered the first impression.
Concepts:
Text Size · Centering · First Impressions
import machine # Controls the board hardware
import oled128x64 # Controls the OLED display
# Sets up the I2C connection for the OLED
# pin25 corresponds to 3 on the Sensor Shield V5.0
# pin26 corresponds to 2 on the Sensor Shield V5.0
# Sets up the OLED as a 128 x 64 pixel display
i2c_extend = machine.SoftI2C(scl = machine.Pin(26), sda = machine.Pin(25), freq = 100000)
oled = oled128x64.OLED(i2c_extend,address=0x3c,font_address=0x3A0000)
# Publish
oled.shows("CLU-BOTS",x = 0,y = 20,size = 2,center = True) # The name, triple size, dead center
💡 Note: size = 3 means each text pixel becomes a 3×3 block triple width, triple height. Try 1, 2, then 3 and watch the same words grow from sticky-note to billboard. Every dot tripled: size is just multiplication applied to the whole drawing. Then reposition with y: 20 puts the title near the vertical middle of 64 rows. First impressions are a coordinate decision.
🎮 The Position Laboratory
Goal:
visit the four corners of the screen mastering the x/y grid by traveling.
Concepts:
Coordinates · Screen Geography · Sequenced Layouts
import machine # Controls the board hardware
import oled128x64 # Controls the OLED display
import time # Controls pauses and timing
# Sets up the I2C connection for the OLED
# pin25 corresponds to 3 on the Sensor Shield V5.0
# pin26 corresponds to 2 on the Sensor Shield V5.0
# Sets up the OLED as a 128 x 64 pixel display
i2c_extend = machine.SoftI2C(scl = machine.Pin(26), sda = machine.Pin(25), freq = 100000)
oled = oled128x64.OLED(i2c_extend,address=0x3c,font_address=0x3A0000) # Wakes the screen
while True: # Tour forever
oled.fill(0) # Clear
oled.show() # Publish the wipe
oled.shows("TOP LEFT",x = 0,y = 0,size = 1,space = 0,center = False) # The origin corner
oled.show() # Publish
time.sleep(1) # Admire for 1 second
oled.fill(0) # Clear
oled.show() # Publish
oled.shows("TOP CENTER",x = 60,y = 0,size = 1,space = 0,center = True) # center
oled.show() # Publish
time.sleep(1) # Admire
oled.fill(0) # Clear
oled.show() # Publish
oled.shows("BOTTOM L",x = 0,y = 50,size = 1,space = 0,center = False) # South-west corner
oled.show() # Publish
time.sleep(1) # Admire
oled.fill(0) # Clear
oled.show() # Publish
oled.shows("BOTTOM C",x = 60,y = 50,size = 1,space = 0,center = True) # South-center corner
oled.show() # Publish
time.sleep(1) # Admire💡 Note: you just learned screen geography the best way by visiting it. Two honest observations: x=60 doesn't fully clear the right side (text is wider than 64 px at size 1 some letters may touch), and y=55 is the safe bottom because tall letters need headroom. Safe margins are discovered, not calculated nudge numbers until it looks right; that IS the skill.
🎮 The Boss Health Bar
Goal:
a battle-game health bar that drains from 100 to 0 outline, inner fill, percent counter, and the GAME OVER moment.
Concepts:
rect + fill_rect Together · Draining Animation · Game Feedback
import machine # Controls the board hardware
import oled128x64 # Controls the OLED display
import time # Controls pauses and timing
# Sets up the I2C connection for the OLED
# pin25 corresponds to 3 on the Sensor Shield V5.0
# pin26 corresponds to 2 on the Sensor Shield V5.0
# Sets up the OLED as a 128 x 64 pixel display
i2c_extend = machine.SoftI2C(scl = machine.Pin(26), sda = machine.Pin(25), freq = 100000)
oled = oled128x64.OLED(i2c_extend,address=0x3c,font_address=0x3A0000)
print("BOSS BATTLE the bar is draining!") # Backstage narration
oled.shows('BOSS',x = 0,y = 0,size = 2,space = 0,center = True) # The boss's name above the bar
oled.show() # Publish the name
for health in range(100, -1, -10): # health drains 100,90,80...0
oled.fill(0) # Clear the whole canvas
oled.shows('BOSS',x = 0,y = 0,size = 2,space = 0,center = True) # Rewrite the name
oled.shows(('HP ' + str(health)),x = 10,y = 50,size = 1,space = 0,center = False) # The numeric HP below
oled.rect(8, 28, 112, 14, 1) # The bar's OUTLINE (a hollow rectangle)
if health > 0: # Only draw fill while alive
oled.fill_rect(10, 30, health, 10, 1) # The solid fill: width = health!
oled.show() # Publish the frame
print("Boss HP:", health) # Serial narration
time.sleep_ms(700) # Dramatic drain speed
oled.shows('GAME OVER',x = 0,y = 20,size = 2,space = 0,center = True) # The verdict, big
oled.show() # Publish it
print("DEFEATED! The boss is gone.") # Grand endingSerial Monitor:
BOSS BATTLE the bar is draining!
Boss HP: 100
Boss HP: 90
...
Boss HP: 10
Boss HP: 0
DEFEATED! The boss is gone.
💡 Note: the outline + fill teamwork is a real technique: rect draws the empty frame (112 wide, fixed forever), and fill_rect draws the living part whose width equals the health. The frame stays, the fill shrinks inside it exactly how health bars, volume bars and battery icons work in every game and phone. And the drama secret: the if health > 0 guard at zero, no fill at all, an empty shell. One conditional turned a graph into a story.
🎮 The Sliding Heart
Goal:
true animation a Heart image crosses the screen from left wall to right wall, frame by frame.
Concepts:
Frame Animation · Position Loops · Persistence of Vision
import machine # Controls the board hardware
import oled128x64 # Controls the OLED display
import time # Controls pauses and timing
from expression_picture import Heart # Brings the Heart image
# Sets up the I2C connection for the OLED
# pin25 corresponds to 3 on the Sensor Shield V5.0
# pin26 corresponds to 2 on the Sensor Shield V5.0
# Sets up the OLED as a 128 x 64 pixel display
i2c_extend = machine.SoftI2C(scl = machine.Pin(26), sda = machine.Pin(25), freq = 100000)
oled = oled128x64.OLED(i2c_extend,address=0x3c,font_address=0x3A0000) # Wakes the screen
print("The heart is sliding across!") # Backstage narration
for x in range(0, 113, 8): # x marches right: 0,8,16...112
oled.fill(0) # Wipe the previous frame completely
oled.image(Heart,x = x,y = 2,size = 1) # Draw the heart at the NEW position
oled.show() # Publish the frame
time.sleep_ms(100) # 10 frames per second smooth!
print("The heart arrived!") # Ending💡 Note: this is the entire secret of animation, the same one used by cartoons, films and games: nothing actually moves the frames change fast and your eye lies to you. Each loop turn: erase everything, draw the heart 8 pixels further right, publish, pause 100 ms. At 10 frames per second, the jumps melt into gliding. Two experiments: raise the sleep to 400 ms and watch it become a slideshow (proof the eye did the moving), and change y to 0 or 50 to change the lane. You are one loop away from Pong.
🎮 Rock-Paper-Scissors Showdown
Goal:
the first video game of the course. You type R, P or S; the robot picks secretly at random and shows its hand as an image; the logic judges the round; the score accumulates. Type STOP for the final verdict.
Concepts:
Random Opponent · Game Logic (Win Combinations) · Score Keeping · Complete Synthesis
import machine # Controls the board hardware
import oled128x64 # Controls the OLED display
import time # Controls pauses and timing
import random # Controls random numbers
from expression_picture import Rock # Brings the Rock image
from expression_picture import Paper # Brings the Paper image
from expression_picture import Scissors # Brings the Scissors image
# Sets up the I2C connection for the OLED
# pin25 corresponds to 3 on the Sensor Shield V5.0
# pin26 corresponds to 2 on the Sensor Shield V5.0
# Sets up the OLED as a 128 x 64 pixel display
i2c_extend = machine.SoftI2C(scl = machine.Pin(26), sda = machine.Pin(25), freq = 100000)
oled = oled128x64.OLED(i2c_extend,address=0x3c,font_address=0x3A0000) # Wakes the screen
my_wins = 0 # Your score
robot_wins = 0 # The robot's score
print("=== ROCK PAPER SCISSORS ===") # Title
print("Type R, P or S STOP to end") # The rules
while True: # The match runs until STOP
choice = input("Your move: ") # Wait for your play
if choice == "STOP": # The quit command
break # Leave the match
my = 0 # Assume an invalid move for now
if choice == "R": # You played Rock
my = 1 # Rock's secret code is 1
elif choice == "P": # You played Paper
my = 2 # Paper's secret code is 2
elif choice == "S": # You played Scissors
my = 3 # Scissors' secret code is 3
else: # Unknown input
print("Use R, P or S only!") # The referee warns you
if my > 0: # Only play valid rounds
robot = random.randint(1, 3) # The robot secretly picks 1, 2 or 3
oled.fill(0) # Clear the arena
oled.show() # Publish the wipe
oled.shows(('YOU: ' + choice),x = 0,y = 0,size = 1,space = 0,center = False)
# Your move, top-left
if robot == 1: # Robot drew Rock
oled.image(Rock,x = 40,y = 4,size = 1) # Show its hand
robot_name = "ROCK" # Name for the report
elif robot == 2: # Robot drew Paper
oled.image(Paper,x = 40,y = 4,size = 1) # Show its hand
robot_name = "PAPER" # Name
else: # Robot drew Scissors
oled.image(Scissors,x = 40,y = 4,size = 1) # Show its hand
robot_name = "SCISSORS" # Name
oled.show() # Publish the arena
print("Robot plays:", robot_name) # Serial reveals it
if my == robot: # Same codes same move!
print("TIE! Nobody scores") # The verdict
elif (my == 1 and robot == 3) or (my == 2 and robot == 1) or (my == 3 and robot == 2): # Rock beats Scissors, Paper beats Rock, Scissors beats Paper
my_wins += 1 # Your score grows
print("YOU WIN! Score:", my_wins, "-", robot_wins) # The verdict
else: # Every other case
robot_wins += 1 # Robot scores
print("ROBOT WINS! Score:", my_wins, "-", robot_wins) # The verdict
time.sleep_ms(2000) # Admire the arena before the next round
print("========== FINAL SCORE ==========") # The summary wall
print("You:", my_wins, "| Robot:", robot_wins) # Both tallies
if my_wins > robot_wins: # You scored more?
print("CHAMPION: YOU! The machines bow.") # Glory
elif robot_wins > my_wins: # Robot scored more?
print("CHAMPION: THE ROBOT. Rematch?") # Defeat, politely
else: # Equal scores
print("A DRAW! Perfect balance.") # HarmonySerial Monitor:
=== ROCK PAPER SCISSORS ===
Type R, P or S STOP to end
Your move: R
Robot plays: SCISSORS
YOU WIN! Score: 1 - 0
Your move: P
Robot plays: SCISSORS
ROBOT WINS! Score: 1 - 1
Your move: S
Robot plays: S
TIE! Nobody scores
Your move: STOP
========== FINAL SCORE ==========
You: 1 | Robot: 1
A DRAW! Perfect balance.
💡 Note the graduation exercise: every idea you own appears on stage one last time. The command menu and input() (Module 1) · the else safety net (Mini-Assistant) · random numbers (the Dice) · break (Security System) · clear-publish frames (today) · and the crown jewel, the win-combination logic: (Rock beats Scissors) OR (Paper beats Rock) OR (Scissors beats Paper) one line holding the entire rules of the game. Design challenge for advanced teams: the robot plays purely randomly it can be beaten by pattern-spotting humans. Could it remember your last move and counter it? That single upgrade turns a toy into a strategy opponent... and it needs exactly one memory variable you already know how to use. The displays family is complete: your robot can now speak, show its face, draw, animate and play. Next stop the human controls: six buttons and two axes waiting on a shield.
