I have a python script which, when a GPIO button is pressed, launches an arecord
process. This records audio from a USB microphone (plughw:CARD=GS3,DEV=0
) and pipes it to another program for modulation and eventually transmission via rpitx
.
I spent a long time debugging the script from the terminal and have gotten it to work flawlessly. However, when I launch it from a systemd
service (whether manually or on startup), the arecord
process fails with the following error:
tx.py[9204]: arecord: main:828: audio open error: Device or resource busy
Again, this never occurs when I run the command manually from the CLI (as root or any other user). But, the goal is to have this script run at startup, as I will not be able to access the Pi with a mouse and keyboard. Here is my current code:
/etc/systemd/system/tx.service
[Unit]
Description="Service for detecting and starting RF transmissions"
[Service]
TimeoutStartSec=0
ExecStart=/home/pi/tx.py
[Install]
WantedBy=multi-user.target
/home/pi/tx.py
#!/usr/bin/python
import commands
import os.path
import RPi.GPIO as GPIO
import subprocess
import time
FREQ_PATH = "/home/pi/frequency.txt"
MODE_PATH = "/home/pi/mode.txt"
PS_DELAY = 0.25
RX_FLAG = "/home/pi/rx_flag"
def ConvertFreq(freq):
return str(int(freq) / 1000)
def GetFileText(path):
with open(path, "r") as file:
return file.read().replace("\n", "")
def IsActive(name):
output = commands.getoutput("systemctl is-active " + name)
return output == "active"
def IsRunning(name):
output = commands.getoutput("ps -a")
return name in output
def main():
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(37, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
while True:
if GPIO.input(37) == 0 and IsRunning("rpitx") == False: # Button is pressed, so start transmitting
print ""
print "Starting transmission..."
if IsActive("rx"):
print "Stopping RX service..."
subprocess.Popen("sudo systemctl stop rx; sudo touch " + RX_FLAG, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # The purpose of the last two arguments is to mute the subprocess output
mode = GetFileText(MODE_PATH) # Retrieve which mode to use
freq = ConvertFreq(GetFileText(FREQ_PATH)) # Retrieve which frequency to use
if mode == "am":
subprocess.Popen("arecord -c1 -r 48000 -D plughw:CARD=GS3,DEV=0 -f S16_LE - | csdr convert_s16_f | csdr gain_ff 1.0 | csdr dsb_fc | csdr add_dcoffset_cc | rpitx -i- -m IQFLOAT -f " + freq, shell=True) # PROBLEM OCCURS HERE! Didn't mute the output so we can debug
print "Transmitting on " + freq + " kHz with mode " + mode.upper()
elif GPIO.input(37) == 1 and IsRunning("rpitx"): # Button no longer pressed, end transmission
print "Ending transmission..."
subprocess.Popen("sudo killall arecord", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if os.path.isfile(RX_FLAG):
print "Restarting RX service..."
subprocess.Popen("sudo systemctl restart rx; sudo rm " + RX_FLAG, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print "Transmission ended"
time.sleep(PS_DELAY)
GPIO.cleanup()
quit()
if __name__ == "__main__":
main()
Output of systemctl status tx.service
● tx.service - "Service for detecting and starting RF transmissions"
Loaded: loaded (/etc/systemd/system/tx.service; disabled; vendor preset: enabled)
Active: active (running) since Tue 2019-08-06 23:05:59 EDT; 8s ago
Main PID: 9204 (tx.py)
Tasks: 2 (limit: 2077)
Memory: 3.6M
CGroup: /system.slice/tx.service
└─9204 /usr/bin/python /home/pi/tx.py
Aug 06 23:06:04 Jeep tx.py[9204]: End of file
Aug 06 23:06:05 Jeep tx.py[9204]: Warning : rpitx V2 is only to try to be compatible with version 1
Aug 06 23:06:05 Jeep tx.py[9204]: arecord: main:828: audio open error: Device or resource busy
Aug 06 23:06:05 Jeep tx.py[9204]: End of file
Aug 06 23:06:05 Jeep tx.py[9204]: arecord: main:828: audio open error: Device or resource busy
Aug 06 23:06:05 Jeep tx.py[9204]: Warning : rpitx V2 is only to try to be compatible with version 1
Aug 06 23:06:05 Jeep tx.py[9204]: End of file
Aug 06 23:06:05 Jeep tx.py[9204]: Warning : rpitx V2 is only to try to be compatible with version 1
Aug 06 23:06:05 Jeep tx.py[9204]: arecord: main:828: audio open error: Device or resource busy
Aug 06 23:06:05 Jeep tx.py[9204]: End of file
(The output repeats because rpitx
never runs [a result of arecord
not running]. Therefore, the script keeps running the same code.)
Thinking it was an issue with systemd
, I tried launching it with an entry in rc.local
, but the same behavior resulted. I have tried running the service with different users, groups, and by changing all commands to include sudo
; also to no avail. Additionally, I checked to make sure no other processes were using the USB microphone at the time (and nothing was).
To me, this seems like an issue due to the environment the script is executed in (since it works from the CLI but not from systemd
). Are there special privileges a systemd
service needs to access the USB microphone? It is being run in a sandbox? Are multiple instances of the service running? Are any of the settings in systemctl show tx.service
required for this to work? Is there anything blatantly obvious that I'm overlooking?