Skip to content

Commit 452d7f2

Browse files
committed
2 parents fb661f9 + 90f5e76 commit 452d7f2

13 files changed

Lines changed: 459 additions & 33 deletions

File tree

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
Continuously scroll randomly generated After Dark style toasters.
3+
Designed for an ItsyBitsy M4 Express and a 1.3" 240x240 TFT
4+
5+
Adafruit invests time and resources providing this open source code.
6+
Please support Adafruit and open source hardware by purchasing
7+
products from Adafruit!
8+
9+
Written by Dave Astels for Adafruit Industries
10+
Copyright (c) 2019 Adafruit Industries
11+
Licensed under the MIT license.
12+
13+
All text above must be included in any redistribution.
14+
15+
Requires CircuitPython 5.0 or later.
16+
"""
17+
18+
import time
19+
from random import seed, randint
20+
import board
21+
import displayio
22+
from adafruit_st7789 import ST7789
23+
import adafruit_imageload
24+
25+
26+
# Sprite cell values
27+
EMPTY = 0
28+
CELL_1 = EMPTY + 1
29+
CELL_2 = CELL_1 + 1
30+
CELL_3 = CELL_2 + 1
31+
CELL_4 = CELL_3 + 1
32+
TOAST = CELL_4 + 1
33+
34+
NUMBER_OF_SPRITES = TOAST + 1
35+
36+
# Animation support
37+
38+
FIRST_CELL = CELL_1
39+
LAST_CELL = CELL_4
40+
41+
NUMBER_OF_CELLS = (LAST_CELL - FIRST_CELL) + 1
42+
43+
# A boolean array corresponding to the sprites, True if it's part of the animation sequence.
44+
ANIMATED = [_sprite >= FIRST_CELL and _sprite <= LAST_CELL for _sprite in range(NUMBER_OF_SPRITES)]
45+
46+
47+
# The chance (out of 10) that a new toaster, or toast will enter
48+
CHANCE_OF_NEW_TOASTER = 5
49+
CHANCE_OF_NEW_TOAST = 2
50+
51+
# How many sprites to styart with
52+
INITIAL_NUMBER_OF_SPRITES= 5
53+
54+
# Global variables
55+
display = None
56+
tilegrid = None
57+
58+
seed(int(time.monotonic()))
59+
60+
def make_display():
61+
"""Set up the display support.
62+
Return the Display object.
63+
"""
64+
spi = board.SPI()
65+
while not spi.try_lock():
66+
pass
67+
spi.configure(baudrate=24000000) # Configure SPI for 24MHz
68+
spi.unlock()
69+
tft_cs = board.D10
70+
tft_dc = board.D7
71+
72+
displayio.release_displays()
73+
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.D9)
74+
75+
return ST7789(display_bus, width=240, height=240, rowstart=80, auto_refresh=True)
76+
77+
def make_tilegrid():
78+
"""Construct and return the tilegrid."""
79+
group = displayio.Group(max_size=10)
80+
81+
sprite_sheet, palette = adafruit_imageload.load("/spritesheet.bmp",
82+
bitmap=displayio.Bitmap,
83+
palette=displayio.Palette)
84+
grid = displayio.TileGrid(sprite_sheet, pixel_shader=palette,
85+
width=9, height=9,
86+
tile_height=32, tile_width=32,
87+
x=0, y=-32,
88+
default_tile=EMPTY)
89+
group.append(grid)
90+
display.show(group)
91+
return grid
92+
93+
def random_cell():
94+
return randint(FIRST_CELL, LAST_CELL)
95+
96+
def evaluate_position(row, col):
97+
"""Return whether how long of aa toaster is placable at the given location.
98+
:param row: the tile row (0-9)
99+
:param col: the tile column (0-9)
100+
"""
101+
return tilegrid[col, row] == EMPTY
102+
103+
def seed_toasters(number_of_toasters):
104+
"""Create the initial toasters so it doesn't start empty"""
105+
for _ in range(number_of_toasters):
106+
while True:
107+
row = randint(0, 8)
108+
col = randint(0, 8)
109+
if evaluate_position(row, col):
110+
break
111+
tilegrid[col, row] = random_cell()
112+
113+
def next_sprite(sprite):
114+
if ANIMATED[sprite]:
115+
return (((sprite - FIRST_CELL) + 1) % NUMBER_OF_CELLS) + FIRST_CELL
116+
return sprite
117+
118+
def advance_animation():
119+
"""Cycle through animation cells each time."""
120+
for tile_number in range(81):
121+
tilegrid[tile_number] = next_sprite(tilegrid[tile_number])
122+
123+
def slide_tiles():
124+
"""Move the tilegrid one pixel to the bottom-left."""
125+
tilegrid.x -= 1
126+
tilegrid.y += 1
127+
128+
def shift_tiles():
129+
"""Move tiles one spot to the left, and reset the tilegrid's position"""
130+
for row in range(8, 0, -1):
131+
for col in range(8):
132+
tilegrid[col, row] = tilegrid[col + 1, row - 1]
133+
tilegrid[8, row] = EMPTY
134+
for col in range(9):
135+
tilegrid[col, 0] = EMPTY
136+
tilegrid.x = 0
137+
tilegrid.y = -32
138+
139+
def get_entry_row():
140+
while True:
141+
row = randint(0, 8)
142+
if tilegrid[8, row] == EMPTY and tilegrid[7, row] == EMPTY:
143+
return row
144+
145+
def get_entry_column():
146+
while True:
147+
col = randint(0, 8)
148+
if tilegrid[col, 0] == EMPTY and tilegrid[col, 1] == EMPTY:
149+
return col
150+
151+
def add_toaster_or_toast():
152+
"""Maybe add a new toaster or toast on the right and/or top at a randon open location"""
153+
if randint(1, 10) <= CHANCE_OF_NEW_TOAST:
154+
tile = TOAST
155+
elif randint(1, 10) <= CHANCE_OF_NEW_TOASTER:
156+
tile = random_cell()
157+
else:
158+
tile = EMPTY
159+
tilegrid[8, get_entry_row()] = tile
160+
161+
if randint(1, 10) <= CHANCE_OF_NEW_TOAST:
162+
tile = TOAST
163+
elif randint(1, 8) <= CHANCE_OF_NEW_TOASTER:
164+
tile = random_cell()
165+
else:
166+
tile = EMPTY
167+
tilegrid[get_entry_column(), 0] = tile
168+
169+
display = make_display()
170+
tilegrid = make_tilegrid()
171+
seed_toasters(INITIAL_NUMBER_OF_SPRITES)
172+
display.refresh()
173+
174+
while True:
175+
for _ in range(32):
176+
display.refresh(target_frames_per_second=80)
177+
advance_animation()
178+
slide_tiles()
179+
shift_tiles()
180+
add_toaster_or_toast()
181+
display.refresh(target_frames_per_second=120)
9.12 KB
Binary file not shown.

