Run script by radio input

I would like to execute a script from a switch on my radio. For example, using an RC input to turn on/off gpio or start recording video to a USB stick. Does anyone have any ideas? Could this be hacked into the source?

@slackr31337

Hello Robert!

I’ll lay out some hints about the way I see it implemented. I think the easiest way is adding a function to UserCode.cpp where users’ hooks are intended to be placed into. This function implements the trigger detection logic using RC_Channel_aux or RCInput API. For example you can check if there’s new sample available with new_input(), aux_ch->read() and compare the input with a threshold value. Once the input is above the value you can trigger the logic you have in mind.

Beware that this function must not block since it could lead to a crash.

Wow I had no idea of this code entry point. Are there any examples of the usage the OP asked for?

Hi George,

Thank you for the information. That is what I was looking for.

@pneves
I’m not sure if the APM documentation schreds a light on the issue. I’ve come across these user hooks while reading APM source code. I think there’re plenty of examples on APM forum, but I can’t be sure.

@slackr31337

You’re welcome! I’d be glad if you posted the results of your work. The community would benefit from that.

So far this is what I have. It works but it could be written better. I have a script in /etc/init.d/raspivid and it starts and stops video recording to the SD card. I assigned channel 6 to a switch on my transmitter.

added this file to /etc/init.d/raspivid

http://1drv.ms/1GQk9Px

Added this to defines.h

#define USERHOOK_SUPERSLOWLOOP ENABLED

Added this to UserCode.pde

#ifdef USERHOOK_SUPERSLOWLOOP
#include <stdlib.h>
#include <AP_HAL.h>
#include <AP_HAL_Linux.h>
#include <AP_Common.h>
#define MAX_CHANNELS 16

static uint16_t last_value[MAX_CHANNELS];

void userhook_SuperSlowLoop()
{
uint8_t _channel=6;
bool run = false;
uint16_t v = hal.rcin->read(_channel-1);

if ( v > 1600 ) {
	run = true;
	v = 1;
} else {
	v = 0;
} 

if ( last_value[_channel-1] != v ) {
	if ( run ) {
		system("/etc/init.d/raspivid start");
		hal.console->printf("Running script start triggered by RC input %2u", _channel);
	} else {
		system("/etc/init.d/raspivid stop");
		hal.console->printf("Running script stop triggered by RC input %2u", _channel);
	}
	last_value[_channel-1] = v;
	hal.console->println();
}

}
#endif

Hello!
Does this script implies to recompile ArduCopter?

I am seeking for a script that could be triggered without modifying Ardupilot so that I can enjoy the upgrades in time…

Thanks for the infos !
Guillaume

yeah you’d have to recompile;
what do you want to achieve with your script?

I would like to launch a raspivid instance upon RC switch and kill it upon de-activation
In case I cannot avoid recompiling arducopter can you please detail a bit more how to proceed as i am not aware on code structure :slight_smile:

description for compiling is here: https://docs.emlid.com/navio2/Navio-APM/building-from-sources/

you can start a videostream upon boot of rpi too; and it stops when you close the video you are watching? I don’t see a big advantage of having an rc-switch for the video stream, or am I wrong?

You can use Dronekit. This is a preferred way to interact with a drone nowadays.

Something like this should help:

channels.py

from dronekit import connect, VehicleMode

import time

import argparse  
parser = argparse.ArgumentParser(description='Simple example how to interact with RC channels.')
parser.add_argument('--connect', 
                   help="vehicle connection target string.")
args = parser.parse_args()
connection_string = args.connect

print("\nConnecting to vehicle on: %s" % connection_string)
vehicle = connect(connection_string, wait_ready=True)

vehicle.wait_ready('autopilot_version')

from plumbum import local
#use plumbum to launch programs
raspivid = local["raspivid"]

@vehicle.on_attribute('channels')
def on_channels(self, attr_name, value):
	if value['1'] == 0:
		print(value['1'])
		#launch raspivid or whatever
		

while True:
	time.sleep(1)

sudo pip install plumbum dronekit
In one ssh-session:
sudo ArduCopter-quad -A udp:127.0.0.1:14650

In another:
python channels.py

The example above is rather simplistic but this is intentionally so. You can modify it and play around using extremely essential documentation on the topic that you can get by following the link.

2 Likes

It looks extremely powerful and interesting, George
Being still a noob for most of it, I might come back to you after my “in-depth” documentation reading and code debugging… :slight_smile:

However to save some “investigation” time could you let me know what would look your code like with the following raspivid command :
raspivid -n -w 1280 -h 720 -b 6000000 -fps 15 -t 0 -o -
| tee /pi/VideoRecord-$(date +%y%m%d-%H:%M:%S).h264
| gst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay config-interval=10 pt=96 ! udpsink host=192.168.200.100 port=9000

Launched upon activation of RC Channel 7 at above 1900 PWM

For info IP address of my RPi station is 192.168.200.1

Hope it’ll be ok for you to let me know
Thanks!

Hi George
After having spent some hours investigating (sorry as I am REALLY not qualified in python…), I finally found out your code works only with the following changes:

  • I have to launch Arducopter using primary telemetry for my GCS and secondary for my “companion computer” which is channels.py
    sudo nohup ArduCopter-quad -A udp:192.168.200.100:14550 -C udp:127.0.0.1:14550
  • I launch your code using the following command
    python channels.py --connect udp:127.0.0.1:14550

And it connects :slight_smile:
I hope I understood it well.

Now I still am stuck understanding the second part of your code… Would for sure appreciate some help if you could make it more detailed. I understand the “scanning” of the channels attribute but I don’t figure out the syntax to launch raspivid

Guillaume

I just suggested using plumbum library to launch raspivid as IMHO it makes clearer the intent of launching an external application.

You’re free to to use subprocess.call() or the like.

I finally managed to start raspivid using os.system command
My “new” issue is now that the number of channels in vehicule attributes is limited to 8… which i am all already using for gimbal control, flight modes, servo…
My rc tx and rx are capable of 10 channels in ppm do you have an idea what I can do ?
Guillaume