OP-25 Boatbod RPi Autostart on Bootup from a Python program

Status
Not open for further replies.

Carter911

Member
Joined
Aug 17, 2021
Messages
59
Hi Iwvmobile,
Nice article!
There are obviously numerous methods available to shut down an RPi!

rjdj,
The two (Python) LED Flasher programs.
The first is a simple LED Flasher, (only).
The second sets up a push button switch, via an interrupt, to gracefully shut down the RPi.
It also is a framework for having itself, (Program #1), read the state of several RPi I/O pins, and spin off various Program #2's, or shell commands, based upon how the User had set the switches on the RPi's I/O pins.

Sorry for the excessive comments, but it helps me remember what I was doing while it is all still new, and I only get some time now and then to work on it.

Take care,

Jay

Code:
# File JCRPiFlashV2B.py
# Python on RPi 3B+
# JC 4/11/22   4/23/22

# This Python RPi program beeps an piezo
# and then flashes an LED On and Off.
# Crawl before you walk before you run!

# To run in a Terminal, via a command line:
# $ python3 /home/pi/Documents/JCRPiFlashV2B.py <Enter>

import RPi.GPIO as GPIO
import time

# Define Alias names
# This uses the GPIO numbers for the I/O Pins
# Set up the hardware attached to the RPi's I/O pins.
# LEDs have series resistors.
# Switches use RPi's internal pull-up resistors.

LED1 = 23
LED2 = 24
LED3 = 25
SW1 = 10
SW2 = 9
PB1 = 27
PB2 = 22
Piezo = 11

# Setup / Configure the I/O Pins:
GPIO.setwarnings(False)     #Turn Off Warnings
GPIO.setmode(GPIO.BCM)      #Use the GPIO numbers
GPIO.setup(LED1, GPIO.OUT)
GPIO.setup(LED2, GPIO.OUT)
GPIO.setup(LED3, GPIO.OUT)
GPIO.setup(Piezo, GPIO.OUT)
GPIO.setup(PB1, GPIO.IN, pull_up_down=GPIO.PUD_UP)        #Push Button Switch, with Pull Up Enabled

# Beep Piezo on Start-Up
GPIO.output(Piezo, True)
time.sleep(0.025)     #Seconds
GPIO.output(Piezo, False)

# The following Loop runs forever flashing LED1 on and off
# The Python delay, below, is in Seconds.
# The LED flashes at ~ 1 Hz, (990 mSec off, 10 mSec on)
while True:                 
    GPIO.output(LED1, True)
    time.sleep(0.01)
    GPIO.output(LED1, False)
    time.sleep(0.99)
          
# If User Exits nicely via Control C:
GPIO.cleanup()

Code:
# File JCRPiFlashV3B.py
# Python on RPi 3B+
# JC 4/11/22, 4/23/22

# This program demonstrates a Python program, started via crontab
# on RPi's Boot-up, using an interrupt driven push button switch
# to trigger a graceful RPi shut down.

# This program demonstrates Pyton Program #1, (this program),
# reading the state of two RPi I/O pins, to determine the
# user's desired Mode, and then spinning off various other
# Python programs, (Program #2).

# Note well:
# This is currently working fine to spin off a Python LED Flasher
# program or a simple shell command, as the Program #2.
# This is NOT working to spin off initiating OP-25.

# This program runs on an RPi 3B+, with a little HW test PCB
# plugged into the RPi's I/O pins.
# It has: LED 1,2,3 Slide Switch 1,2 PB Switch 1,2 and a Piezo
# LEDs have series resistor.
# Push button and slide swithes use the RPi's internal pull-up resistors.

# One can run this program from the command line in a termnal
# and see the comments, CPU temperature, etc.
# $ python3 /home/pi/Documents/JCRPiFlashV3B.py <Enter>

# One can add this to the end of the RPI's crontab file to
# execute it on the RPi's boot-up.
# The interrupt driven push button switch then triggers a
# graceful shut down of the RPi.
# sudo crontab -e <Enter>
# @reboot python3 /home/pi/JCRPiFlashV3B.py
# This program must be Executable.

# This program reads the Mode, (0-3), of the two slide
# switches.

# This program runs forever on the RPi, so as to keep the
# push button shut down interrupt active.

# On start up the program beeps and briefly flashes 2 LEDs.
# It then sits in the back ground until one presses the
# shut down push button.

# This program is also the framework for testing having this
# program spin off up to 4 different programs, based upon the
# MODE, i.e. the setting of the two slide switches.

# These could be other Python programs, or system actions.
# For testing it triggers different LED flasher programs
# and reads and displays the RPi's temperature,
# (If run from a terminal, not the auto start version which
# doesn't have a terminal in which to display such data)

# JCRPiFlashV6 single flashes LED1
# JCRPiFlashV7 single flashes LED2
# JCRPiFlashV8 double flashes LED1, single flashes LED2

# Mode   Slide SW1  SW2   Testbed action
#  0       0    0        JCRpiFlashV6.py
#  1       0    1        JCRpiFlashV7.py
#  2       1    0        JCRpiFlashV8.py
#  3       1    1        Beep

# This version sets up TWO ISRs for two different PB Switches.
# PB1 Turns on /off LED3 using Interrupts, (Toggles the LED's state)
# PB2 Beeps Piezo, then runs Linux Command to display the uC's Temperature
# The Temp is displayed on the Thonny Shell box, or in a Terminal
# This PBSw then instructs the RPi to Shut Down.
# It takes about 8 Sec for the RPi to execute its shut down.

# Bottom line, I can detect, using a non-CPU intensive ISR,
# a Pin Level change interrupt and then execute a Linux command.

# More notes:
# Note: The os.system command is technically deprecated.
# It could disappear from a future version of Python.
# In the usage below, it initiated the process, but then
# it waits for the spun off process to finish before returning to this program.

# The Subprocess approach spins off the desired program / command,
# to run as its own process under the OS, and then
# returns to this program to keep doing whatever.
# It doesn't wait for the spun off program to exit / terminate
# before resuming here.

# RPi Interrupts can be on Rising Edge, Falling Edge, or Both
# wait_for_edge() is a blocking function...
# add-event-detected is a true interrupt

# PB Sw1 & 2 use the RPi's Internal Pull-Up Resistors,
# so pressing them pulls the pins low.

import subprocess   #This is to run Shell commnands, as spin offs, and return
import os           #This is to run Linus Command from Python
import RPi.GPIO as GPIO
import time

# Define Alias names
# This uses the GPIO numbers for the I/O Pins
LED1 = 23
LED2 = 24
LED3 = 25
SW1 = 10
SW2 = 9
PB1 = 27
PB2 = 22
Piezo = 11

# Init variables:
LEDStatus = 0
    
# Now add the ISR Handler routines:
# Note that the GPIO number is passed back to the ISR routine as the channel
# In this case knowing the channel, the interrupting GPIO pin, doesn't matter.

# PB Sw 1: Toggle LED3 On and Off with each press. Interrupt Driven.
# Added displaying the CPU Temp each time the LED is turned on just for kicks.

def PB1_callback(channel1):
    global LEDStatus               #This tells this function to use the Global LEDStatus variable, not a local one with the same name
    print("PB SW Pressed on GPIO: ", channel1)
    if LEDStatus == 0:       # If LED is off,then turn it on     
        GPIO.output(LED3, GPIO.HIGH)   #Turn On LED
        LEDStatus = 1
        os.system('vcgencmd measure_temp')
    else:                    # If LED is on,then turn it off 
        GPIO.output(LED3, GPIO.LOW)     #Turn Off LED
        LEDStatus = 0
 
# This gracefully shuts down the RPi when the PB Sw is pressed.
# It also beeps the piezo, and displays the RPi's temp, all for kicks.
# Can see messages and Temp if run the program from the terminal, for testing.
# One won't see them when running from crontab startup.

def PB2_callback(channel2):
    print("PB SW #2 Pressed on GPIO: ", channel2)
    GPIO.output(Piezo, True)
    time.sleep(0.02)     #Seconds
    GPIO.output(Piezo, False)
    os.system('vcgencmd measure_temp')   #Display the RPi's temperature
    time.sleep(2.0)
    os.system('sudo shutdown -h now')    #Tell the RPi to Shut Down, Now
 
# Python General setup:
GPIO.setwarnings(False)     #Turn Off Warnings
GPIO.setmode(GPIO.BCM)      #Use the RPi GPIO numbers

# Setup / Configure the I/O Pins:
GPIO.setup(LED1, GPIO.OUT)
GPIO.setup(LED2, GPIO.OUT)
GPIO.setup(LED3, GPIO.OUT)
GPIO.setup(Piezo, GPIO.OUT)
GPIO.setup(PB1, GPIO.IN, pull_up_down=GPIO.PUD_UP)        #Push Button Switch, with Pull Up Enabled
GPIO.setup(PB2, GPIO.IN, pull_up_down=GPIO.PUD_UP)        #Push Button Switch, with Pull Up Enabled
GPIO.setup(SW1, GPIO.IN, pull_up_down=GPIO.PUD_UP)        #Slide Switch, with Pull Up Enabled
GPIO.setup(SW2, GPIO.IN, pull_up_down=GPIO.PUD_UP)        #Slide Switch, with Pull Up Enabled

# Add the ISR Trigger:      i.e. register the callback
# Parameters: GPIO Number, Falling, rising, or Both; callback function name, Debounce time in mSec
GPIO.add_event_detect(PB1, GPIO.FALLING, callback=PB1_callback, bouncetime= 200)   #Debounce 50 mSec

# Add the ISR Trigger:      i.e. register the callback
# Parameters: GPIO Number, Falling, rising, or Both; callback function name, Debounce time in mSec
GPIO.add_event_detect(PB2, GPIO.FALLING, PB2_callback, bouncetime= 200)   #Debounce __ mSec

# What follows is my Main Loop, i.e. the primary foreground code
# Print a Start-Up Message on display
MyMessage = "Starting Program"
print (MyMessage)

print ("Stalling for a few seconds for AVR co-processor to set I/O Mode")
time.sleep(3.0)       #This is a Python delay, in seconds.
print ("Done stalling")

# Read the PB Switchess to get UserMode 0-3 for which program to run.
# On PCB, Slide Switch 1 is on the Left...
UserMode = 0               #Init UserMode value

if GPIO.input(SW1):
    print ("SW 1 is Off")
else:
    print ("SW 1 is On")
    UserMode = UserMode + 2
    
if GPIO.input(SW2):
    print ("SW 2 is Off")
else:
    print ("SW 2 is On")
    UserMode = UserMode + 1
    
print ("UserMode = ", UserMode) #Range: 0 - 3   
 
# Beep Piezo on Start-Up
GPIO.output(Piezo, True)
time.sleep(0.025)     #Seconds
GPIO.output(Piezo, False)

# The Main Loop code goes Here
# This Try loop will capture ^C exits and then execute the GPIO cleanup
# Can run some simple shell commands to show that the program got here.
#os.system('vcgencmd measure_temp')    # Display RPi's temperature
#os.system('ls -l')                    # Directory listing

# Now spin off separate actions based upon the User Mode:
# Can spin off "Program #2", or a simple shell action.
# Note well:
# Spinning off an LED Flasher Python program works.
# Spinning off a simple shell command works.
# Spinning off a shell script to initiate OP-25 doesn't work.

# Remember, run this program from the command line in a terminal
# to see the shell command outputs.
# One won't see them when running this program on RPi Boot-up
# via the crontab file.

if UserMode == 0:
    os.system('vcgencmd measure_temp')     # Show RPi's temperature in terminal
elif UserMode == 1:
    # os.system('python3 JCRPiFlashV6.py')    # This works, and BLOCKS on it, doesn't return
    proc = subprocess.Popen('python3 /home/pi/Documents/JCRPiFlashV6.py', shell=True) # Works and returns!
elif UserMode == 2:
    os.system('python3 /home/pi/Documents/JCRPiFlashV7.py')  # A different LED flasher
elif UserMode == 3:
    # The LED Flasher works, starting OP-25 fails:
    os.system('python3 JCRPiFlashV8.py')
    # os.system('RF7All.sh')     # This worked to start the Terminal mode OP-25, It blocks. it doesn't return
    # proc = subprocess.Popen('/home/pi/JCFiles/RF7All.sh', shell=True)     
    # proc = subprocess.Popen('/home/pi/JCFiles/RF7Hweb.sh', shell=True)     

# Print the following to show one executed the above and returned to this program:
print ("Processed Slide Switch Mode")     
print ("Are back to the Main Loop Code")

# Flash as below to see this if run this on Boot-up without a Terminal to see the message:
GPIO.output(LED1, True)
GPIO.output(LED2, True)
time.sleep(0.5)
GPIO.output(LED1, False)
GPIO.output(LED2, False)

# Add the following code to keep this program alive and running,
# so that one maintains the ISR for shut down capability.

try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:    # ^C Will exit the program       
    GPIO.cleanup()
 

rjdj2000

Gone Cuckoo
Feed Provider
Joined
Jan 24, 2011
Messages
354
Location
Central NY
@wgbecks Here is what is in my json file:

Code:
{
    "channels": [
        {
            "demod_type": "cqpsk",
            "destination": "udp://127.0.0.1:23456",
            "excess_bw": 0.2,
            "filter_type": "rc",
            "frequency": 0,
            "if_rate": 24000,
            "name": "453-454 MHz VC",
            "plot": "symbol",
            "decode": "p25_decoder:role=vc:dev=rtl11",
            "symbol_rate": 4800
        },
        {
            "demod_type": "cqpsk",
            "destination": "udp://127.0.0.1:56124",
            "excess_bw": 0.2,
            "filter_type": "rc",
            "frequency": 0,
            "if_rate": 24000,
            "name": "Cortland CC",
            "plot": "sync",
            "decode": "p25_decoder:role=cc:dev=rtl11:nac=0x4e1",
            "symbol_rate": 4800
        }
    ],
    "devices": [
        {
            "args": "rtl=00000012",
            "frequency": 454400000,
            "gains": "lna:49",
            "name": "rtl11",
            "offset": 0,
            "ppm": 54,
            "rate": 1000000,
            "tunable": false
        }
    ]
}

Script file to run multi_rx:

Code:
#! /bin/sh

l="-l http:127.0.0.1:8080"
U="-U"
T="-T cort.tsv"
python3 multi_rx.py -c cfg-trunk-cort.json $l -v 255 -X $U $T 2> stderr.2

Max provided these for me as he lives not far from me and listens to the same system as me.

This morning while messing with things I removed the python3 call in the script file and put back to ./ and it would run fine. Do just add what you show in your post to the json file?
 
Last edited:

rjdj2000

Gone Cuckoo
Feed Provider
Joined
Jan 24, 2011
Messages
354
Location
Central NY
Jay,

Interesting code there for the LED..... May borrow some of it if I can get it to work on what I need. I have been trying to figure out if the GPIO pin 12 mirrors PWM0 (the 1/8" AV jack on the Pi) as I have found a diagram of the GPIO pin assignments for a Pi 4 and it shows it does. Basically I am going to have 8 LED's (that is how many are in the scanner) and they will sequence along, need to figure out timing on it, until audio is played (will be using the 1/8" jack to a 2.5w amplifier from adafruit to the scanner speaker) then to pause on whatever LED it is on, then resume scanning once it is silent again.

It will be neat if I can get it going as I will be reusing the LED's and switches that are in the scanner along with the volume pot to control volume and to turn off LED's, there is a switch in there and going to wire it in for the common (-) side of the LED's so they all shut off. Probably could do something with the switch to a GPIO to tell it to stop running but I am not planning on turning it off so I'm at the liberty of the power staying on and if it goes out then hopefully everything will be fine as I won't have battery backup (case won't allow it) and it will fire back up once power comes back on.

