Initial version

This commit is contained in:
cpu
2023-12-02 03:15:11 +01:00
committed by cpu
parent 4abdbfc089
commit 6a9d31f231
10 changed files with 253 additions and 2 deletions
+15
View File
@@ -0,0 +1,15 @@
pcm.my_usb_sound_device {
type hw
card USB
device 0
}
pcm.my_usb_sound_device_with_converter {
type plug
slave {
pcm my_usb_sound_device
format S16_LE
channels 2
rate 48000
}
}
+83 -2
View File
@@ -1,3 +1,84 @@
# dog-trainer Dog Trainer
===========
A device that allows dogs to speak A device that allows dogs to speak. Click on the picture to see the video with the `dog-trainer` in action
![dog-trainer-photo](assets/dog-trainer-photo.png "https://peertube.virtonline.eu/w/2MXWQPC1zd2pgms5HQE1Ma")
Hardware
--------
Connect buttons to the Rarspberry pi GPIO as shown in the schema to debounce noisy buttons
![dog-trainer-schema](assets/dog-trainer-schema.png)
Operating system
----------------
Install `Raspberry Pi Imager` into your notebook. Launch it and choose `Raspberry Pi OS Lite` to write into the microSD card
Power on the Raspberry pi and finish the installation process. Log into its console using either:
- ethernet/wifi from your notebook: `ssh pi@raspberrypi`
- connected keyboard and monitor
- serial port
How to install
--------------
Once you are logged into the `Raspberry pi` console clone the `dog-trainer` source code into your home directory
> `git clone https://gitea.virtonline.eu/2HoursProject/dog-trainer.git --depth 1`
Go into the newly created directory
> `cd ~/dog-trainer`
Dependencies
------------
The recordings must be edited a little bit so some audio processing tool is needed. Install `ffmpeg` which will do the job
> `apt install -y ffmpeg`
Configuration
-------------
If your sound device is other then USB type then modify and adapt the line `hw:CARD=USB,DEV=0` in ALSA config
> `nano .asoundrc`
Install the ALSA config file
> `mv .asoundrc ~/`
Run it!
-------
Just run
> `python3 dog-trainer.py`
Pres `Ctrl+c` to exit
Make it a service to automaticaly run in the backgroud
------------------------------------------------------
If your `$HOME` directory is other then `/home/pi` then modify the systemd service file
> `sed -i "s#/home/pi#$HOME#g" dog-trainer.service`
If your username and group is other then `pi` and `pi` then also run
> `sed -i "s#User=pi#User=$USER#" dog-trainer.service`
`sed -i "s#Group=pi#Group=$(id -gn)#" dog-trainer.service`
Install the systemd service file
> `sudo mv dog-trainer.service /etc/systemd/system/`
Reload systemd manager configuration
> `systemctl daemon-reload`
Enable the `dog-trainer` service at boot time and also run it
> `systemctl enable --now dog-trainer`
If it does not work check logs
> `journalctl -f -u dog-trainer`
Set volume for playback and recording
-------------------------------------
Launch soundcard mixer
> `alsamixer`
Press F6 to select a soundcard. Find your USB sound device in the list e.g. `Jabra Speak 410 USB`. Press F4 and use up/down arrow keys to set recording volume and repeat also for the playback (F3). Press ESC fo exit.
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
View File
Binary file not shown.
Executable
+139
View File
@@ -0,0 +1,139 @@
import RPi.GPIO as GPIO
import time
from datetime import datetime
import subprocess
import sys
PLAY_DEVICE = "my_usb_sound_device_with_converter" # as defined in '~/.asoundrc'
REC_DEVICE = "my_usb_sound_device" # as defined in '~/.asoundrc'
MAX_RECORDING_TIME = 30 # limit recordings to only e.g. 30 seconds long
TRIM_RECORDING_END = 0.8 # remove the clicking noise from the end of recordings
RECORD_BUTTON_PRESS_TIME = 2 # pressing and holding a button longer then e.g. 2 seconds triggers recording otherwise playing
PINS = [17, 23] # listen to changes on this GPIOs e.g. [4, 17, 23, 0, 5, 6]
GPIO.setmode(GPIO.BCM) # use BCM pin layout
GPIO.setwarnings(False)
def setup_pins():
for pin in PINS:
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_OFF)
def terminate_recording(proc, pin):
print("Terminating recording...")
proc.terminate()
proc.wait(timeout=5)
print(f"Cutting last {TRIM_RECORDING_END} second (click noise) from the recording")
audio_duration = subprocess.run(
[
"ffprobe",
"-v",
"error",
"-select_streams",
"a:0",
"-show_entries",
"stream=duration",
"-of",
"default=noprint_wrappers=1:nokey=1",
f"recording_temp_{pin}.wav",
],
stdout=subprocess.PIPE,
).stdout.decode("utf-8")
subprocess.call(
[
"ffmpeg",
"-hide_banner",
"-y",
"-i",
f"recording_temp_{pin}.wav",
"-t",
f"{float(audio_duration) - TRIM_RECORDING_END}",
"-acodec",
"copy",
f"recording_{pin}.wav",
]
)
DATE_TIME = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
print(f"Archiving old sound to archive/recording_{pin}_{DATE_TIME}.wav")
subprocess.call(
["mv", f"recording_temp_{pin}.wav", f"archive/recording_{pin}__{DATE_TIME}.wav"]
)
play_sound(pin)
def play(file):
subprocess.call(["aplay", "-D", PLAY_DEVICE, file])
def play_sound(pin):
play(f"recording_{pin}.wav")
def play_beep():
play("beep.wav")
def play_intro():
play("Windows_95.wav")
def record_sound(pin):
print(f"GPIO {pin} LOW - Starting recording_temp_{pin}.wav")
proc = subprocess.Popen(
[
"arecord",
"-D",
REC_DEVICE,
"-f",
"S16_LE",
"-r",
"16000",
"-c",
"1",
"-d",
f"{MAX_RECORDING_TIME}",
f"recording_temp_{pin}.wav",
]
)
print(f"Recording in progress (max {MAX_RECORDING_TIME} seconds)...")
wait_for_button_release_or_timeout(pin, MAX_RECORDING_TIME)
print("GPIO went HIGH - terminating recording")
play_beep()
terminate_recording(proc, pin)
def wait_for_button_release_or_timeout(pin, timeout):
timeout_start = time.time()
while time.time() < timeout_start + timeout:
if GPIO.input(pin) == GPIO.HIGH:
break
time.sleep(0.05)
def handle_pin(pin):
print(f"GPIO {pin} LOW - event triggered")
timeout_start = time.time()
wait_for_button_release_or_timeout(pin, RECORD_BUTTON_PRESS_TIME)
if time.time() - timeout_start >= RECORD_BUTTON_PRESS_TIME:
# Pin was LOW for longer time; recording
play_beep()
record_sound(pin)
else:
# Pin was LOW for shorter time; playing
play_sound(pin)
try:
setup_pins()
subprocess.call(["mkdir", "-p", "archive"])
play_intro()
for pin in PINS:
GPIO.add_event_detect(pin, GPIO.FALLING, callback=handle_pin, bouncetime=1000)
while True:
time.sleep(0.01)
except Exception:
print("Cought exception...")
sys.exit()
finally:
GPIO.cleanup()
+16
View File
@@ -0,0 +1,16 @@
[Unit]
Description=Dog Trainer
After=sound.target
[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/dog-trainer
ExecStart=/usr/bin/python3 -u /home/pi/dog-trainer/dog-trainer.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.