User Tools

Site Tools


squawk

This is an old revision of the document!


Audio Alerts

Traditionally this was handled by doorpi. However, that pi has too much stuff hanging off it, and was very out of date. There was an issue of a loud pop occurring before any sound played, this was fixed in later firmware updates. Rob has now dedicated a pi (original B) for the purpose and documents it thus:

Raspbian

Latest Raspbian minimal image from the Raspberry Pi website.

raspi-config

  • Medium overclock
  • Graphics mem reduced to 16M
  • Audio output forced to 3.5mm
  • Hostname set

RAM /tmp

sudo systemctl enable tmp.mount

Packages

sudo apt-get install mpg123
sudo apt-get install sox
sudo apt-get install python-pip
sudo pip install paho-mqtt

/etc/rc.local

These lines added before exit:

su -c "mpg123 /home/pi/sounds/indyboot.mp3" pi &
su -c "/home/pi/respawn --syslog /home/pi/squawk.py" pi &

Scripts

Various scripts to make things happen.

/home/pi/squawk.py

#!/usr/bin/env python
 
import paho.mqtt.client as mqtt
import subprocess
import os
import logging
import signal
import time
 
 
last_state = None
 
max_playtime  = 15
 
sounds_path = "/home/pi/sounds"
 
# runs a command and terminates it after a specified timeout
def call_with_timeout(command, timeout):
    logging.info('call_with_timeout(%r, %r)' % (command, timeout))
    class TimeoutException(Exception):
        pass
    def alrm_handler(signum, frame):
        raise TimeoutException()
    try:
        old_handler = signal.signal(signal.SIGALRM, alrm_handler)
        signal.alarm(timeout)
        p = subprocess.Popen(command)
        retcode = p.wait()
        logging.info('call_with_timeout: command exited with code %s' % (retcode))
    except TimeoutException:
        logging.info('call_with_timeout: command exceeded timeout, terminating...')
        p.terminate()
        retcode = p.wait()
    finally:
        signal.signal(signal.SIGALRM, old_handler)
    signal.alarm(0)
    return retcode
 
# waffle waffle
def speak(data, timeout=max_playtime):
    command = ['/home/pi/pico.sh', data]
    call_with_timeout(command, timeout=timeout)
 
def getfiles(path, exts=[".mp3", ".wav"]):
    allfiles = []
    for dirpath, dirnames, filenames in os.walk(path):
        for filename in filenames:
            base, ext = os.path.splitext(filename)
            if ext in exts:
                #allfiles.append(os.path.relpath(os.path.join(dirpath, filename), path))
                allfiles.append(os.path.join(dirpath, filename))
    return allfiles
 
# make some noise for the vengaboys
def play(filename, timeout=max_playtime):
    allfiles = getfiles(sounds_path)
    filename = os.path.join(sounds_path, filename)
 
    if filename.endswith('/'):
        # pick a random file from a directory
        candidates = []
        for f in allfiles:
            if f.startswith(filename):
                candidates.append(f)
        if len(candidates) == 0:
            raise Exception('No files matching %s' % (filename))
        filename = random.choice(candidates)
    else:
        # single file requested
        if filename not in allfiles:
            raise Exception('File %s not found' % (filename))
 
    base, ext = os.path.splitext(filename)
    if ext == '.mp3':
        command = ['mpg123', ' -q', filename]
        call_with_timeout(command, timeout=timeout)
    else:
        command = ['play', ' -q', filename]
        call_with_timeout(command, timeout=timeout)
 
def on_connect(client, userdata, flags, rc):
    client.subscribe("sound/g1/play")
    client.subscribe("sound/g1/speak")
 
def on_message(client, userdata, msg):
 
    # ignore retained (non-realtime) messages
    if msg.retain:
        return
 
    if msg.topic == 'sound/g1/play':
	play(msg.payload)
 
    if msg.topic == 'sound/g1/speak':
	play('dong.mp3')
        speak(msg.payload)
 
 
m = mqtt.Client()
m.on_connect = on_connect
m.on_message = on_message
m.connect("mqtt")
m.loop_forever()

/home/pi/respawn

#!/usr/bin/env python
#
# Tim Hawes <me@timhawes.com>
# April 2015
#
 
import argparse
import logging
import logging.handlers
import os
import signal
import subprocess
import sys
import time
 
process = None
hup_received = False
term_received = False
 
parser = argparse.ArgumentParser(description='Respawn an application.')
parser.add_argument('--name', type=str, dest='name', action='store')
parser.add_argument('--delay', type=int, dest='delay', action='store', default=1)
parser.add_argument('--min-backoff', type=int, dest='min_backoff', action='store', default=1)
parser.add_argument('--max-backoff', type=int, dest='max_backoff', action='store', default=60)
parser.add_argument('--reset-backoff-after', type=int, dest='backoff_reset_after', action='store', default=30)
parser.add_argument('--syslog', dest='syslog', action='store_true', default=False)
parser.add_argument('--debug', dest='debug', action='store_true', default=False)
parser.add_argument('command', nargs='*')
args = parser.parse_args()
 
def setup_logging(log_level=logging.INFO, syslog=True, stdout=False, ident=os.path.basename(sys.argv[0])):
    logger = logging.getLogger()
    logger.setLevel(log_level)
    if syslog:
        syslog_format_string = ident + "[%(process)d]: %(message)s"
        syslog_handler = logging.handlers.SysLogHandler(address="/dev/log", facility=logging.handlers.SysLogHandler.LOG_USER)
        syslog_handler.log_format_string = "<%d>%s"
        syslog_handler.setFormatter(logging.Formatter(fmt=syslog_format_string))
        syslog_handler.setLevel(log_level)
        logger.addHandler(syslog_handler)
    if stdout:
        stream_format_string = "%(asctime)s %(message)s"
        stream_handler = logging.StreamHandler(stream=sys.__stdout__)
        stream_handler.setFormatter(logging.Formatter(fmt=stream_format_string))
        stream_handler.setLevel(log_level)
        logger.addHandler(stream_handler)
 