I will document it all on here somewhere, just not sure where LOL
 

Carter911

Member
Joined
Aug 17, 2021
Messages
59
Nice project!

Is it your intention to have specific LEDs associated with specific Talk Groups, or just have the visual effect of the LED chaser, and it stops whenever any TG becomes active?

I don't know C at all, and I've not looked at the underlying OP-25 code.
I suspect you will have a separate LED chaser program, and likely monitor a flag that is set/cleared whenever OP-25 stops on an active TG.

I suspect the two "interesting" parts of the project will be determining where within the OP-25 code you can easily determine the "stopped on an active TG status", and secondly, how to communicate that flag back and forth between OP-25 and your LED chaser program.

Weekend project, right?! ; )

Jay
 

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,035
Location
NE Wisconsin
Bill, any other thoughts on the Linux / OP-25 audio systems?

Jay

Jay,

PA can and very often is very problematic! I want to recall you're using an RPi but wonder about the USB audio device? Can you elaborate as to why you choose to use USB over the Pi's onboard audio? I am pretty sure you'll have more configs to do in order to make the USB audio device become the system default.

Can you fill in some of the blanks and post your op25 configs and detail the USB device?

Bill
 

Carter911

Member
Joined
Aug 17, 2021
Messages
59
Success!

Using the System.service methodology for activating / starting a process, I now have my Python program #1 starting on RPi Boot-up, via crontab.
It reads a couple of I/O ports, and then initiates OP-25 Boatbod.
I appreciate the help above to get this working.