Introducing_CircuitPlaygroundExpress/CircuitPlaygroundExpress_AnalogIn.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
# CircuitPlaygroundExpress_AnalogIn
2-
# reads the analog voltage level from a 10k potentiometer
3-
# connected to GND, 3.3V, and pin A1
4-
# and prints the results to the REPL
1+
# Circuit Playground AnalogIn
2+
# Reads the analog voltage level from a 10k potentiometer connected to GND, 3.3V, and pin A1
3+
# and prints the results to the serial console.
54

65
import time
7-
86
import board
9-
from analogio import AnalogIn
7+
import analogio
108

11-
analogin = AnalogIn(board.A1)
9+
analogin = analogio.AnalogIn(board.A1)
1210

1311

1412
def getVoltage(pin): # helper

Introducing_CircuitPlaygroundExpress/CircuitPlaygroundExpress_DigitalIO.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
# CircuitPlaygroundExpress_DigitalIO
1+
# Circuit Playground digitalio example
22

33
import time
4-
54
import board
6-
from digitalio import DigitalInOut, Direction, Pull
5+
import digitalio
76

8-
led = DigitalInOut(board.D13)
9-
led.direction = Direction.OUTPUT
7+
led = digitalio.DigitalInOut(board.D13)
8+
led.switch_to_output()
109

11-
button = DigitalInOut(board.BUTTON_A)
12-
button.direction = Direction.INPUT
13-
button.pull = Pull.DOWN
10+
button = digitalio.DigitalInOut(board.BUTTON_A)
11+
button.switch_to_input(pull=digitalio.Pull.DOWN)
1412

1513
while True:
1614
if button.value: # button is pushed

