wb2osz / direwolf

Dire Wolf is a software "soundcard" AX.25 packet modem/TNC and APRS encoder/decoder. It can be used stand-alone to observe APRS traffic, as a tracker, digipeater, APRStt gateway, or Internet Gateway (IGate). For more information, look at the bottom 1/4 of this page and in https://github.com/wb2osz/direwolf/blob/dev/doc/README.md
GNU General Public License v2.0
1.55k stars 302 forks source link

Add GPIO LED support when PTT is asserted #61

Open dranch opened 7 years ago

dranch commented 7 years ago

The Direwolf DCD indicator is very helpful and I wonder if it would be possible to add another output GPIO pin for when PTT is asserted? I realize I could just tap the PTT GPIO pin to get the LED to light up but I think it would be a cleaner design if I could do this via another GPIO pin.

craigerl commented 4 years ago

Any traction here? I was just going over the source myself. I can't think of an elegant hardware solution (radio's ptt and led hooked up to same gpio). Monitoring the PTT state and applying a copy -cat cpio assertion would be doable. Ideally we add two pins to the PTT line in direwolf.conf.

ew1abz commented 4 years ago

@dranch, @craigerl The most elegant solution to connect LED (or 2 LEDs) along with radio's PTT is HARDWARE. It does not make sense to double that functionality in software.

craigerl commented 4 years ago

I respectfully disagree. I pulled the source, and I think I can make a branch that has this functionality. vs pulling out the soldering iron, or coming up with two circuits that can somehow use the same gpio pin without affecting each other.

dranch commented 4 years ago

There is some validity to this request as there is another request for Direwolf to have a pre and post-PTT command as well. I can see where a pre-PTT command might change an antenna relay box to disconnect the antenna from an SDR and connect it to a TX radio's antenna. Direwolf would then transmit it's packet and then the post-PTT command would return things.

ew1abz commented 4 years ago

Sorry, maybe I should've provided more arguments. Functionality, that you are requesting, is very custom and most of users never going to use it. It's just not practical to satisfy every small request. Hardware modification seems harder but it's easy and safe in fact. I can check your schematic if you want.

craigerl commented 4 years ago

ok, giving up on a direwolf tweak.. I wrote a gpio mirror script that asserts voltage on a gpio pin (27) whenever direwolf's PTT gpio pin (24) is high. It uses the kernel's inode-notify feature and sysfs, so there's no polling. The LED on gpio 27 lights up as direwolf transmits.

"pip install pyinotify" first

#!/usr/bin/python

import pyinotify
import RPi.GPIO as GPIO

#gpio27 output works
#gpio23 output fails

def handle_change(cb):
   with open('/sys/class/gpio/gpio24/value', 'r') as f:
      #print(f.read())
      status = f.read(1)
      if status == '0':
         #print("OFF")
         GPIO.output(27, GPIO.LOW)
      else:
         #print("ON")
         GPIO.output(27, GPIO.HIGH)
   f.close

def null_function(junk):  # default callback prints tons of debugging info
   return()

GPIO.setmode(GPIO.BCM)    # logical pin numbers, not BOARD
GPIO.setup(27, GPIO.OUT)
GPIO.setwarnings(False)   # suppress pin-is-in-use warning

# Instanciate a new WatchManager (will be used to store watches).
wm = pyinotify.WatchManager()

# Associate this WatchManager with a Notifier
notifier = pyinotify.Notifier(wm, default_proc_fun=null_function)

# Add a new watch
wm.add_watch('/sys/class/gpio/gpio24/value', pyinotify.IN_MODIFY)

# Loop forever and handle events.
notifier.loop(callback=handle_change)
ew1abz commented 4 years ago

So @craigerl, you found the third way how to solve it. I even didn't think this direction. It really looks elegant and solderless :) I like it! It also may be useful for other projects. Thank you for sharing it!

hemna commented 3 years ago

I would very much like this feature as well.

craigerl commented 3 years ago

This keeps coming up. This time, if PTT is accomplished via CAT/USB, there's just no way for us to know if direwolf is transmitting, and no way for us to light up a red indicator LED. I would really like to see a feature that allows for multiple PTT devices on the PTT line of direwolf.conf.

craigerl commented 2 years ago

So is there any way to know if direwolf is transmitting? I'm down to USB sniffing for cat commands at this point. Ultimately, if a raspberry Pi is connected to a USB-based radio, there is no way to light up the red LED on this project, http://craiger.org/digipi/

dranch commented 2 years ago

There are several ways:

craigerl commented 2 years ago

ok, i'm watching for lines that start with [0H] since they seem to be transmit lines. More specifically "[0[A-Z]]". Does that regex capture all transmit lines in the log file?

It's a hack, but at least I can light up a Red LED when direwolf is keying a transmitter over a USB cable :/

It really would be nice to have direwolf assert voltage on multiple GPIO pins during a transmit...

def red_led_from_logfile_thread():                               ## RED logfile
   f = subprocess.Popen(['tail','-F',logfile], stdout=subprocess.PIPE,stderr=subprocess.PIPE)
   while True:
      line = f.stdout.readline().decode("utf-8", errors="ignore")
      search = re.search("^\[\d[A-Z]\]", line)
      if search is not None:
         draw.ellipse(( width - title_bar_height * 2           , padding,    width - title_bar_height - padding * 2 , title_bar_height - padding), fill=(200,0,0,0))
         with display_lock:
            disp.image(image)
         time.sleep(1)
         draw.ellipse(( width - title_bar_height * 2           , padding,    width - title_bar_height - padding * 2 , title_bar_height - padding), fill=(80,0,0,0))
         with display_lock:
            disp.image(image)
