110 lines
3.7 KiB
Python
110 lines
3.7 KiB
Python
# Standalone MicroPython Voice Animation: Kid Companion Robot
|
|
# Exposes run(text="", frames=...) and runs automatically if executed directly.
|
|
import time
|
|
import math
|
|
|
|
try:
|
|
import board_config
|
|
display = getattr(board_config, 'display_instance', None)
|
|
except Exception:
|
|
board_config = None
|
|
display = None
|
|
|
|
def draw_center_text(d, msg, y, w, scale=1, c=1):
|
|
char_w = 8 * scale
|
|
x = max(0, (w - len(msg) * char_w) // 2)
|
|
if hasattr(d, 'text_large') and scale > 1:
|
|
try:
|
|
d.text_large(msg, x, y, scale, c)
|
|
return
|
|
except Exception:
|
|
pass
|
|
d.text(msg, x, y, c)
|
|
|
|
def run(text="", frames=100):
|
|
if not display:
|
|
print("board_config.display_instance is not available. Skipping animation.")
|
|
return
|
|
|
|
width = getattr(display, 'width', 400)
|
|
height = getattr(display, 'height', 300)
|
|
cx, cy = width // 2, height // 2
|
|
|
|
label = text if text else "HELLO BUDDY!"
|
|
frame = 0
|
|
|
|
try:
|
|
while True:
|
|
if frames is not None and frames > 0 and frame >= frames:
|
|
break
|
|
|
|
display.clear(0)
|
|
|
|
# Robot head main box
|
|
head_y = cy - 15
|
|
display.rect(cx - 50, head_y - 40, 100, 80, 1)
|
|
display.rect(cx - 49, head_y - 39, 98, 78, 1) # thicker
|
|
|
|
# Neck
|
|
display.fill_rect(cx - 10, head_y + 40, 20, 12, 1)
|
|
|
|
# Ears wiggling dynamically using sin waves
|
|
ear_offset = int(5 * math.sin(frame * 0.25))
|
|
# Left ear
|
|
display.rect(cx - 62, head_y - 15 + ear_offset, 12, 30, 1)
|
|
display.line(cx - 62, head_y + ear_offset, cx - 50, head_y, 1)
|
|
# Right ear
|
|
display.rect(cx + 50, head_y - 15 - ear_offset, 12, 30, 1)
|
|
display.line(cx + 62, head_y - ear_offset, cx + 50, head_y, 1)
|
|
|
|
# Antenna extending from head top
|
|
display.line(cx, head_y - 40, cx, head_y - 58, 1)
|
|
display.line(cx - 1, head_y - 40, cx - 1, head_y - 58, 1)
|
|
|
|
# Antenna bulb winks (flashes)
|
|
bulb_on = (frame // 4) % 2 == 0
|
|
if bulb_on:
|
|
display.fill_rect(cx - 6, head_y - 69, 12, 12, 1)
|
|
else:
|
|
display.rect(cx - 6, head_y - 69, 12, 12, 1)
|
|
|
|
# Eyes
|
|
eye_y = head_y - 12
|
|
# Left Eye (always open, cute square with highlight)
|
|
display.rect(cx - 32, eye_y - 8, 16, 16, 1)
|
|
display.fill_rect(cx - 28, eye_y - 4, 8, 8, 1)
|
|
display.fill_rect(cx - 26, eye_y - 2, 3, 3, 0) # highlight
|
|
|
|
# Right Eye winks every 12 frames
|
|
is_winking = (frame // 10) % 2 == 1
|
|
if is_winking:
|
|
# Wink line (happy curved line)
|
|
display.line(cx + 12, eye_y, cx + 20, eye_y + 4, 1)
|
|
display.line(cx + 20, eye_y + 4, cx + 28, eye_y, 1)
|
|
else:
|
|
# Open right eye
|
|
display.rect(cx + 16, eye_y - 8, 16, 16, 1)
|
|
display.fill_rect(cx + 20, eye_y - 4, 8, 8, 1)
|
|
display.fill_rect(cx + 22, eye_y - 2, 3, 3, 0) # highlight
|
|
|
|
# Cute happy smile mouth
|
|
display.line(cx - 16, head_y + 16, cx + 16, head_y + 16, 1)
|
|
display.line(cx - 16, head_y + 16, cx - 20, head_y + 10, 1)
|
|
display.line(cx + 16, head_y + 16, cx + 20, head_y + 10, 1)
|
|
|
|
# Draw centered text label at the bottom
|
|
draw_center_text(display, label, height - 30, width, scale=1, c=1)
|
|
|
|
display.show()
|
|
frame += 1
|
|
|
|
try:
|
|
time.sleep_ms(60)
|
|
except AttributeError:
|
|
time.sleep(0.06)
|
|
except KeyboardInterrupt:
|
|
print("Animation interrupted by user.")
|
|
|
|
if __name__ in ('__main__', '__name__'):
|
|
run()
|