RC_Override - for 2nd RC remote control

Building a newer, 300kg mower.
Looking for an easy way to " Jog " the machine around the workshop without firing up the normal ground control stations, etc.
I have a simple (2nd) RC controller that looks the part - its 8 channel PWM (not ppm) output.

**Question - will the following scripting (with a bit of control logic) work for forward/backward control on our skid steer mower? any gotchas? ** - TIA

local RC4 = rc:get_channel(4)
function update()
   -- mirror RC1 onto RC4
   rc1_input = rc:get_pwm(1)
   return update, 10

from script Scripting/examples/RC_override.lua

You can use an RC transmitter and receiver without a GCS. I do so routinely for exactly the purpose you describe. You may find that you need to override some prearm checks to arm indoors.

Your script looks like it will constantly override any RC input…with RC input. I don’t think that’s really what you want or need. It will preclude normal operation if it works at all.

Highly recommend you use a better transmitter/receiver set than that thing. Trust me, you’ll be glad for it when operating a machine of that size and power.

yep - hear you on the opportunity for chaos and carnage. We’ll manage the safety aspect, its more how best to have a 2nd RC or even cabled joystick to jog the machine around. (very slowly). Our number 1 RC is " well better than that thing :slight_smile: "

Your script looks like it will constantly override any RC input…with RC input.

so a bit of code to turn the override on and off - or a switch on the mower. ? eg RC 1 or RC 2


You are adding a non-trivial amount of complexity in what appears to be a well intentioned but ill informed effort to…reduce complexity?

What is the aversion to using the primary RC transmitter?

I have a pending question on the dev Discord that, if answered the way I think it works (that would benefit you), would make your problem trivial, no matter my perception of impracticality. I will let you know how that turns out.

Actually, I think I found your answer without the need to await an answer on the chat.


What autopilot are you using? If it’s a Cube Orange or similar, the above article applies and makes your intended use case possible using native features.

I’m still rather confused by the need for it, but I’m only judging you slightly :smiley:

1 Like

Thanks - i found that link, the rc pictured is PWM only, l believe the doco is expecting PPM, but i have the simple - store on the mower, single stick RC controller in hand. I could get another ppm rx/tx as an option.

The flight controller - Qio-tek Zealot H7 controller is up to the task - scripting and 2nd ppm RC.

current primary RC (taranis/rfsky) is working well, and is multiplexed over RFD900x ppm via an " rc relay box " with RC receiver, RFD900 and ESP8266 for wifi" up and running. Mission planner goes via wifi to the rfd900 also. This is all good for long range comms, but a couple of steps too many for moving the mower around the shed, or just onto a trailer.

so im looking for a quick and easy (i’m responsible for the safe) way of shifting 300kg 1m forwards…
Thanks - your ideas (and slight judgements :slight_smile: ) apprecated.

Ok, that’s a lot of monkey motion for RC which might be avoidable via a different protocol. ExpressLRS has impressive range and may be an option to avoid some of that.

Regarding the linked docs, I have some bad news. As much as I like the ZealotH743, it lacks an IOMCU, so that solution will not work with your present architecture (ppm or not).

And as much as I advocate for scripting, driving a second RC receiver with Lua seems ill advised, especially when you intend to use it without a GCS or other situational awareness tools at hand. Certainly your present script will not do what you want (there is a provision to read a single PWM input that may also require an IOMCU, but it’s not via the RC bindings, and RC override isn’t the answer to produce output). There’s room for scripting in this discussion, but I think it should revolve around curating the proper prearm checks for the situation rather than directly overriding RC signals.

My honest opinion is to create this thing from the ground up using proper, in place features. The only way to do that is to replace the autopilot with one that contains an IOMCU (or choose a different RC scheme that avoids all of that infrastructure). The Cube Orange/Orange+ will certainly work. CUAV makes a few nice options, but they are higher priced. Holybro makes the Pixhawk 6C which, when purchased without upgrades, competes well with the Zealot on price.

If you want to just overhaul the RC system, ExpressLRS will afford fairly long range, even over 2.4GHz. In fact, the 915MHz ELRS hardware was so sparse and sometimes poorly architected a year or two ago that I really didn’t notice any appreciable range difference between 915MHz vs 2.4GHz when testing over the ground, despite the science saying I should see a significant improvement at the lower frequency band. That may have changed since I last tested, but I shy away from ExpressLRS 915MHz hardware after that experience.

Or go caveman. Tie an onboard 3-way switch to a couple of GPIO pins and use the scripting button features to override prearm checks and output a low-value servo output when toggled physically. RC overrides are almost always NOT the answer. And you won’t be doing any steering with a 3 way switch tied to scripted forward/reverse motor outputs.

EDIT: But hold all of these thoughts for now and certainly don’t spend any money until I get back to you with a few more answers.

I stand by all of my recommendations above. But I think I have a scripted solution for you (what better way to spend a Friday night than bench testing autopilot features… :D). I do have a beer in hand.

Give me a few more minutes to see what I can achieve.

1 Like

This is not my favorite script, but it will do what you want if you connect your single RC channel output to main out 12 of the ZealotH743. I chose output 12 because it is somewhat unlikely to overlap timer groups in use for other functions.

The script is fairly well commented so you can see what’s happening. I considered trying to override all the prearm checks, but that became a parameter control nightmare, and there are some failsafes that almost always preclude throttle output - a situation you are likely to encounter indoors without the primary RC system involved.

While you can leave it as is, that’s probably unwise. You should set up your radio failsafe actions and values and then monitor for valid RC input within the update loop. Use valid RC input as a means to set the enable_jog_control variable to false, which should preclude jog control when the primary RC handset is in use.

If you have to reverse one of the throttle channels, this script will make your robot spin in circles. You’ll need to do some math in the script to get reversed output. I did not account for that. I’m also assuming that you’re using a skid steer setup. It’s easy enough to modify the script for a single throttle output if you need to do that.

You might also consider limiting the min and max output PWM values, since the script will allow a pretty wide range of output (essentially all the way up to full forward and reverse throttle).

I can confirm that this script will move the Rover forward and backward with nearly every failsafe imaginable active, since I tested it on a live, disarmed ZealotH743 with no GPS, no RC system, and wildly out of calibration.

As written, there is a deadband of +/- 50µs around 1500µs, which is the usual neutral point of an RC channel. You can modify the values to suit your RC remote as needed. If the jog channel remains within the deadband for a half second or more, normal throttle function is returned.

PLEASE test this thoroughly with the engine off and/or vehicle wheels off the ground before you trust it to move your big, expensive toy!

You would be safer and better off using one of my recommendations above rather than this script. Using an autopilot with an IOMCU and a true second RC system would provide a ton more flexibility than this simple, hacky forward/reverse scheme.


rover-jog-disarmed ArduPilot Lua script

Extremely hacky script to allow moving a rover by directly influencing
servo output while disarmed via PWM input.

CAUTION: This script is capable of engaging and disengaging autonomous control
of a vehicle.  Use this script AT YOUR OWN RISK.

local RUN_INTERVAL_MS = 25
local DEADBAND = 50
local IDLE_TIMEOUT = 500

local pwm_in = PWMSource()
local throttle_left_ch = assert(SRV_Channels:find_channel(73), 'ThrottleLeft not configured')
local throttle_right_ch = assert(SRV_Channels:find_channel(74), 'ThrottleRight not configured')
local servo_left_fn = Parameter(('SERVO%d_FUNCTION'):format(throttle_left_ch + 1))
local servo_right_fn = Parameter(('SERVO%d_FUNCTION'):format(throttle_right_ch + 1))
local timeout = uint32_t(0)
local is_active = false

if not pwm_in:set_pin(61) then -- Pin 12
    gcs:send_text(0, 'Failed to configure input on PWM 12')

-- temporarily use scripted servo functions to allow movement regardless of arming state
local function take_control()
    is_active = true
    gcs:send_text(5, 'Jog active')

-- return servo functions to throttle left/right
local function release_control()
    is_active = false
    gcs:send_text(5, 'Released jog control')

function update()
    -- TODO: user should implement a way to set enable_jog_control based on radio failsafe indications
    local enable_jog_control = true

    -- if jog control is disabled, release control and immediately return
    if not enable_jog_control then
        if is_active then
        return update, RUN_INTERVAL_MS

    local pwm_value = pwm_in:get_pwm_us()

    local is_pwm_valid = pwm_value > 900 and pwm_value < 2100

    -- if pwm value is out of range, release control and immediately return
    if not is_pwm_valid then
        if is_active then
        return update, RUN_INTERVAL_MS

    -- if pwm value is in range and outside deadband, take jog control
    if pwm_value < (1500 - DEADBAND) or pwm_value > (1500 + DEADBAND) then
        if not is_active then
        SRV_Channels:set_output_pwm_chan(throttle_left_ch, pwm_value)
        SRV_Channels:set_output_pwm_chan(throttle_right_ch, pwm_value)
        timeout = millis()

    -- if pwm value is in range and within deadband, release control
    if is_active and millis() - timeout > IDLE_TIMEOUT then

    gcs:send_named_float('JOG', pwm_value)
    return update, RUN_INTERVAL_MS

gcs:send_text(6, 'Jog script loaded')
return update()
1 Like

wow - thanks - excellent. Apols i snatched a few minutes between building things and shooting things and saw your reply.

I hear you on the - do it right 1st time logic. Interesting on the IOMCU not on the zealot. thanks.

Clear on the safety messages. Im basically terrified of this robot all of the time, its currently up on blocks as it gets its new brain installed, which is perfect for testing large machinery.

I’ll go through the code, and have a play - should be fun. The fail modes (as in the opportunity space for surprising events) is large with this idea/jog/script. Its a work in progress.

will come back with some feedback to you and the group. Somewhere between awesome and too scary !

Yep - skid steer rover, im good to figure out directions and translations as required