When I started studying for this project, I didn't imagine the possibility of integrating the Displayio library and its assistants with the Neopixel screen. This is not documented, but it is possible. In my research, I found a user on the Adafruit forum who presented an interesting solution for displaying text in marquee, with a bitmap font.
This solution opened up the possibility that other features of the DisplayIo library could be used with the Neopixel display.
One of the possibilities would be to use the image file processing from the Adafruit_imageload library to place BMP images directly on the display. In the Pixel_Framebuf library examples we see a method to do this when using the library with Raspberry Pi computers with linux, using Blinka. But the library itself does not have a method to do this on microcontrollers.
But I managed to do this in a roundabout way. I read the bitmap file and the palette, perform two loops, one for the width of the bitmap and the second for the height. I read the pixel color value in the bitmap and apply that same color to the LED. This is something that the BMP Reader library performs, and which can be implemented as a method in the Pixel_Framebuf library itself.
This method works very well, even for animations. The frame rate is very low, of course. But it is possible and it has become a good solution for creating the menu system that will allow you to choose games, in my Neopixel Arcade Project.
To use the example below, save the image file in a folder images, with the name all_frames.bmp.
import time
import board
import neopixel_spi as neopixel
import displayio
import adafruit_imageload # To use Tile Sheet
from adafruit_pixel_framebuf import PixelFramebuffer, VERTICAL
spi = board.SPI()
pixel_pin = board.D10
pixel_width = 16
pixel_height = 32
num_pixels = pixel_width * pixel_height
pixels = neopixel.NeoPixel_SPI(
spi,
pixel_width * pixel_height,
brightness=0.2,
auto_write=False,
)
screen = PixelFramebuffer(
pixels,
pixel_width,
pixel_height,
rotation=2,
reverse_x=True,
#orientation=VERTICAL,
)
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load(
"images/all_frames.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette,
)
frame_index = 0 # Actual Frame
frame_delay = 0.2 # Frame Rate.
frame_num = 16 # Amount of Frames
frame_width = 16
frame_height = 32
last_frame_time = time.monotonic()
while True:
# Clear Screen
screen.fill(0)
# Calculate actual tile in Bitmap
tile_x = (frame_index % frame_num) * frame_width # X position of tile in sprite_sheet
tile_y = 0 # All tiles in same line
# Show tile in neopixel Screen - This is the function
for x in range(frame_width): # Tile Width
for y in range(frame_height): # Tile Height
pixel_color = sprite_sheet[tile_x + x, tile_y + y]
# Extract RGB Colors
r = (pixel_color >> 11) & 0x1F # Red
g = (pixel_color >> 5) & 0x3F # Green
b = pixel_color & 0x1F # Blue
# Convert to RGB 8 Bits (0-255)
r = (r * 255) // 31
g = (g * 255) // 63
b = (b * 255) // 31
# Show it on Neopixel
neopixel_color = (r, g, b)
screen.pixel(x, y, neopixel_color)
# Check Next Frame
current_time = time.monotonic()
if current_time - last_frame_time >= frame_delay:
# Update next frame
frame_index = (frame_index + 1) % frame_num
last_frame_time = current_time
# Update Screen
screen.display()
Written as a simple function:
def display_bitmap(screen, tile_width, tile_height, bitmap, frame_index=0):
'''
Get any loaded Bitmap and Show it on Neopixel Screen
Can be used with single bitmap or tile_sheet
Parameters:
screen = The Neopixel Screen Object
tile_width and tile_height = The tile dimensions.
bitmap = The bitmap object loaded by Adafruit_Imageload
frame_index = optional. The first frame position
'''
bitmap_width = bitmap.width
bitmap_height = bitmap.height
# This two variables will calculate tile amount in any dimension.
# Usefull when you have only a bitmap with the same dimensions of screen
tiles_per_row = bitmap_width // tile_width # Get tile amount in width
tiles_per_column = bitmap_height // tile_height # Get tile amount in height
if tiles_per_row * tiles_per_column > 1:
total_tiles = tiles_per_row * tiles_per_column
if frame_index >= total_tiles:
raise ValueError("Tile index out of range")
tile_x = (frame_index % tiles_per_row) * tile_width
tile_y = (frame_index // tiles_per_row) * tile_height
else:
tile_x = 0
tile_y = 0
for x in range(tile_width):
for y in range(tile_height):
pixel_color = bitmap[tile_x + x, tile_y + y]
# Extract RGB 16 bits data
r = (pixel_color >> 11) & 0x1F # RED
g = (pixel_color >> 5) & 0x3F # GREEN
b = pixel_color & 0x1F # BLUE
# Convert the components from 16 bits to 8 bits (0-255)
r = (r * 255) // 31
g = (g * 255) // 63
b = (b * 255) // 31
# Show it on Neopixel Screen (RGB 24 bits)
neopixel_color = (r, g, b)
screen.pixel(x, y, neopixel_color)
Rotating the little fella left or right using the joystickimport time
import board
import neopixel_spi as neopixel
import displayio
import adafruit_imageload # To use Tile Sheet
from adafruit_pixel_framebuf import PixelFramebuffer, VERTICAL
# Treating Joystick
from analogio import AnalogIn
from digitalio import DigitalInOut, Direction, Pull
from simpleio import map_range
# Prepare Joystick in Analog Pins
joystick_x = AnalogIn(board.A0)
joystick_y = AnalogIn(board.A1)
# Prepare Trigger Switch
trigger = DigitalInOut(board.D2)
trigger.direction = Direction.INPUT
trigger.pull = Pull.UP
spi = board.SPI()
pixel_pin = board.D10
pixel_width = 16
pixel_height = 32
num_pixels = pixel_width * pixel_height
pixels = neopixel.NeoPixel_SPI(
spi,
pixel_width * pixel_height,
brightness=0.2,
auto_write=False,
)
screen = PixelFramebuffer(
pixels,
pixel_width,
pixel_height,
rotation=2,
reverse_x=True,
#orientation=VERTICAL,
)
# Load the sprite sheet (bitmap)
sprite_sheet, palette = adafruit_imageload.load(
"images/all_frames.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette,
)
frame_index = 7 # Actual Frame in middle position
frame_delay = 0.2 # Frame Rate.
frame_num = 16 # Amount of Frames
frame_width = 16
frame_height = 32
joystick_delay = 0.1 # Delay para evitar mudanças rápidas
last_joystick_time = time.monotonic()
def display_bitmap(screen, tile_width, tile_height, bitmap, frame_index=0):
bitmap_width = bitmap.width
bitmap_height = bitmap.height
tiles_per_row = bitmap_width // tile_width
tiles_per_column = bitmap_height // tile_height
if tiles_per_row * tiles_per_column > 1:
total_tiles = tiles_per_row * tiles_per_column
if frame_index >= total_tiles:
raise ValueError("Tile index out of range")
tile_x = (frame_index % tiles_per_row) * tile_width
tile_y = (frame_index // tiles_per_row) * tile_height
else:
tile_x = 0
tile_y = 0
for x in range(tile_width):
for y in range(tile_height):
pixel_color = bitmap[tile_x + x, tile_y + y]
# Extrair os componentes RGB (vermelho, verde, azul) de 16 bits
r = (pixel_color >> 11) & 0x1F # Componente vermelho
g = (pixel_color >> 5) & 0x3F # Componente verde
b = pixel_color & 0x1F # Componente azul
# Converter os componentes de 16 bits em 8 bits (0-255)
r = (r * 255) // 31
g = (g * 255) // 63
b = (b * 255) // 31
# Exibir o pixel na tela Neopixel (formato RGB de 24 bits)
neopixel_color = (r, g, b)
screen.pixel(x, y, neopixel_color)
def get_joystick():
# Returns -1 0 or 1 depending on joystick position
x_coord = int (map_range (joystick_x.value, 200, 65535, - 2 , 2))
y_coord = int (map_range (joystick_y.value, 200, 65535, - 2 , 2))
return x_coord, y_coord
while True:
screen.fill(0)
# Show actual frame
display_bitmap(screen, frame_width, frame_height, sprite_sheet, frame_index)
screen.display()
current_time = time.monotonic()
if current_time - last_joystick_time >= joystick_delay:
x_coord, y_coord = get_joystick()
if y_coord == -1: # RIGHT
frame_index = (frame_index + 1) % frame_num
if frame_index > frame_num:
frame_index = 0
last_joystick_time = current_time
elif y_coord == 1: # LEFT
if frame_index < 0:
frame_index = frame_num
frame_index = (frame_index - 1) % frame_num
last_joystick_time = current_time
Comments
Please log in or sign up to comment.