"""
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Raspberry Pi Pico SSD1306 OLED Display (MicroPython) ┃
┃ ┃
┃ A program to display Raspberry Pi logo, text, and a ┃
┃ simple timer animation on an SSD1306 OLED display ┃
┃ connected to a Raspberry Pi Pico. ┃
┃ ┃
┃ Copyright (c) 2023 Juan Carlos Arias ┃
┃ GitHub: github.com/striwensko ┃
┃ License: MIT ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
"""
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import framebuf, sys
import utime
class Oled:
"""
Represents an OLED display. Used an availavle SSD1306 library.
https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
Args:
width (int): The width of the display in pixels.
height (int): The height of the display in pixels.
"""
def __init__(self, width, height):
self.width = width
self.height = height
# Use I2C interface
i2c_dev = I2C(1, scl=Pin(27), sda=Pin(26), freq=400_000)
self.oled = SSD1306_I2C(width, height, i2c_dev)
def printText(self, text, x, y):
cursor_x = x;
# Why is divided by 8 and floored (using //) since the screen
# prints 8 pixels rows per byte by default
cursor_y = y // 8
cursor_end = x + len(text) * 8
if y % 8 == 0:
# Clear where we print the text
while cursor_x < cursor_end:
offset = cursor_x + cursor_y * self.width
self.oled.buffer[offset + 0] = 0x00
cursor_x += 1
elif y % 8 == 4:
# Clear where we print the text
while cursor_x < cursor_end:
offset = cursor_x + cursor_y * oled.width
self.oled.buffer[offset] = (self.oled.buffer[offset] & 0x0F)
offset += oled.width
self.oled.buffer[offset] = (self.oled.buffer[offset] & 0xF0)
cursor_x+= 1;
self.oled.text(text, x, y, 1)
def clear(self):
self.oled.fill(0)
def show(self):
self.oled.show()
startTime = utime.ticks_ms();
def getTime():
"""
Returns the time elapsed since the start time.
Returns:
int: The time elapsed in milliseconds.
"""
global startTime
return utime.ticks_diff(utime.ticks_ms(), startTime)
SINGLE_PRESS = const(0x00)
DOUBLE_PRESS = const(0x04)
LONG_PRESS = const(0x01)
IDLE = const(0x02)
RELEASE = const(0x03)
class Button():
def __init__(
self,
pin,
label = "",
isPullUp = False
):
self.isPullUp = isPullUp
if isPullUp:
self.button = Pin(pin, Pin.IN, Pin.PULL_UP)
else:
self.button = Pin(pin, Pin.IN, Pin.PULL_DOWN)
self.label = label
self.lastTimeTrue = getTime() - 1000
self.lastTimeFalse = -1
self.isPressed = False
self.lastTimePressed = -1
def update(self):
now = getTime()
status = IDLE
isKeyPressed = self.button.value()
if self.isPullUp:
isKeyPressed = not isKeyPressed
if isKeyPressed:
if not self.isPressed:
self.lastTimeTrue = now
if (utime.ticks_diff(now, self.lastTimePressed) < 250):
status = DOUBLE_PRESS
self.lastTimePressed = -1
else:
status = SINGLE_PRESS
self.lastTimePressed = now
# print(self.label + " pressed:" + str(now))
if utime.ticks_diff(now, self.lastTimeTrue) > 500:
self.lastTimeTrue = now
self.lastTimePressed = now
# print(self.label + " long pressed:" + str(now))
status = LONG_PRESS
self.isPressed = True
self.lastTimeFalse = -1
else:
if self.lastTimeFalse == -1:
self.lastTimeFalse = now
if utime.ticks_diff(now, self.lastTimeTrue) > 100:
if self.isPressed:
status = RELEASE
self.isPressed = False
return status
prevTime = getTime() - 10
FRAME_INDEX = 0
def updateFPS(buffer):
global prevTime, FRAME_INDEX, FPS
FPS = 1000 // (getTime() - prevTime)
prevTime = getTime()
buffer[(FRAME_INDEX) % 18] = 0xFF ^ (0xFF >> max((FPS - 16)// 2, 0))
buffer[(FRAME_INDEX + 1) % 18] = 0x00
FRAME_INDEX = (FRAME_INDEX + 1) % (18 * 5)
oled.printText(str(FPS), 20, 0)
oled = Oled(128, 64)
buffer = bytearray([
0xE3, 0xF7, 0xE3, 0xD7, 0xE3, 0xD7, 0xE3, 0xF7,
0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07,
0xDF, 0xFB, 0xD0, 0x0B, 0xDF, 0xFB, 0xD0, 0x0B,
0xDF, 0xFB, 0xD0, 0x0B, 0x5F, 0xFA, 0xDF, 0xFB,
])
bufferMonitor = bytearray([
0x00, 0x00, 0x7F, 0xFE, 0x60, 0x02, 0x60, 0x3A,
0x60, 0x72, 0x60, 0xE2, 0x61, 0xC2, 0x63, 0x82,
0x67, 0x02, 0x6E, 0x02, 0x60, 0x02, 0x7F, 0xFE,
0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x1F, 0xF8,
])
iconIndex = 0
labels = ["PC", "Xbox", "CD-Rom", "MacBook", "Camera", "Music", "Save"]
fbIcons = [
framebuf.FrameBuffer(buffer, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(bufferMonitor, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(buffer, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(bufferMonitor, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(buffer, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(bufferMonitor, 16, 16, framebuf.MONO_HLSB),
framebuf.FrameBuffer(buffer, 16, 16, framebuf.MONO_HLSB),
]
exitLoop = False;
exitButton = Pin(15, Pin.IN, Pin.PULL_DOWN)
btnBlue = Button(14)
btnGreen = Button(13)
while not exitLoop:
updateFPS(oled.oled.buffer)
oled.printText("t:" + str(getTime() % 10_000), 80, 0)
oled.printText("t:" + str(getTime() % 10_000), 80, 12)
leftStatus = btnBlue.update();
rightStatus = btnGreen.update();
if (leftStatus == SINGLE_PRESS or leftStatus == LONG_PRESS):
iconIndex = (iconIndex + 1) % len(fbIcons)
if (rightStatus == SINGLE_PRESS or rightStatus == LONG_PRESS):
iconIndex = (iconIndex - 1 + len(fbIcons)) % len(fbIcons)
oled.oled.blit(fbIcons[(iconIndex - 2 + len(fbIcons)) % len(fbIcons)], 4 + 26 * 0, 40)
oled.oled.blit(fbIcons[(iconIndex - 1 + len(fbIcons)) % len(fbIcons)], 4 + 26 * 1, 40)
oled.oled.blit(fbIcons[(iconIndex - 0 + len(fbIcons)) % len(fbIcons)], 4 + 26 * 2, 40)
oled.oled.blit(fbIcons[(iconIndex + 1 + len(fbIcons)) % len(fbIcons)], 4 + 26 * 3, 40)
oled.oled.blit(fbIcons[(iconIndex + 2 + len(fbIcons)) % len(fbIcons)], 4 + 26 * 4, 40)
oled.oled.blit(fbIcons[iconIndex], 56, 0)
oled.printText(labels[iconIndex], 0, 20)
# TODO [HW-005]: Calc number of characters per line
# Issue with characters perline
oled.printText("123456789012345678901234567890", 0, 28)
oled.show()
exitLoop = exitButton.value()
oled.clear();