M4_Eyes/M4_Eyes.ino

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,12 @@ void setup() {
162162
delay(20);
163163
#endif
164164

165-
uint8_t e;
165+
uint8_t e, rtna = 0x01; // Screen refresh rate control (datasheet 9.2.18, FRCTRL2)
166166
// Initialize displays
167167
for(e=0; e<NUM_EYES; e++) {
168168
eye[e].display = new Adafruit_ST7789(eye[e].spi, eye[e].cs, eye[e].dc, eye[e].rst);
169169
eye[e].display->init(240, 240);
170+
eye[e].display->sendCommand(0xC6, &rtna, 1);
170171
eye[e].spi->setClockSource(DISPLAY_CLKSRC);
171172
eye[e].display->fillScreen(0x1234);
172173
eye[e].display->setRotation(0);
@@ -510,24 +511,30 @@ void loop() {
510511
timeToNextBlink = blinkDuration * 3 + random(4000000);
511512
}
512513

513-
// Eyelids naturally "track" the pupils (move up or down automatically)
514-
int ix = (int)map2screen(mapRadius - eye[eyeNum].eyeX) + 120, // Pupil position
515-
iy = (int)map2screen(mapRadius - eye[eyeNum].eyeY) + 120; // on screen
516-
iy += irisRadius / 2; // top edge of iris (ish) in screen pixels
517514
float uq, lq; // So many sloppy temp vars in here for now, sorry
518-
if(eyeNum & 1) ix = 239 - ix; // Flip for right eye
519-
if(iy > upperOpen[ix]) {
520-
uq = 1.0;
521-
} else if(iy < upperClosed[ix]) {
522-
uq = 0.0;
523-
} else {
524-
uq = (float)(iy - upperClosed[ix]) / (float)(upperOpen[ix] - upperClosed[ix]);
525-
}
526-
if(booped) {
527-
uq = 0.9;
528-
lq = 0.7;
515+
if(tracking) {
516+
// Eyelids naturally "track" the pupils (move up or down automatically)
517+
int ix = (int)map2screen(mapRadius - eye[eyeNum].eyeX) + 120, // Pupil position
518+
iy = (int)map2screen(mapRadius - eye[eyeNum].eyeY) + 120; // on screen
519+
iy += irisRadius * trackFactor;
520+
if(eyeNum & 1) ix = 239 - ix; // Flip for right eye
521+
if(iy > upperOpen[ix]) {
522+
uq = 1.0;
523+
} else if(iy < upperClosed[ix]) {
524+
uq = 0.0;
525+
} else {
526+
uq = (float)(iy - upperClosed[ix]) / (float)(upperOpen[ix] - upperClosed[ix]);
527+
}
528+
if(booped) {
529+
uq = 0.9;
530+
lq = 0.7;
531+
} else {
532+
lq = 1.0 - uq;
533+
}
529534
} else {
530-
lq = 1.0 - uq;
535+
// If no tracking, eye is FULLY OPEN when not blinking
536+
uq = 1.0;
537+
lq = 1.0;
531538
}
532539
// Dampen eyelid movements slightly
533540
// SAVE upper & lower lid factors per eye,

M4_Eyes/file.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ void loadConfig(char *filename) {
266266
JsonVariant iristv = doc["irisTexture"],
267267
scleratv = doc["scleraTexture"];
268268

269+
v = doc["tracking"];
270+
if(v.is<bool>()) tracking = v.as<bool>();
271+
v = doc["squint"];
272+
if(v.is<float>()) {
273+
trackFactor = 1.0 - v.as<float>();
274+
if(trackFactor < 0.0) trackFactor = 0.0;
275+
else if(trackFactor > 1.0) trackFactor = 1.0;
276+
}
277+
269278
// Convert clockwise int (0-1023) or float (0.0-1.0) values to CCW int used internally:
270279
v = doc["irisSpin"];
271280
if(v.is<float>()) irisSpin = v.as<float>() * -1024.0;

M4_Eyes/globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ GLOBAL_VAR uint16_t lightSensorMax GLOBAL_INIT(1023);
6969
GLOBAL_VAR float lightSensorCurve GLOBAL_INIT(1.0);
7070
GLOBAL_VAR float irisMin GLOBAL_INIT(0.45);
7171
GLOBAL_VAR float irisRange GLOBAL_INIT(0.35);
72+
GLOBAL_VAR bool tracking GLOBAL_INIT(true);
73+
GLOBAL_VAR float trackFactor GLOBAL_INIT(0.5);
7274

7375
// Pin definition stuff will go here
7476

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import time
2+
import board
3+
import busio
4+
from adafruit_ht16k33 import segments
5+
6+
# Create the I2C interface.
7+
i2c = busio.I2C(board.SCL, board.SDA)
8+
9+
# Create the LED segment class.
10+
# This creates a 14 segment 4 character display:
11+
display = segments.Seg14x4(i2c)
12+
13+
# Clear the display.
14+
display.fill(0)
15+
16+
# set brightness, range 1-15, 15 max brightness
17+
display.brightness = 15
18+
19+
# show phrase on alphanumeric display
20+
message = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
21+
count = 0
22+
23+
# print one character at time with short sleep
24+
# creates smooth scrolling effect
25+
while count < len(message):
26+
display.print(message[count])
27+
count += 1
28+
time.sleep(0.3)
29+
30+
# Can just print a number
31+
display.print(42)
32+
time.sleep(1)
33+
34+
# Set the first character to '1':
35+
display[0] = '1'
36+
# Set the second character to '2':
37+
display[1] = '2'
38+
# Set the third character to 'A':
39+
display[2] = 'A'
40+
# Set the forth character to 'B':
41+
display[3] = 'B'
42+
time.sleep(1)
43+
44+
numbers = [0.0, 1.0, -1.0, 0.55, -0.55, 10.23, -10.2, 100.5, -100.5]
45+
46+
# print negative and positive floating point numbers
47+
for i in numbers:
48+
display.print(i)
49+
time.sleep(0.5)
50+
51+
# print hex values, enable colon
52+
for i in range(0xFF):
53+
display.fill(0)
54+
display.print(':')
55+
display.print(hex(i))
56+
time.sleep(0.25)

0 commit comments

Comments
 (0)