Initial backup of LTP-305G matrix clock setup on matrixpi
Captures everything needed to redeploy the two-display clock (hour on I2C 0x61, minute on I2C 0x63) on a fresh Pi: - Both systemd units (matrix0x61.service, matrix0x63.service) - Deployed Pimoroni script tree, including the local %I (12-hour) clock customization - Vendored upstream sources (ltp305-python, breakout-garden) so restore is fully offline-capable - Boot config snippet enabling I2C - install.sh that wires it all back up idempotently - Inventory doc cross-referencing every live-system path Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
107
vendor/breakout-garden/examples/spirit-level/spirit-level.py
vendored
Executable file
107
vendor/breakout-garden/examples/spirit-level/spirit-level.py
vendored
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||
import math
|
||||
import msa301
|
||||
import ST7789 as ST7789
|
||||
|
||||
print("""This Pimoroni Breakout Garden example requires an
|
||||
MSA301 3DoF Motion Sensor Breakout and a 1.3" LCD Breakout.
|
||||
|
||||
This examples emulates a circular spirit level, using the
|
||||
LCD to draw the spirit level and the accelerometer to
|
||||
detect orientation.
|
||||
|
||||
Press Ctrl+C to exit.
|
||||
""")
|
||||
|
||||
# Set up LCD
|
||||
disp = ST7789.ST7789(
|
||||
port=0,
|
||||
cs=ST7789.BG_SPI_CS_FRONT,
|
||||
dc=9,
|
||||
backlight=19,
|
||||
spi_speed_hz=80 * 1000 * 1000
|
||||
)
|
||||
|
||||
WIDTH = disp.width
|
||||
HEIGHT = disp.height
|
||||
|
||||
disp.begin()
|
||||
|
||||
# Load the image assets
|
||||
level = Image.open("images/spirit-level.png").convert("RGBA")
|
||||
bubble = Image.open("images/spirit-level-bubble.png").convert("RGBA")
|
||||
crosshair_black = Image.open("images/spirit-level-crosshair-black.png").convert("RGBA")
|
||||
crosshair_red = Image.open("images/spirit-level-crosshair-red.png").convert("RGBA")
|
||||
|
||||
# Sizes/coordinates of things
|
||||
bubble_dia = 64
|
||||
bubble_rad = bubble_dia / 2
|
||||
circle_dia = 190
|
||||
circle_rad = circle_dia / 2
|
||||
border = (WIDTH - circle_dia) / 2
|
||||
|
||||
centre_x = WIDTH / 2
|
||||
centre_y = HEIGHT / 2
|
||||
|
||||
# Set up MSA301
|
||||
accel = msa301.MSA301()
|
||||
accel.set_power_mode('normal')
|
||||
|
||||
x_vals = []
|
||||
y_vals = []
|
||||
|
||||
smooth = 3
|
||||
|
||||
while True:
|
||||
# Read MSA301 values
|
||||
x, y, z = accel.get_measurements()
|
||||
|
||||
# z = x-axis, y = y-axis
|
||||
bubble_centre_x = (2 - (z + 1)) * (circle_dia / 2) + border
|
||||
bubble_centre_y = (y + 1) * (circle_dia / 2) + border
|
||||
|
||||
# Work out vector length to check if outside circle
|
||||
delta_x = bubble_centre_x - centre_x
|
||||
delta_y = bubble_centre_y - centre_y
|
||||
|
||||
vector_length = math.sqrt(delta_x ** 2 + delta_y ** 2)
|
||||
|
||||
# If outside circle, scale position back down relatively
|
||||
if vector_length > circle_rad - bubble_rad:
|
||||
scale = (circle_rad - bubble_rad) / vector_length
|
||||
bubble_centre_x = centre_x + delta_x * scale
|
||||
bubble_centre_y = centre_y + delta_y * scale
|
||||
|
||||
|
||||
# Average x and y values to smooth jitter
|
||||
x_vals.append(bubble_centre_x)
|
||||
|
||||
if len(x_vals) > smooth:
|
||||
x_vals = x_vals[1:]
|
||||
|
||||
bubble_centre_x = int(sum(x_vals) / len(x_vals))
|
||||
|
||||
y_vals.append(bubble_centre_y)
|
||||
|
||||
if len(y_vals) > smooth:
|
||||
y_vals = y_vals[1:]
|
||||
|
||||
bubble_centre_y = int(sum(y_vals) / len(y_vals))
|
||||
|
||||
|
||||
# Use red crosshair if bubble is close to centre
|
||||
if (-0.05 < z < 0.05) and (-0.05 < y < 0.05):
|
||||
crosshair = crosshair_red
|
||||
else:
|
||||
crosshair = crosshair_black
|
||||
|
||||
# Construct image
|
||||
image = Image.new('RGBA', (WIDTH, HEIGHT), color=(0, 0, 0, 0))
|
||||
image.paste(level, (0, 0))
|
||||
image.paste(bubble, (int(bubble_centre_x - (bubble_dia / 2)), int(bubble_centre_y - (bubble_dia / 2))), mask=bubble)
|
||||
image.paste(crosshair, (0, 0), mask=crosshair)
|
||||
|
||||
# Display on LCD
|
||||
disp.display(image)
|
||||
Reference in New Issue
Block a user