176 lines
5.7 KiB
Python
176 lines
5.7 KiB
Python
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import os
|
|
import time
|
|
import board
|
|
import audiocore
|
|
import audiobusio
|
|
import audiomixer
|
|
import adafruit_ds3231
|
|
from digitalio import DigitalInOut, Direction
|
|
import neopixel
|
|
import keypad
|
|
|
|
# enable external power pin
|
|
# provides power to the external components
|
|
external_power = DigitalInOut(board.EXTERNAL_POWER)
|
|
external_power.direction = Direction.OUTPUT
|
|
external_power.value = True
|
|
|
|
# setup external button pin as a keypad object
|
|
key = keypad.Keys((board.EXTERNAL_BUTTON,), value_when_pressed=False, pull=True)
|
|
|
|
# neopixel mouth setup
|
|
pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, 8, auto_write=True, brightness=0.2)
|
|
blue = (0, 0, 255)
|
|
off = (0, 0, 0)
|
|
pixels.fill(off)
|
|
|
|
# rtc module
|
|
i2c = board.I2C()
|
|
rtc = adafruit_ds3231.DS3231(i2c)
|
|
|
|
# pylint: disable-msg=using-constant-test
|
|
if False: # change to True if you want to set the time!
|
|
# year, mon, date, hour, min, sec, wday, yday, isdst
|
|
t = time.struct_time((2023, 8, 22, 16, 47, 00, 1, -1, -1))
|
|
# you must set year, mon, date, hour, min, sec and weekday
|
|
# yearday is not supported, isdst can be set but we don't do anything with it at this time
|
|
print("Setting time to:", t) # uncomment for debugging
|
|
rtc.datetime = t
|
|
print()
|
|
# pylint: enable-msg=using-constant-test
|
|
|
|
# sound arrays
|
|
bookend_sounds = []
|
|
hour_sounds = []
|
|
min_sounds = []
|
|
clock_queue = []
|
|
# bring in the audio files and put them into the different arrays
|
|
for filename in os.listdir('/clock_sounds'):
|
|
if filename.lower().endswith('.wav') and not filename.startswith('.'):
|
|
if filename.startswith('h'):
|
|
hour_sounds.append("/clock_sounds/"+filename)
|
|
elif filename.startswith('m'):
|
|
min_sounds.append("/clock_sounds/"+filename)
|
|
else:
|
|
bookend_sounds.append("/clock_sounds/"+filename)
|
|
# sort the arrays alphabetically
|
|
bookend_sounds.sort()
|
|
hour_sounds.sort()
|
|
min_sounds.sort()
|
|
print(hour_sounds)
|
|
print(min_sounds)
|
|
print(bookend_sounds)
|
|
|
|
# i2s amp setup with mixer
|
|
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
|
|
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
|
|
bits_per_sample=16, samples_signed=True)
|
|
audio.play(mixer)
|
|
mixer.voice[0].level = 0.5
|
|
|
|
# function for queueing wave files
|
|
def open_audio(wavs, num):
|
|
n = wavs[num]
|
|
f = open(n, "rb")
|
|
w = audiocore.WaveFile(f)
|
|
clock_queue.append(w)
|
|
return w
|
|
|
|
# queue boot up sound to play before going into the loop
|
|
boot_sound = open_audio(bookend_sounds, 2)
|
|
|
|
# function for gathering clock audio files
|
|
def prep_clock(hour_num, minute_num):
|
|
# queues "the time is.."
|
|
open_audio(bookend_sounds, 1)
|
|
# convert 24 hour time from RTC to 12 hour
|
|
if hour_num >= 12:
|
|
# convert 24 hour to 12 hour
|
|
if hour_num > 12:
|
|
hour_num = hour_num - 12
|
|
# set to PM
|
|
ampm_num = 3
|
|
# otherwise its the morning
|
|
else:
|
|
# set AM
|
|
ampm_num = 0
|
|
# queueing hour, single digit waves have a 0 before the number
|
|
if hour_num <= 9:
|
|
print(hour_num)
|
|
h = hour_sounds.index(f'/clock_sounds/h0{hour_num}.wav')
|
|
else:
|
|
print(hour_num)
|
|
h = hour_sounds.index(f'/clock_sounds/h{hour_num}.wav')
|
|
# open wave file for the hour
|
|
open_audio(hour_sounds, h)
|
|
# if the minute is divisible by 10 (10, 20, 30, etc)
|
|
if minute_num % 10 == 0:
|
|
# check for on the hour (1:00, 2:00, etc)
|
|
if minute_num == 0:
|
|
m = min_sounds.index(f'/clock_sounds/m{minute_num}0.wav')
|
|
else:
|
|
m = min_sounds.index(f'/clock_sounds/m{minute_num}.wav')
|
|
print(minute_num)
|
|
# individual wave files exist for 1-19
|
|
elif minute_num <= 19:
|
|
print(minute_num)
|
|
# if it's a single digit number, bring in the "oh" wave file
|
|
if minute_num <= 9:
|
|
mm = min_sounds.index('/clock_sounds/m0x.wav')
|
|
open_audio(min_sounds, mm)
|
|
m = min_sounds.index(f'/clock_sounds/m{minute_num}.wav')
|
|
# otherwise, for minutes 21-59 that are not divisible by 10
|
|
else:
|
|
# we need to seperate the minutes digits
|
|
digits = list(map(int, str(minute_num)))
|
|
#print(digits)
|
|
# get the tens spot (30, 40, etc)
|
|
mm = min_sounds.index(f'/clock_sounds/m{digits[0]}x.wav')
|
|
# add the wav to the array
|
|
open_audio(min_sounds, mm)
|
|
# get the ones spot (1, 2, etc)
|
|
m = min_sounds.index(f'/clock_sounds/m{digits[1]}.wav')
|
|
# combined it will say ex: "30...5"
|
|
# add the wav to the array
|
|
open_audio(min_sounds, m)
|
|
# finish with am or pm
|
|
open_audio(bookend_sounds, ampm_num)
|
|
# initial RTC read
|
|
t = rtc.datetime
|
|
print(t)
|
|
# say the time on boot
|
|
pixels.fill(blue)
|
|
prep_clock(t.tm_hour, t.tm_min)
|
|
for i in range(len(clock_queue)):
|
|
mixer.voice[0].play(clock_queue[i], loop=False)
|
|
while mixer.playing:
|
|
pass
|
|
# clear the audio file queue
|
|
clock_queue.clear()
|
|
pixels.fill(off)
|
|
|
|
while True:
|
|
# listen for button input
|
|
event = key.events.get()
|
|
if event:
|
|
# if the button is pressed
|
|
if event.pressed:
|
|
# read from RTC module
|
|
t = rtc.datetime
|
|
# turn on neopixels
|
|
pixels.fill(blue)
|
|
# gather the audio files based on RTC reading
|
|
prep_clock(t.tm_hour, t.tm_min)
|
|
# play through each wave one by one
|
|
for i in range(len(clock_queue)):
|
|
mixer.voice[0].play(clock_queue[i], loop=False)
|
|
while mixer.playing:
|
|
pass
|
|
# afterwards clear out the queue
|
|
clock_queue.clear()
|
|
# turn off neopixels
|
|
pixels.fill(off)
|