Skip to content

Controlling Servos Using a Lua Script

This page walks you through setting up your system so that you can control a servo from a Lua script running on the Cube. It assumes you can already move your servo from Mission Planner's Servo/Relay tab: if not, go back to Servos via Mission Planner first.

We'll use one of ArduPilot's own example scripts, servo_slew.lua. It moves a servo back and forth in a smooth sine wave, and it creates its own parameters so you can change how it moves from Mission Planner. The next page goes through how it works.

No arming needed

You don't need to arm the Cube for this. You do need to toggle the safety switch from the Ctrl+F menu, as on the previous page.

Why Lua?

ArduPilot and Mission Planner are pretty functional, but we're working a little 'off-design'. We're using a flight controller that expects to be, well, flying, and we're confusing it by not giving it a GPS signal, constraining some of its degrees of freedom in the tunnel, and asking it to do things that would probably be a bad idea in flight, such as positioning control surface servos directly by hand.

We can work around this using the built-in scripting functionality. The Cube supports Lua scripting (via ArduPilot), enabling onboard automation such as custom sensor logging, custom failsafes, LED behaviour, telemetry filtering, or mission logic, without modifying the firmware (which is a good idea for verification and certification of core flight functionality). Lua is a lightweight, high-level scripting language designed for embedded systems, which makes it ideal for writing custom logic directly on the Cube.

Don't be scared of Lua!

It's a strange programming language, but it's the only way to write onboard code for the Cube. You'll get used to it, don't worry.

ArduPilot's scripting pages:

Enabling scripting

  1. Connect Mission Planner, and open the full parameter list: the CONFIG tab, then Full Parameter List.
  2. Search for SCR_ENABLE and set it to 1.
  3. Write the parameters with Write Params.
  4. Reboot the Cube, with reboot pixhawk in the Ctrl+F menu or with a power cycle.

Rebooting with scripting enabled creates a scripts folder inside the APM folder on the Cube's SD card. If it doesn't appear, create it yourself.

Uploading the script

servo_slew.lua

The script, servo_slew.lua
-- move a servo in a sinusoidal fashion, with settable limits and frequency

local PARAM_TABLE_KEY = 135
local PARAM_TABLE_PREFIX = "STEST_"

-- bind a parameter to a variable
function bind_param(name)
   local p = Parameter()
   assert(p:init(name), string.format('could not find %s parameter', name))
   return p
end

-- add a parameter and bind it to a variable
function bind_add_param(name, idx, default_value)
   assert(param:add_param(PARAM_TABLE_KEY, idx, name, default_value), string.format('could not add param %s', name))
   return bind_param(PARAM_TABLE_PREFIX .. name)
end

-- setup script specific parameters
assert(param:add_table(PARAM_TABLE_KEY, PARAM_TABLE_PREFIX, 10), 'could not add param table')

local STEST_CHAN      = bind_add_param('CHAN',          1, 0)
local STEST_PCT       = bind_add_param('PCT',           2, 100)
local STEST_PWM_MIN   = bind_add_param('PWM_MIN',       3, 1000)
local STEST_PWM_MAX   = bind_add_param('PWM_MAX',       4, 2000)
local STEST_FREQ      = bind_add_param('FREQ',          5, 1)

function update()
   local chan = STEST_CHAN:get()
   local freq = STEST_FREQ:get()
   if chan > 0 and freq > 0 then
      local t = 0.001 * millis():tofloat()
      local pi = 3.1415
      local output = math.sin(pi * t * freq * 2.0) * STEST_PCT:get() * 0.01
      local pwm_min = STEST_PWM_MIN:get()
      local pwm_max = STEST_PWM_MAX:get()
      local pwm_mid = 0.5*(pwm_min+pwm_max)
      local pwm = math.floor(pwm_mid + (pwm_max-pwm_mid) * output)
      SRV_Channels:set_output_pwm_chan_timeout(chan-1, pwm, 100)
      --gcs:send_text(0, string.format("pwm=%u", pwm))
   end
   return update, 5 -- 200Hz
end

return update()
  1. Open MAVFtp: the CONFIG tab, then MAVFtp. This lets you see the files on the Cube's SD card.
  2. Open the scripts folder: open the top folder, which has no name, then APM, then scripts.

    MAVFtp MP

  3. Upload the script: right-click in scripts, choose Upload, and find servo_slew.lua on your laptop.

  4. Reboot the Cube. Scripts in APM/scripts start automatically every time the Cube boots.

Setting the script's parameters

When the script runs for the first time it creates five new parameters, all starting with STEST_. To see them, go back to Full Parameter List and click Refresh Params, then search for STEST.

Parameter Default What it does
STEST_CHAN 0 The servo output the script drives. 0 means off.
STEST_PCT 100 How far the servo swings, as a percentage of the full range.
STEST_PWM_MIN 1000 The PWM signal at one end of the swing.
STEST_PWM_MAX 2000 The PWM signal at the other end.
STEST_FREQ 1 How many full swings per second.

Set STEST_CHAN to 5, to match the MAIN OUT pins your servo is plugged into, and write the parameters. SERVO5_FUNCTION must still be 0, as on the previous page.

Mind the end stops

By default the script swings the servo across its full range. If your servo is fitted to a mechanism, that may drive it into the end of its travel and stall it. Reduce STEST_PCT, or bring STEST_PWM_MIN and STEST_PWM_MAX closer together, before you connect anything to the servo horn.

These parameters are saved on the Cube, so they survive reboots and edits to the script.

Watching it run

Toggle the safety switch: press Ctrl+F and click Toggle Saftey Switch. Your servo should now sweep back and forth, once a second.

Check the signal in Servo Output

SETUP › Mandatory Hardware › Servo Output shows the PWM signal on each output in its Position bar. With the default settings, output 5 should move through one full cycle between high and low every second.

Servo output MP

To stop the script moving the servo, set STEST_CHAN back to 0.

Troubleshooting

If the servo doesn't move, work through these in order.

  1. Check the servo moves from the Servo/Relay tab, as on the previous page. If it doesn't, the problem isn't the script: follow that page's troubleshooting.
  2. Check the script is running. Look in the Messages tab, in the bottom left of the DATA tab, for any message mentioning Lua. An error there names the script and the line it failed on.
  3. Check the STEST_ parameters exist. If they don't appear after Refresh Params, the script hasn't run: check SCR_ENABLE is 1, the file is in APM/scripts, and you've rebooted since uploading it.
  4. Check STEST_CHAN is 5 and SERVO5_FUNCTION is 0.
  5. Check the safety switch has been toggled since the last reboot.
  6. Check Servo Output, as above. If output 5 is changing but the servo isn't moving, check the servo's wiring and power.
  7. Try arming the Cube, as on the previous page.

Recap

  1. SCR_ENABLE = 1, write the parameters, and reboot.
  2. Upload servo_slew.lua to APM/scripts, and reboot.
  3. Refresh the parameters, set STEST_CHAN = 5, and write the parameters.
  4. Toggle the safety switch.
  5. If it isn't working, check Servo Output, then try arming.

Once your servo is moving under the script's control, head to the next page to see how the script works.