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.

This guide was first published on Feb 25, 2022. It was last updated on Feb 25, 2022.

This page (Code the Dial-a-Song) was last updated on May 31, 2023.

Text editor powered by tinymce.