Let's start with a simple version of the snowglobe. This version does not require any additional bitmap files. You can set a background image if you want. But if you don't have one and just want to get up and running, just leave it alone and it will use a solid color.
Here's the code:
# SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries
#
# SPDX-License-Identifier: MIT
from random import randrange
import board
import busio
import displayio
from adafruit_gizmo import tft_gizmo
import adafruit_imageload
import adafruit_lis3dh
#---| User Config |---------------
BACKGROUND = 0xAA0000 # specify color or background BMP file
NUM_FLAKES = 50 # total number of snowflakes
SNOW_COLOR = 0xFFFFFF # snow color
SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive
#---| User Config |---------------
# Accelerometer setup
accelo_i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA)
accelo = adafruit_lis3dh.LIS3DH_I2C(accelo_i2c, address=0x19)
# Create the TFT Gizmo display
display = tft_gizmo.TFT_Gizmo()
# Load background image
try:
bg_bitmap, bg_palette = adafruit_imageload.load(BACKGROUND,
bitmap=displayio.Bitmap,
palette=displayio.Palette)
# Or just use solid color
except (OSError, TypeError, AttributeError):
BACKGROUND = BACKGROUND if isinstance(BACKGROUND, int) else 0x000000
bg_bitmap = displayio.Bitmap(display.width, display.height, 1)
bg_palette = displayio.Palette(1)
bg_palette[0] = BACKGROUND
background = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette)
# Shared palette for snow bitmaps
palette = displayio.Palette(2)
palette[0] = 0xADAF00 # transparent color
palette[1] = SNOW_COLOR # snow color
palette.make_transparent(0)
# Snowflake setup
FLAKES = (
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
)
flake_sheet = displayio.Bitmap(12, 4, len(palette))
for i, value in enumerate(FLAKES):
flake_sheet[i] = value
flake_pos = [0.0] * NUM_FLAKES
flakes = displayio.Group()
for _ in range(NUM_FLAKES):
flakes.append(displayio.TileGrid(flake_sheet, pixel_shader=palette,
width = 1,
height = 1,
tile_width = 4,
tile_height = 4 ) )
# Snowfield setup
snow_depth = [display.height] * display.width
snow_bmp = displayio.Bitmap(display.width, display.height, len(palette))
snow = displayio.TileGrid(snow_bmp, pixel_shader=palette)
# Add everything to display
splash = displayio.Group()
splash.append(background)
splash.append(flakes)
splash.append(snow)
display.root_group = splash
def clear_the_snow():
#pylint: disable=global-statement, redefined-outer-name
global flakes, flake_pos, snow_depth
display.auto_refresh = False
for flake in flakes:
# set to a random sprite
flake[0] = randrange(0, 3)
# set to a random x location
flake.x = randrange(0, display.width)
# set random y locations, off screen to start
flake_pos = [-1.0*randrange(0, display.height) for _ in range(NUM_FLAKES)]
# reset snow level
snow_depth = [display.height] * display.width
# and snow bitmap
for i in range(display.width * display.height):
snow_bmp[i] = 0
display.auto_refresh = True
def add_snow(index, amount, steepness=2):
location = []
# local steepness check
for x in range(index - amount, index + amount):
add = False
if x == 0:
# check depth to right
if snow_depth[x+1] - snow_depth[x] < steepness:
add = True
elif x == display.width - 1:
# check depth to left
if snow_depth[x-1] - snow_depth[x] < steepness:
add = True
elif 0 < x < display.width - 1:
# check depth to left AND right
if snow_depth[x-1] - snow_depth[x] < steepness and \
snow_depth[x+1] - snow_depth[x] < steepness:
add = True
if add:
location.append(x)
# add where snow is not too steep
for x in location:
new_level = snow_depth[x] - 1
if new_level >= 0:
snow_depth[x] = new_level
snow_bmp[x, new_level] = 1
while True:
clear_the_snow()
# loop until globe is full of snow
while snow_depth.count(0) < display.width:
# check for shake
if accelo.shake(SHAKE_THRESHOLD, 5, 0):
break
# update snowflakes
for i, flake in enumerate(flakes):
# speed based on sprite index
flake_pos[i] += 1 - flake[0] / 3
# check if snowflake has hit the ground
if int(flake_pos[i]) >= snow_depth[flake.x]:
# add snow where it fell
add_snow(flake.x, flake[0] + 2)
# reset flake to top
flake_pos[i] = 0
# at a new x location
flake.x = randrange(0, display.width)
flake.y = int(flake_pos[i])
display.refresh()
The features you can customize are grouped at the top of the code:
#---| User Config |--------------- BACKGROUND = 0xAA0000 # specify color or background BMP file NUM_FLAKES = 50 # total number of snowflakes SNOW_COLOR = 0xFFFFFF # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive #---| User Config |---------------
The code comments describe what they do. Here's a summary:
-
BACKGROUND- the background color OR image filename -
NUM_FLAKES- the total number of flakes to show -
SNOW_COLOR- the color of the snow -
SHAKE_THRESHOLD- shake-to-clear sensitivity
For now, just go ahead and try running it as is with the default values. You should get some nice white snow on a red background.
Change Colors
Now try changing the BACKGROUND and SNOW_COLOR values. Change those lines to something like this:
BACKGROUND = 0x00FF00 # specify color or background BMP file NUM_FLAKES = 50 # total number of snowflakes SNOW_COLOR = 0xFF00FF # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive
Hey! Yellow snow! On a green background.
Change Background
If you specify a bitmap file instead of a color for BACKGROUND, it will be loaded and used. For the TFT Gizmo, the file needs to be an indexed BMP file that is 240x240 in size. Here's one you can use to try this out:
Download that file and copy it to your CIRCUITPY folder. Then change the background setting line to:
BACKGROUND = "/blinka_dark.bmp" # specify color or background BMP file
And change the snow color back to white:
SNOW_COLOR = 0xFFFFFF # snow color
Run that and you should end up with snow falling over a Blinka background.
Change Flakes
You can do a minor amount of flake shape customization. We'll provide a fancier version of the code next that will really let you do this. But if you want, you can play around with these lines of code:
FLAKES = (
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
)
See how there are 3 separate 4x4 grids? Those are the flakes, which are each 4 pixels wide by 4 pixels high. A 0 ends up being transparent. A 1 ends up being the snow color. So you can edit those if you want to change the flake shape. But with only 4x4 pixels to work with, it's pretty minimal customization.
Page last edited January 21, 2025
Text editor powered by tinymce.