It's easy to use OLEDs with Python and the Adafruit CircuitPython DisplayIO SSD1306 module. This module allows you to easily write Python code to control the display.
To demonstrate the usage, we'll initialize the library and use Python code to control the OLED from the board's Python REPL.
I2C Initialization
If your display is connected to the board using I2C (like if using a Feather and the FeatherWing OLED) you'll first need to initialize the I2C bus. First import the necessary modules:
import board
Now for either board run this command to create the I2C instance using the default SCL and SDA pins (which will be marked on the boards pins if using a Feather or similar Adafruit board):
i2c = board.I2C()
Some boards (such as the ESP32-S2 Feather) may need the I2C_POWER enabled before I2C can be used.
After initializing the I2C interface for your firmware as described above, you can create an instance of the I2CDisplay bus:
import displayio import adafruit_displayio_ssd1306 display_bus = displayio.I2CDisplay(i2c, device_address=0x3c)
Finally, you can pass the display_bus in and create an instance of the SSD1306 I2C driver by running:
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
Now you should be seeing an image of the REPL. Note that the last two parameters to the SSD1306
class initializer are the width and height of the display in pixels. Be sure to use the right values for the display you're using!
display_bus = displayio.I2CDisplay(i2c, device_address=0x3c, reset=board.D9)
At this point the I2C bus and display are initialized.
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ This test will initialize the display using displayio and draw a solid white background, a smaller black rectangle, and some white text. """ import board import displayio # Compatibility with both CircuitPython 8.x.x and 9.x.x. # Remove after 8.x.x is no longer a supported release. try: from i2cdisplaybus import I2CDisplayBus except ImportError: from displayio import I2CDisplay as I2CDisplayBus import terminalio from adafruit_display_text import label import adafruit_displayio_ssd1306 displayio.release_displays() i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller display_bus = I2CDisplayBus(i2c, device_address=0x3C) display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32) # Make the display context splash = displayio.Group() display.root_group = splash color_bitmap = displayio.Bitmap(128, 32, 1) color_palette = displayio.Palette(1) color_palette[0] = 0xFFFFFF # White bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite) # Draw a smaller inner rectangle inner_bitmap = displayio.Bitmap(118, 24, 1) inner_palette = displayio.Palette(1) inner_palette[0] = 0x000000 # Black inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=5, y=4) splash.append(inner_sprite) # Draw a label text = "Hello World!" text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=28, y=15) splash.append(text_area) while True: pass
Let's take a look at the sections of code one by one. We start by importing the board so that we can initialize SPI
, displayio
,terminalio
for the font, a label
, and the adafruit_displayio_ssd1306
driver.
import board import displayio import terminalio from adafruit_display_text import label import adafruit_displayio_ssd1306
Next we release any previously used displays. This is important because if the microprocessor is reset, the display pins are not automatically released and this makes them available for use again.
displayio.release_displays()
The FeatherWing uses I2C, so we set the I2C object to the board's I2C with the easy shortcut function board.I2C()
. By using this function, it finds the SPI module and initializes using the default SPI parameters. We also set the display bus to I2CDisplay which makes use of the I2C bus.
# Use for I2C i2c = board.I2C() display_bus = displayio.I2CDisplay(i2c, device_address=0x3c)
Finally, we initialize the driver with a width of the 128 variable and a height of the 32 variable. If we stopped at this point and ran the code, we would have a terminal that we could type at and have the screen update.
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
Next we create a background splash image. We do this by creating a group that we can add elements to and adding that group to the display. In this example, we are limiting the maximum number of elements to 10, but this can be increased if you would like. The display will automatically handle updating the group.
splash = displayio.Group(max_size=10) display.show(splash)
Next we create a Bitmap that is the full width and height of the display. The Bitmap is like a canvas that we can draw on. In this case we are creating the Bitmap to be the same size as the screen, but only have one color. Although the Bitmaps can handle up to 256 different colors, the display is monochrome so we only need one. We create a Palette with one color and set that color to 0xFFFFFF
which happens to be white. If were to place a different color here, displayio
handles color conversion automatically, so it may end up black or white depending on the calculation.
color_bitmap = displayio.Bitmap(128, 32, 1) color_palette = displayio.Palette(1) color_palette[0] = 0xFFFFFF # White
With all those pieces in place, we create a TileGrid by passing the bitmap and palette and draw it at (0, 0)
which represents the display's upper left.
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0) splash.append(bg_sprite)
Next we will create a smaller black rectangle. The easiest way to do this is the create a new bitmap that is a little smaller than the full screen with a single color of 0x000000
, which is black, and place it in a specific location. In this case, we will create a bitmap that is 5 pixels smaller on each side. The screen we're using here is 128x32 and we have the border set to 5 , so we'll want to subtract 10 from each of those numbers.
We'll also want to place it at the position (5, 5)
so that it ends up centered.
inner_bitmap = displayio.Bitmap(118, 22, 1) inner_palette = displayio.Palette(1) inner_palette[0] = 0x000000 # Black inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=5, y=5) splash.append(inner_sprite)
Since we are adding this after the first square, it's automatically drawn on top. Here's what it looks like now.
Next add a label that says "Hello World!" on top of that. We're going to use the built-in Terminal Font. In this example, we won't be doing any scaling because of the small resolution, so we'll add the label directly the main group. If we were scaling, we would have used a subgroup.
Labels are centered vertically, so we'll place it at half the heigh for the Y coordinate and subtract one so it looks good. We'll set the width to around 28 pixels make it appear to be centered horizontally, but if you want to change the text, change this to whatever looks good to you. Let's go with some white text, so we'll pass it a value of 0xFFFFFF
.
text = "Hello World!" text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=28, y=15) splash.append(text_area)
Finally, we place an infinite loop at the end so that the graphics screen remains in place and isn't replaced by a terminal.
while True: pass
Using the Buttons
The buttons are simple buttons connected directly to IO lines. The specific GPIO can vary depending on the microcontroller you are using. See the Pinouts page for more specifics. Here is an example of using it with the Feather M4:
import time import board import digitalio button_pins = (board.D9, board.D6, board.D5) buttons = [] for pin in button_pins: button = digitalio.DigitalInOut(pin) button.switch_to_input(pull=digitalio.Pull.UP) buttons.append(button) while True: if not buttons[0].value: print("Button A Pressed") if not buttons[1].value: print("Button B Pressed") if not buttons[2].value: print("Button C Pressed") time.sleep(0.1)
Where to go from here
Be sure to check out this excellent guide to CircuitPython Display Support Using displayio
Text editor powered by tinymce.