Currently the Audio is playing via the RPi's audio jack, (even though the RPi Icon says USB Audio), but hey, it works!

I still have to test "stopping" the service, and starting a different OP-25 configuration, but I expect all of that to work, (What could possibly go wrong!, <smirk>).

Bill,

Yes, I am running on an RPi 3B+, with a single RTL-SDR, and OP-25 Boatbod.
I am running the RPi 32-bit OS with the GUI.

The USB audio is a small USB "sound card" dongle with a stereo speaker jack and a mic jack.
My "normal" OP-25 setup, started from the command line from a terminal, plays through this without any difficulties, (for both web and terminal interfaces).
IIRC, I started using this when I initially started setting up an RPi and OP-25, as I was having some audio routing issues, (and secondarily many on-line comments said the RPi's on board audio is via a PWM output, and is inferior to even a cheap "sound card" output. For voice it probably doesn't matter much!).

It would still be nice to know why / how the audio is going to the RPI's audio jack, instead of to the USB audio device, (regardless of what the RPi's speaker Icon says to use), and to know that this is the expected behavior, and it won't suddenly change down the road with either an RPi OS update, or with an OP-25 update.

Full disclaimer: I've not looked at all of the OP-25 parameters lately, as the config worked. I'll post my command line below.
I still, also, have the pulseaudio in my crontab file, as suggested above, also.

The setup is:
crontab calls both JCRPiFlashV11.py, and pulseaudio
JCRPiFlashV11.py starts JCServiceTestV1.service
JCServiceTestV1.service starts JCLED1.sh
JCLED1.sh calls RF7Allweb.sh
RF7Allweb.sh starts up rx.py...., as shown below.

Whew! Trivial! A piece of cake! No sweat!
(OK, don't ask me how many hours I've invested in this, but I"ve learned a lot along the way!)

RF7Allweb.sh (Just plays all Summit County traffic, for testing, (No white list or priority))
#!/bin/bash
cd /home/pi/op25/op25/gr-op25_repeater/apps
./rx.py --args 'rtl' -N 'LNA:35' --crypt-behavior 2 -S 1000000 -x 2 -o 25000 -q -1 -d -300 -T trunkJC1.tsv -D cqpsk -w -U -l http:0.0.0.0:8080 2>stderr-stream0.2

Once again, I am very appreciative of everyone's insight and support!

Jay
 

rjdj2000

Gone Cuckoo
Feed Provider
Joined
Jan 24, 2011
Messages
354
Location
Central NY
Jay..... Sounds good.... I could get rx.py to work as well doing the systemd setup but having a heck of a time with the multi_rx, even looking at some things on the version you are running, I have not faired well. Hopefully Bill can give some insight to me as I posted stuff in here and also have sent him a PM on it.

As to the audio on the 1/8 jack, when using powered speakers I see no audio difference from another pc running SDRTrunk nearby. So it is why I am going to use it on my project, actually got a 1/8" jack to screw terminals from adafruit to use with the amp board I got from there. Then I know I am not going to get any video signals off of it as well. Which brings me to another project...... Well maybe..... Ya see I have this old Ranger portable TV, with like a 4" black and white screen that is of no longer any use... I wonder if I use an old tv hookup box like the atari's used to use and then take a rca of the video on that port if it would work..... See why it is not best to leave me unsupervised? That project will have to wait as I need to even see if it will turn on anymore...lol Talk about a portable Pi though... plastic case so could use wireless keyboard and mouse, interal mounted antenna. Just not sure how much room is in there for everything as could add battery (tv worked on 12VDC I believe) then have full portability for a little bit....

Well I've let my mind ramble on too much for now but am glad you got things sorted out to get you in the right direction....

RJ

Edit: Picture of the Ranger-505
ranger.jpg
 
Last edited:

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,035
Location
NE Wisconsin
- - - - many on-line comments said the RPi's on board audio is via a PWM output, and is inferior to even a cheap "sound card" output. For voice it probably doesn't matter much!.

Jay,

That's an untrue statement regarding inferior audio quality being produced by the RPi's onboard audio device! The problem is that most
people aren't aware that you will often get noise and distortion when inserting a standard 3.5 mm TRS plug into the RPi's earphone jack.

This is because the Pi's use 4-pole TRRS jack that have an analog video signal tied to one of the poles that often gets crossed up with the
audio when inserting a standard TRS plug. You need to be using a 4-Pole TRRS plug wired to mate with the RPi's hardware architecture
or the proper adapter. I have had very good results using the Sennheiser PCV 05 Combo Audio Adapter to interface my amplified speakers that terminate with the usual 3.5 mm TRS plugs.

Two of my Pi-4's arranged for private feeds have amplified speakers connected to the earphone connectors in addition to the streaming
audio from OP25 linked into Liquidsoap that in turns supplies the mp3 streams going to and Icecast server that both produce excellent
audio quality.

My recommendation would be to stick with the Pi's audio device while using the appropriate plug or adapter and to scrap the USB audio
device unless you are prepared to deal with the additional configurations necessary to make it become the default device and to insure
that it all runs automatically under control of a system service.

Bill
 

Carter911

Member
Joined
Aug 17, 2021
Messages
59
Pi's use 4-pole TRRS jack

I guess I read that before, and forgot about it.
That would explain why one of my amplified speakers, with the usual 3 conductor setup, just buzzes loudly when plugged in.

Onward and upward!

Thank you, again, everyone!

JC
 

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,035
Location
NE Wisconsin
Here's the circuit drawing of the audio section of a Pi-3B showing the A/V Jack (J7) that's very unconventionally wired to say the least.
Observe the composite video tied to the sleeve that normally serves as the audio return (ground) in the 3.5 mm TRS world.

The actual ground return is wired to Ring-2 that if you're lucky when inserting a standard TRS plug will short with the sleeve and not
produce any buzzing or extraneous noise. You'll love the audio one you use the correct plug and wiring!


AV Jack.png
 

Carter911

Member
Joined
Aug 17, 2021
Messages
59
Hi Bill,

Gotta laugh!

I've got a couple of meetings today, but before then I wanted to see about ordering a TRRS plug or two.
So I pulled up an RPi 3B+ schematic and zoomed in on the audio section to see what I was dealing with.
I have that same image on one of my displays at the moment!

I remember that you had mentioned an adapter that worked well for you, so I opened this Thread on another screen and found you'd updated this Thread with the same image!

Take care,

Jay
 

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,035
Location
NE Wisconsin
Gotta laugh!

I remember that you had mentioned an adapter that worked well for you, so I opened this Thread on another screen and found you'd updated this Thread with the same image!

I really don't understand the need for composite video in this day in age. Who even has a compatible video monitor let alone the
totally "non-standard" jack wiring pinout! Perhaps there are countries where the Pi's are sold that are still in the video dark ages?
 
Status
Not open for further replies.
Top