/ Dial-a-Song / code.py
code.py
1 # SPDX-FileCopyrightText: 2022 John Park for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 # 5 # DTMF keypad phone Dial-a-Song 6 import time 7 import random 8 import board 9 import keypad 10 from audiocore import WaveFile 11 from audiopwmio import PWMAudioOut as AudioOut # for RP2040 etc 12 import audiomixer 13 14 # time.sleep(3) # let USB settle during development, remove when on battery 15 16 km = keypad.KeyMatrix( 17 # 2500 phone ignoring first column store/redial/memory. reverse mount on Feather RP2040 18 column_pins=( board.A3, board.A2, board.A1,), 19 row_pins=( 20 board.D24, 21 board.D25, 22 board.SCK, 23 board.MOSI, 24 ), 25 ) 26 27 numbers = { 28 "8675309" : "songs/beepbox.wav", 29 "6358393" : "songs/streetchicken.wav", 30 "5551212" : "songs/carpeter.wav", 31 "7654321" : "songs/daisy.wav" 32 } 33 34 ringing = "songs/full_ring.wav" 35 wrong_number = "songs/blank_number.wav" 36 dial_tone = "songs/dial_tone_loop.wav" 37 busy_signal = "songs/busy_loop.wav" 38 39 button_tones = [ 40 "dtmf/tt_1.wav", "dtmf/tt_2.wav", "dtmf/tt_3.wav", 41 "dtmf/tt_4.wav", "dtmf/tt_5.wav", "dtmf/tt_6.wav", 42 "dtmf/tt_7.wav", "dtmf/tt_8.wav", "dtmf/tt_9.wav", 43 "dtmf/tt_star.wav", "dtmf/tt_0.wav", "dtmf/tt_pound.wav" 44 ] 45 46 digits_entered = 0 # counter 47 dialed = [] # list of digits user enters to make one 7 digit number 48 dialed_str = "" # stores the phone number string for dictionary comparison 49 50 audio = AudioOut(board.TX) # PWM out pin 51 mixer = audiomixer.Mixer( 52 voice_count=4, 53 sample_rate=22050, 54 channel_count=1, 55 bits_per_sample=16, 56 samples_signed=True, 57 ) 58 audio.play(mixer) 59 mixer.voice[0].level = 1.0 # dial tone voice 60 mixer.voice[1].level = 1.0 # touch tone voice 61 mixer.voice[2].level = 0.0 # song/message voice 62 mixer.voice[3].level = 0.0 # busy signal 63 64 wave_file0 = open(dial_tone, "rb") 65 wave0 = WaveFile(wave_file0) 66 mixer.voice[0].play(wave0, loop=True) # play dial tone 67 68 wave_file2 = open(wrong_number, "rb") 69 wave2 = WaveFile(wave_file2) 70 71 wave_file3 = open(busy_signal, "rb") 72 wave3 = WaveFile(wave_file3) 73 mixer.voice[3].play(wave3, loop=True) # play dial tone 74 75 76 def reset_number(): 77 # pylint: disable=global-statement 78 global digits_entered, dialed, dialed_str 79 digits_entered = 0 80 dialed = [] 81 dialed_str = "" 82 km.events.clear() 83 84 85 while True: 86 87 event = km.events.get() # check for keypad presses 88 if event: 89 if event.pressed: 90 mixer.voice[0].level = 0.0 # mute the dial tone 91 wave_file1 = open(button_tones[event.key_number], "rb") # play Touch Tone 92 wave1 = WaveFile(wave_file1) 93 mixer.voice[1].play(wave1) 94 if event.key_number == 9 or event.key_number == 11: # check for special keys 95 if event.key_number == 9: # pressed the '*' key 96 reset_number() # or make some cool new function for this key 97 if event.key_number == 11: # pressed the '#' key 98 reset_number() # or make some cool new function for this key 99 100 else: # number keys 101 if digits_entered < 7: # adding up to full number 102 # convert event to number printed on the keypad button, append to string 103 if event.key_number < 9: # 1-9 on keypad 104 dialed.append(event.key_number+1) 105 if event.key_number == 10: # the 0 key, ignore '*' and "#' 106 dialed.append(0) 107 dialed_str = "".join(str(n) for n in dialed) 108 digits_entered = digits_entered + 1 # increment counter 109 110 if digits_entered == 7: # a full number has been entered 111 if not mixer.voice[2].playing: 112 dialed_str = "".join(str(n) for n in dialed) 113 if dialed_str in numbers: # check if dialed string is one in the directory 114 value = numbers[dialed_str] 115 time.sleep(0.6) 116 117 wave_file2 = open(ringing, "rb") # ring before it answers 118 wave2 = WaveFile(wave_file2) 119 mixer.voice[2].level = 1.0 120 mixer.voice[2].play(wave2, loop=True) 121 122 time.sleep(random.uniform(4.0, 9.5)) # random ring before "answer" 123 124 wave_file2 = open(value, "rb") # answered 125 wave2 = WaveFile(wave_file2) 126 mixer.voice[2].level = 1.0 127 mixer.voice[2].play(wave2, loop=True) 128 129 else: # number is not in directory 130 time.sleep(0.5) 131 weighted_coin_toss = random.randint(0, 4) 132 if weighted_coin_toss < 3: # favor the "not in service" message 133 mixer.voice[2].level = 1.0 134 mixer.voice[2].play(wave2) 135 else: 136 mixer.voice[3].level = 1.0 137 138 reset_number() 139 140 if mixer.voice[2].playing: 141 reset_number() # stop #s dialed during message play from doing anything