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):
- Servo flick left then right (dump bait).
- Continue straight for 5 seconds.
- Turn 90° left relative to current heading toward home vector (avoid running over fishing lines).
- Drive straight ~30 seconds.
- 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())