LUA Script for Bait Boat Mission (ArduRover - Boat)

Bait Boat Waypoint Capture, Dual Missions, and Custom Failsafe (ArduPilot)

Hi all, I’d appreciate a review of a LUA script I’m developing for a bait boat running on ArduPilot. The intended behavior is:

Waypoint Capture (Manual Mode)

  • Boat is driven manually to locations.
  • Aileron stick left → actuates left bait bucket servo and saves Waypoint 1.
  • Aileron stick right → actuates right bait bucket servo and saves Waypoint 2.
  • Boat is then driven to a third point → saves Waypoint 3.
  • Note: By the time WP3 is saved, both bait buckets have already been dropped, so saving WP3 can be triggered by either left or right servo input (universal trigger).
  • Home is already defined (Waypoint 0).
  • Mission sequence becomes: Home → WP1 → WP2 → WP3 → Home.

Auto Run

  • Auto mode executes the stored mission, performing the same servo actions at the relevant waypoints.

Persistence

  • Missions must be saved to the SD card so they are not lost after power cycling.

Dual Mission Selection

  • A switch/knob position selects between Mission A and Mission B (two independent stored missions on SD).

Custom Failsafe (RC Loss Early Mission)
If RC is lost before WP2 (before bait drop complete):

  1. Servo flick left then right (dump bait).
  2. Continue straight for 5 seconds.
  3. Turn 90° left relative to current heading toward home vector (avoid running over fishing lines).
  4. Drive straight ~30 seconds.
  5. Return to Home.

Please note that I do not intend on using any Mission Planner at the lake due to logistics.

Hoping I can rin this from my Transmitter only (FlySky FS-i6x).

See LUA script below.

Any feedback on logic, reliability, SD persistence handling, or better approaches within Lua/mission APIs would be greatly appreciated.

Thank you

-- ArduRover Bait Boat "Headless" Mission 
local RC_WP      = 3   -- Aileron
local RC_CLEAR   = 9   -- Safety Knob (1800-1900)
local RC_SELECT  = 10  -- Selector (Left=A, Right=B)
local SERVO_OUT  = 3

local MODE_MANUAL = 0
local MODE_AUTO   = 10
local MODE_RTL    = 11
local MODE_GUIDED = 15

local missions = { A = {}, B = {} }
local last_wp_pwm = 1500
local clear_timer = 0
local home_pos = nil
local fs_active = false
local active_slot = "NONE"

-- 1. FILE SYSTEM: Persistent Storage (Absolute Paths)
function load_from_sd(slot)
    local file = io.open("/APM/scripts/mission_" .. slot .. ".txt", "r")
    if file then
        for line in file:lines() do
            local lat, lng = line:match("([^,]+),([^,]+)")
            if lat and lng then table.insert(missions[slot], {lat=tonumber(lat), lng=tonumber(lng)}) end
        end
        file:close()
    end
end

function save_to_sd(slot)
    local file = io.open("/APM/scripts/mission_" .. slot .. ".txt", "w")
    if file then
        for _, v in ipairs(missions[slot]) do file:write(v.lat .. "," .. v.lng .. "\n") end
        file:close()
    end
end

load_from_sd("A")
load_from_sd("B")

function update()
    local wp_pwm     = rc:get_pwm(RC_WP)
    local select_pwm = rc:get_pwm(RC_SELECT)
    local mode       = vehicle:get_mode()
    
    -- Home Sync on Arming (Required for Failsafe Vector)
    if arming:is_armed() and not home_pos then 
        home_pos = ahrs:get_home() 
    elseif not arming:is_armed() then 
        home_pos = nil 
    end

    local slot = (select_pwm < 1300) and "A" or (select_pwm > 1700 and "B" or nil)

    -- 2. FAILSAFE: RC Loss in Auto (Guided Mode Handover)
    if not rc:has_valid_input() and mode == MODE_AUTO and not fs_active then
        local current_wp = mission:get_current_nav_index()
        -- Trigger only if before/at WP2 (Index 1 or 2)
        if current_wp > 0 and current_wp <= 2 and home_pos then
            fs_active = true
            vehicle:set_mode(MODE_GUIDED) -- Script takes control of speed/heading
            
            -- Servo Flick (Left then Right)
            SRV_Channels:set_output_pwm_chan_timeout(SERVO_OUT, 1000, 500)
            update:delay_cb(600, function() SRV_Channels:set_output_pwm_chan_timeout(SERVO_OUT, 2000, 600) end)
            
            -- Maintain Speed for 5s
            vehicle:set_target_throttle_speed(vehicle:get_groundspeed())




Looks like the script is not pasting in full on my post??

I have attached a .txt file with the full script

Apologies

LUA_Script_Bait_Boat.txt (4.9 KB)