CtrlC-Root commented 2 months ago

So I have a radio with two VFOs; one of them is tuned to the APRS frequency and used by Direwolf and the other one is used by other software but they share the PTT signal. I wanted to find a way to perform multiple arbitrary actions before the PTT is engaged and after it is disengaged by Direwolf in order to enforce some rules on which software can transmit at which time. I considered a few options but ultimately I decided to solve this through software. The relevant code in Direwolf is pretty straight-forward but I did not want to maintain my own fork just for this functionality. So I implemented a work-around.

I use a Digirig interface with the PTT toggled through the serial port RTS line. I wrote a shared library which can be loaded with LD_PRELOAD in order to intercept the RTS on/off command, execute an external shell script, and optionally allow or discard the RTS signal from being sent to the serial port based on the script exit code. This allows me to both (1) do arbitrary things like toggle GPIO pins before/after PTT and (2) prevent the PTT signal from being sent to the Digirig interface if I don't want Direwolf to trigger it at that moment.

  1. Clone or download this project: https://github.com/CtrlC-Root/ttyhook
  2. Compile the project using Make. You'll need the ttyhook.so file that's compiled.
  3. Set up a script to handle the RTS/DTR signal you are interested in from Direwolf (see example below).
  4. Set up a script that wraps the direwolf binary and configures the appropriate environment variables to load the ttyhook.so shared library and tell it which script to use (see example below).

Here's an example of a ttyhook script that toggles a GPIO pin before/after the Direwolf PTT RTS signal:

#!/bin/bash

set -e

COMMAND=$1

GPIO_PIN="577" # adjust to whatever GPIO pin number you want to use
GPIO_PIN_ROOT="/sys/class/gpio/gpio${GPIO_PIN}"
GPIO_PIN_DIRECTION="${GPIO_PIN_ROOT}/direction"
GPIO_PIN_VALUE="${GPIO_PIN_ROOT}/value"

ptt_enable() {
  # export pin
  if [ ! -d "$GPIO_PIN_ROOT" ]; then
    echo "$GPIO_PIN" > /sys/class/gpio/export
  fi

  # configure pin for output
  echo "out" > "${GPIO_PIN_DIRECTION}"

  # XXX adjust permissions to allow direwolf to toggle pin
  chown root:direwolf "$GPIO_PIN_VALUE"
  chmod 0660 "$GPIO_PIN_VALUE"
}

ptt_disable() {
  if [ -d "$GPIO_PIN_ROOT" ]; then
    echo 0 > "$GPIO_PIN_VALUE"
    echo "$GPIO_PIN" > /sys/class/gpio/unexport
  fi
}

# process command
case "$COMMAND" in
  # run these commands manually with sudo before starting direwolf
  enable)
    ptt_enable
    ;;

  disable)
    ptt_disable
    ;;

  # toggle pin based on RTS signal from ttyhook
  rts_on)
    echo 1 > "$GPIO_PIN_VALUE"
    ;;

  rts_off)
    echo 0 > "$GPIO_PIN_VALUE"
    ;;

  # fall through
  *)
    echo "unknown command: ${COMMAND}"
    exit 1
    ;;
esac

Here's an example of a script I use to wrap Direwolf and set up the hook:

#!/bin/bash

set -e

TOP=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
DIREWOLF="/usr/bin/direwolf"

export LD_PRELOAD="${TOP}/ttyhook.so"  # adjust to wherever this file is if not in the current folder
export TTYHOOK_SCRIPT="${TOP}/ttyhook.sh"  # adjust to wherever your ttyhook script is if not in the current folder

exec "$DIREWOLF" $@

Here is how I start direwolf with these scripts:

sudo ttyhook.sh enable  # configure GPIO pin for output by direwolf user
sudo -u direwolf ./direwolf.sh  # start Direwolf with the ttyhook library as the direwolf user

This works well enough for my purposes at the moment (single PTT via serial port RTS signal). If you use multiple serial port PTTs you would need to adjust the ttyhook implementation to differentiate between them somehow (probably by monitoring open() calls and keeping track of file descriptors). If you use a different kind of PTT mode (GPIO, Hamlib, etc) you could use this same technicue to hook into whatever API Direwolf uses to trigger the PTT in those modes.

I think a more maintainable long term solution would be to build scripting support into Direwolf or if that's too complicated then implement some kind of plugin API for third parties to implement their own custom functionality. That might even allow all the different audio/PTT code to be moved into separate plugins.

CtrlC-Root commented 2 months ago

There is some validity to this request as there is another request for Direwolf to have a pre and post-PTT command as well. I can see where a pre-PTT command might change an antenna relay box to disconnect the antenna from an SDR and connect it to a TX radio's antenna. Direwolf would then transmit it's packet and then the post-PTT command would return things.

This is indeed one of the things I plan to do in the future now that I have this functionality. Note that the script is called before RTS/DTR is turned on (so it can delay that until after the script runs or prevent it entirely if it's not safe to continue) and after RTS/DTR is turned off (so it can safely restore things afterwards).