Skip to content

Instantly share code, notes, and snippets.

@sgreg
Last active March 16, 2022 21:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sgreg/739d3e515cde781a2308f08f3fc5610d to your computer and use it in GitHub Desktop.
Save sgreg/739d3e515cde781a2308f08f3fc5610d to your computer and use it in GitHub Desktop.
Raspberry Pi powered DTMF Music Box
# Ph0n0r.py
# Copyright (C) 2017 Sven Gregori <sven@craplab.fi>
#
# Released under MIT License
# Don't hold this code against me
#
# Raspberry Pi powered DTMF Music Box
# Playing either "Mary had a little lamb" or "If you're happy and you know it
# clap your hands" on every incoming call.
# SIM800L GSM Modem connected through UART on /dev/ttyS0
# Optional RGB status LED connected to GPIO pins 23, 24 and 25
#
# This uses some power switching to power the GSM modem on and off, but it
# doesn't really work, even with some 1F supercap ..gotta learn some more
# theory here I guess.
#
import RPi.GPIO as gpio
import time
import serial
import threading
DEBUG = False
serport_run = True
serport_name = '/dev/ttyS0'
serport_baud = 9600;
pin = '1234'
gpio_r = 23
gpio_g = 24
gpio_b = 25
gpio_sim_power = 12
gpio.setmode(gpio.BCM)
gpio.setup(gpio_r, gpio.OUT)
gpio.setup(gpio_g, gpio.OUT)
gpio.setup(gpio_b, gpio.OUT)
gpio.setup(gpio_sim_power, gpio.OUT)
gpio.output(gpio_r, 0)
gpio.output(gpio_g, 0)
gpio.output(gpio_b, 0)
gpio.output(gpio_sim_power, 1)
ser = serial.Serial(serport_name, baudrate=serport_baud, timeout=1)
ST_RST = 0
ST_PIN = 1
ST_RDY = 2
ST_CALL_IN = 3
ST_CALL_RDY = 4
ST_ON_CALL = 5
ST_CALL_END = 6
state = ST_RDY
current_call = ""
song_idx = 0;
song_mary = [
["3", 3],
["2", 1],
["1", 2],
["2", 2],
["3", 2],
["3", 2],
["3", 4],
["2", 2],
["2", 2],
["2", 4],
["3", 2],
["9", 2],
["9", 4],
["3", 3],
["2", 1],
["1", 2],
["2", 2],
["3", 2],
["3", 2],
["3", 2],
["3", 2],
["2", 2],
["2", 2],
["3", 2],
["2", 2],
["1", 8],
]
song_happyclap = [
["1", 2],
["1", 1],
["3", 2],
["3", 1],
["3", 2],
["3", 1],
["3", 2],
["3", 1],
["2", 2],
["3", 1],
["6", 3],
[" ", 18],
["1", 2],
["1", 1],
["6", 2],
["6", 1],
["6", 2],
["6", 1],
["6", 2],
["6", 1],
["3", 2],
["6", 1],
["9", 3],
[" ", 18],
["9", 2],
["9", 1],
["#", 2],
["#", 1],
["#", 2],
["#", 1],
["1", 2],
["1", 1],
["#", 2],
["#", 1],
["6", 2],
["6", 1],
["6", 2],
["3", 1],
["2", 2],
["2", 1],
["6", 2],
["6", 1],
["3", 2],
["3", 1],
["3", 2],
["3", 1],
["2", 2],
["2", 1],
["1", 2],
["3", 1],
["2", 3],
]
songlist = [
song_mary,
song_happyclap,
]
songlist_idx = 0;
song = None
def set_state(s):
global state
global song
global song_idx
global songlist_idx
if s == ST_RST:
gpio.output(gpio_r, 0)
gpio.output(gpio_g, 0)
gpio.output(gpio_b, 0)
elif s == ST_PIN:
gpio.output(gpio_b, 1)
elif s == ST_RDY:
gpio.output(gpio_r, 1)
elif s == ST_CALL_IN:
print("incoming call")
elif s == ST_CALL_RDY:
pass
elif s == ST_ON_CALL:
print("On call with %s" % (current_call, ))
song = songlist[songlist_idx]
song_idx = 0
elif s == ST_CALL_END:
print("call ended with %s" % (current_call, ))
songlist_idx = songlist_idx + 1
if songlist_idx == len(songlist):
songlist_idx = 0
else:
print("error, unknown state %d" % (s, ))
return
state = s
def handle_data(data):
global state
global current_call
if data[0:2] == 'AT':
return
data = data.strip()
if data == '':
return
if DEBUG:
print("got data: '%s'" % (data,))
gpio.output(gpio_g, 0)
if data == "+CPIN: SIM PIN":
if state != ST_RST:
set_state(ST_RST)
print("-> sending pin")
ser.write("AT+CPIN=%s\r\n" % (pin,))
set_state(ST_PIN)
elif data == 'SMS Ready':
if state != ST_PIN:
print("error, wrong state %d, should be %d" % (state, ST_PIN))
set_state(ST_RDY)
elif data == 'RING':
if state == ST_RDY:
set_state(ST_CALL_IN);
elif state == ST_CALL_IN:
# waiting for +CLIP
# XXX you need "AT+CLIP=1" set, or this won't work
pass
elif state == ST_CALL_RDY:
ser.write("ATA\r\n")
set_state(ST_ON_CALL)
else:
print("state %d during CALL_IN, might be strange.." % (state, ))
elif data[0:6] == "+CLIP:":
if state == ST_CALL_IN:
current_call = data[7:]
set_state(ST_CALL_RDY)
elif data == "NO CARRIER":
set_state(ST_CALL_END)
set_state(ST_RDY)
elif data == "OK" and state == ST_ON_CALL:
time.sleep(0.05)
play_song(ser)
def play_song(ser):
global song_idx
if song_idx == 0:
time.sleep(0.5)
if song_idx == len(song):
time.sleep(0.5)
ser.write("ATH\r\n")
set_state(ST_CALL_END)
set_state(ST_RDY)
return
note = song[song_idx][0]
length = song[song_idx][1]
if note == ' ':
if DEBUG:
print("play song idx %d: wait %f\r\n" % (song_idx, length))
time.sleep(length / 10.0)
ser.write("AT\r\n") # gotta trigger the next "OK"
else:
if DEBUG:
print("play song idx %d: note %s for %d time\r\n" % (song_idx, note, length))
ser.write("AT+CLDTMF=%d,\"%s\"\r\n" % (length, note))
song_idx = song_idx + 1
def read_serial_port(ser):
while serport_run:
r = ser.readline()
if r != '':
handle_data(r)
thread = threading.Thread(target=read_serial_port, args=(ser,))
thread.start()
try:
while True:
req = raw_input('> ')
if req == '':
continue
elif req == 'q':
break
else:
gpio.output(gpio_g, 1)
ser.write(req + '\r\n')
except KeyboardInterrupt:
pass
except EOFError:
pass
serport_run = False
gpio.output(gpio_sim_power, 0)
print "Bye."
gpio.cleanup()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment