Last active
March 16, 2022 21:51
-
-
Save sgreg/739d3e515cde781a2308f08f3fc5610d to your computer and use it in GitHub Desktop.
Raspberry Pi powered DTMF Music Box
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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