User Tools

Site Tools


squawk

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
squawk [2024-12-04 05:04] – [Speakers] riversquawk [2024-12-07 21:22] (current) aria
Line 1: Line 1:
-====== Audio Alerts (squawk) ======+====== Squawk ====== 
 + 
 +Squawk is the Hacklab's sound effects, alerts (e.g. doorbell), and announcement system.
  
 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 purchased a pi (original B), amplifier and speakers for the purpose and documents them thus: 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 purchased a pi (original B), amplifier and speakers for the purpose and documents them thus:
Line 25: Line 27:
 mosquitto_pub -h mqtt -t 'sound/g1/announce' -m "Good evening, my sensors indicate that the laser cutter may be on fire." mosquitto_pub -h mqtt -t 'sound/g1/announce' -m "Good evening, my sensors indicate that the laser cutter may be on fire."
 </code> </code>
- 
-**Theory:** You could direct sound two only two rooms at once by playing the left or right channel only. By default mpg123 is called with the -m option to ensure that the output is mono and thus the same sound comes out every speaker. 
  
 ===== Adding new sounds ===== ===== Adding new sounds =====
  
-Use the web file browser at http://squawk.hacklab:8080/.+<del>Use the web file browser at http://squawk.hacklab:8080/.</del> 
 + 
 +For now, use scp to squawk.hacklab.
  
 ===== Raspberry Pi OS ===== ===== Raspberry Pi OS =====
  
 Latest Raspberry Pi OS, installed using Raspberry Pi Imager. Latest Raspberry Pi OS, installed using Raspberry Pi Imager.
 +
 ==== raspi-config ==== ==== raspi-config ====
  
Line 72: Line 75:
  
 [Service] [Service]
-ExecStart=/home/pi/startup.sh & /home/pi/respawn --syslog --max-backoff=10 /home/pi/squawk.pi+WorkingDirectory=/home/pi/newsquawk 
 +ExecStart=/home/pi/newsquawk/venv/bin/python /home/pi/newsquawk/main.py 
 +User=pi 
 +Restart=always 
 +RestartSec=3
  
 [Install] [Install]
 WantedBy=multi-user.target WantedBy=multi-user.target
- 
 </code> </code>
  
Line 85: Line 91:
 $ sudo systemctl enable squawk $ sudo systemctl enable squawk
 </code> </code>
 +
 ===== Scripts ===== ===== Scripts =====
  
-Various scripts to make things happen. +The code for squawk is stored in <code>/home/pi/newsquawk</code>TODOthis should probably be mirrored somewhere.
- +
-==== /home/pi/squawk.py ==== +
- +
-<code python> +
-#!/usr/bin/env python +
- +
-import paho.mqtt.client as mqtt +
-import subprocess +
-import os +
-import logging +
-import signal +
-import time +
-import random +
- +
-logging.basicConfig(level=logging.INFO) +
- +
-max_playtime  = 15 +
-sounds_path = "/home/pi/sounds" +
- +
-status = 'closed' +
- +
-# 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: +
-            logging.error('No files matching %s' % (filename)) +
-            return +
-        filename = random.choice(candidates) +
-    else: +
-        # single file requested +
-        if filename not in allfiles: +
-            logging.error('File %s not found' % (filename)) +
-            return +
-    base, ext = os.path.splitext(filename) +
-    if ext == '.mp3': +
-        command = ['mpg123', '-q', '-m', 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"+
-    client.subscribe("sound/g1/announce"+
-    client.subscribe("display/doorbot/intercom"+
-    client.subscribe("labstatus"+
- +
-def on_message(client, userdata, msg): +
- +
-    global status +
- +
-    if msg.topic == 'labstatus': +
-        if msg.payload == 'open': +
-           status = 'open' +
-        else: +
-           status = 'closed' +
- +
-    # 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('dongq.mp3'+
-        speak(msg.payload) +
- +
-    if msg.topic == 'sound/g1/announce': +
-        play('chime.mp3'+
-        speak(msg.payload) +
- +
-    if msg.topic == 'access/entrance/request': +
-        if status == 'closed': +
-            play('doorbell.mp3'+
- +
-m = mqtt.Client() +
-m.on_connect = on_connect +
-m.on_message = on_message +
-m.connect("mqtt"+
-m.loop_forever() +
-</code> +
- +
-==== /home/pi/respawn ==== +
- +
-<code python> +
-#!/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'+
-</code> +
- +
-==== /home/pi/pico.sh ==== +
- +
-<code bash> +
-#!/bin/bash +
-pico2wave -l en-GB -w /tmp/pico.wav "$1" +
-play -q /tmp/pico.wav +
-rm -f /tmp/pico.wav +
-</code> +
- +
-==== /home/pi/startup.sh ==== +
- +
-<code bash> +
-#!/bin/bash +
-sleep 3 +
-_IP4=$(hostname -I | cut -d ' ' -f 1) || true +
- +
-mpg123 -m -q /home/pi/sounds/indyboot.mp3 +
-/home/pi/pico.sh "System boot complete. IP address is $_IP4" +
-</code>+
  
 ===== Audio files ===== ===== Audio files =====
Line 399: Line 116:
 -rw-r--r-- 1 pi pi   6414 Oct  4 22:32 uhoh.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 -rw-r--r-- 1 pi pi  27584 Oct  4 22:32 whistle.mp3
-</code> 
- 
-===== Pico TTS ===== 
- 
-**This is probably not required as we now use AWS TTS service.** 
- 
-<code> 
-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 
 </code> </code>
  
Line 460: Line 160:
  
 Located in the ceilings of G1, G2, G8, and G11. Located in the ceilings of G1, G2, G8, and G11.
 +
 +The wiring to G8 and G11 is in some really awful aluminium Cat 5 which can break if you touch it.
 +
 +{{:photo_2016-10-10_16-39-03.jpg?direct&200 |}} {{:photo_2016-10-10_16-39-09.jpg?direct&200 |}} {{:photo_2016-10-10_16-39-10.jpg?direct&200 |}} {{:photo_2016-10-10_16-39-12.jpg?direct&200 |}}
 +
squawk.1733288693.txt.gz · Last modified: 2024-12-04 05:04 by river

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki