Text Editor
Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.
Alternatively, you can use any text editor that saves simple text files.
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, folders of .wav file assets, and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your Feather board's CIRCUITPYÂ drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2022 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
#
# DTMF keypad phone Dial-a-Song
import time
import random
import board
import keypad
from audiocore import WaveFile
from audiopwmio import PWMAudioOut as AudioOut # for RP2040 etc
import audiomixer
# time.sleep(3) # let USB settle during development, remove when on battery
km = keypad.KeyMatrix(
# 2500 phone ignoring first column store/redial/memory. reverse mount on Feather RP2040
column_pins=( board.A3, board.A2, board.A1,),
row_pins=(
board.D24,
board.D25,
board.SCK,
board.MOSI,
),
)
numbers = {
"8675309" : "songs/beepbox.wav",
"6358393" : "songs/streetchicken.wav",
"5551212" : "songs/carpeter.wav",
"7654321" : "songs/daisy.wav"
}
ringing = "songs/full_ring.wav"
wrong_number = "songs/blank_number.wav"
dial_tone = "songs/dial_tone_loop.wav"
busy_signal = "songs/busy_loop.wav"
button_tones = [
"dtmf/tt_1.wav", "dtmf/tt_2.wav", "dtmf/tt_3.wav",
"dtmf/tt_4.wav", "dtmf/tt_5.wav", "dtmf/tt_6.wav",
"dtmf/tt_7.wav", "dtmf/tt_8.wav", "dtmf/tt_9.wav",
"dtmf/tt_star.wav", "dtmf/tt_0.wav", "dtmf/tt_pound.wav"
]
digits_entered = 0 # counter
dialed = [] # list of digits user enters to make one 7 digit number
dialed_str = "" # stores the phone number string for dictionary comparison
audio = AudioOut(board.TX) # PWM out pin
mixer = audiomixer.Mixer(
voice_count=4,
sample_rate=22050,
channel_count=1,
bits_per_sample=16,
samples_signed=True,
)
audio.play(mixer)
mixer.voice[0].level = 1.0 # dial tone voice
mixer.voice[1].level = 1.0 # touch tone voice
mixer.voice[2].level = 0.0 # song/message voice
mixer.voice[3].level = 0.0 # busy signal
wave_file0 = open(dial_tone, "rb")
wave0 = WaveFile(wave_file0)
mixer.voice[0].play(wave0, loop=True) # play dial tone
wave_file2 = open(wrong_number, "rb")
wave2 = WaveFile(wave_file2)
wave_file3 = open(busy_signal, "rb")
wave3 = WaveFile(wave_file3)
mixer.voice[3].play(wave3, loop=True) # play dial tone
def reset_number():
# pylint: disable=global-statement
global digits_entered, dialed, dialed_str
digits_entered = 0
dialed = []
dialed_str = ""
km.events.clear()
while True:
event = km.events.get() # check for keypad presses
if event:
if event.pressed:
mixer.voice[0].level = 0.0 # mute the dial tone
wave_file1 = open(button_tones[event.key_number], "rb") # play Touch Tone
wave1 = WaveFile(wave_file1)
mixer.voice[1].play(wave1)
if event.key_number == 9 or event.key_number == 11: # check for special keys
if event.key_number == 9: # pressed the '*' key
reset_number() # or make some cool new function for this key
if event.key_number == 11: # pressed the '#' key
reset_number() # or make some cool new function for this key
else: # number keys
if digits_entered < 7: # adding up to full number
# convert event to number printed on the keypad button, append to string
if event.key_number < 9: # 1-9 on keypad
dialed.append(event.key_number+1)
if event.key_number == 10: # the 0 key, ignore '*' and "#'
dialed.append(0)
dialed_str = "".join(str(n) for n in dialed)
digits_entered = digits_entered + 1 # increment counter
if digits_entered == 7: # a full number has been entered
if not mixer.voice[2].playing:
dialed_str = "".join(str(n) for n in dialed)
if dialed_str in numbers: # check if dialed string is one in the directory
value = numbers[dialed_str]
time.sleep(0.6)
wave_file2 = open(ringing, "rb") # ring before it answers
wave2 = WaveFile(wave_file2)
mixer.voice[2].level = 1.0
mixer.voice[2].play(wave2, loop=True)
time.sleep(random.uniform(4.0, 9.5)) # random ring before "answer"
wave_file2 = open(value, "rb") # answered
wave2 = WaveFile(wave_file2)
mixer.voice[2].level = 1.0
mixer.voice[2].play(wave2, loop=True)
else: # number is not in directory
time.sleep(0.5)
weighted_coin_toss = random.randint(0, 4)
if weighted_coin_toss < 3: # favor the "not in service" message
mixer.voice[2].level = 1.0
mixer.voice[2].play(wave2)
else:
mixer.voice[3].level = 1.0
reset_number()
if mixer.voice[2].playing:
reset_number() # stop #s dialed during message play from doing anything
Custom Phone Numbers
Create your own directory of valid numbers and their associated .wav files in the numbers dictionary:
numbers = {
"8675309" : "songs/beepbox.wav",
"6358393" : "songs/streetchicken.wav",
"5551212" : "songs/carpeter.wav",
"7654321" : "songs/daisy.wav"
}
Custom Songs and Messages
To create your own song and message files (great for granting/denying entrance to your secret speakeasy) convert your audio files to 16-bit mono WAV files at 22KHz sample rate. This guide shows how.
Page last edited January 22, 2025
Text editor powered by tinymce.