def run():
    global process
 
    start_time = time.time()
    process = subprocess.Popen(args.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while True:
        line = process.stdout.readline()
        if line == '':
            break
        logging.info("< " + line.rstrip())
    returncode = process.wait()
    runtime = time.time()-start_time
    process = None
    if returncode == 0:
        logging.info('exit=%d runtime=%.3f' % (returncode, runtime))
    else:
        logging.warning('exit=%d runtime=%.3f' % (returncode, runtime))
    return returncode, runtime
 
def hup_handler(signum, frame):
    global process
    global hup_received
 
    if process is not None:
        logging.warning("received SIGHUP, sending SIGTERM to process")
        hup_received = True
        process.send_signal(signal.SIGTERM)
    else:
        logging.warning("received SIGHUP, but no process running")
 
def term_handler(signum, frame):
    global process
    global term_received
 
    if process is not None:
        logging.warning("received SIGTERM, sending SIGTERM to process")
        term_received = True
        process.send_signal(signal.SIGTERM)
    else:
        logging.warning("received SIGTERM, but no process running")
        term_received = True
 
signal.signal(signal.SIGHUP, hup_handler)
signal.signal(signal.SIGTERM, term_handler)
 
ident = os.path.basename(sys.argv[0])
if args.name is not None:
    ident = ident + "/" + args.name
 
if args.debug:
    setup_logging(log_level=logging.DEBUG, stdout=True, syslog=False, ident=ident)
else:
    setup_logging(log_level=logging.INFO, stdout=False, syslog=True, ident=ident)
 
if args.delay > args.min_backoff:
    logging.debug('increasing min-backoff to match delay (%d)' % (args.delay))
    args.min_backoff = args.delay
if args.min_backoff > args.max_backoff:
    logging.debug('increasing max-backoff to match min-backoff (%d)' % (args.min_backoff))
    args.max_backoff = args.min_backoff
 
exit_requested = False
backoff = args.min_backoff
 
logging.info("command: %r" % (args.command))
 
while True:
    start_time = time.time()
    returncode, runtime = run()
    if term_received:
        logging.debug("exited after SIGTERM")
        break
    if hup_received:
        logging.debug("exited after SIGHUP, restarting immediately")
        hup_received = False
        continue
    if returncode == 0:
        if runtime > args.backoff_reset_after:
            backoff = args.min_backoff
            logging.debug('resetting backoff to %d' % (backoff))
        else:
            logging.debug('delaying for %d after a successful run' % (args.delay))
            time.sleep(args.delay)
    else:
        logging.info('backing-off for %d seconds' % (backoff))
        time.sleep(backoff)
        backoff = min(backoff*2, args.max_backoff)
        logging.debug('next backoff will be %d seconds' % (backoff))
 
logging.info('exiting respawn')

/home/pi/pico.sh

#!/bin/bash
pico2wave -l en-GB -w /var/tmp/pico.wav "$1"
play -q /tmp/pico.wav
rm -f /tmp/pico.wav

Audio files

These live in /home/pi/sounds and can be wav or mp3. SCP new ones into here.

pi@squawk:~ $ ls -l /home/pi/sounds/
total 504
-rw-r--r-- 1 pi pi   9249 Oct  4 22:32 alert12.mp3
-rw-r--r-- 1 pi pi   8594 Oct  4 22:32 bingo.mp3
-rw-r--r-- 1 pi pi  59350 Oct  4 22:32 canttouchthis.mp3
-rw-r--r-- 1 pi pi  11703 Oct  4 22:32 cheese.mp3
-rw-r--r-- 1 pi pi  71457 Oct  4 22:32 commandcodesverified_ep.mp3
-rw-r--r-- 1 pi pi   7713 Oct  4 22:32 computerbeep_4.mp3
-rw-r--r-- 1 pi pi  21759 Oct  5 02:24 dong.mp3
-rw-r--r-- 1 pi pi 118725 Oct  5 01:48 doorbell.mp3
-rw-r--r-- 1 pi pi  44329 Oct  5 02:07 indyboot.mp3
-rw-r--r-- 1 pi pi  90825 Oct  4 22:32 scatman.mp3
-rw-r--r-- 1 pi pi   5486 Oct  4 22:32 touchdown.mp3
-rw-r--r-- 1 pi pi   6414 Oct  4 22:32 uhoh.mp3
-rw-r--r-- 1 pi pi  27584 Oct  4 22:32 whistle.mp3

Pico TTS

mkdir ~/pico
cd ~/pico
wget  http://incrediblepbx.com/picotts-raspi.tar.gz
tar -zxf picotts-raspi.tar.gz
sudo cp -R usr /
cd /usr/src/pico_build
sudo dpkg -i libttspico-data_1.0+git20110131-2_all.deb
sudo dpkg -i libttspico0_1.0+git20110131-2_armhf.deb
sudo dpkg -i libttspico-utils_1.0+git20110131-2_armhf.deb
rm -rf ~/pico

Hardware

Pi lives above door. Connected to shit amp board. Amp board and speaker being replaced very soon - will document this then.

squawk.1475671219.txt.gz · Last modified: 2016-10-05 12:40 by rob

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki