/ 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