diff --git a/src/drivers/drv_pwm_output.h b/src/drivers/drv_pwm_output.h
index cfc02c8fb61ef657e1d87b6f3659e3b05df7e569..b0e9218afb6c3f9bfc2231ffa2fb387b720998c2 100644
--- a/src/drivers/drv_pwm_output.h
+++ b/src/drivers/drv_pwm_output.h
@@ -305,6 +305,9 @@ struct pwm_output_rc_config {
  * This is the low-level API to the platform-specific PWM driver.
  */
 
+__EXPORT extern void up_pwm_set_oneshot_mode(bool on);
+__EXPORT extern bool up_pwm_get_oneshot_mode(void);
+
 /**
  * Intialise the PWM servo outputs using the specified configuration.
  *
@@ -360,6 +363,13 @@ __EXPORT extern uint32_t up_pwm_servo_get_rate_group(unsigned group);
  */
 __EXPORT extern int	up_pwm_servo_set_rate_group_update(unsigned group, unsigned rate);
 
+/**
+ * Force update of all timer channels in group.
+ *
+ * @param group		The rate group to update.
+ */
+__EXPORT extern void up_pwm_force_update(void);
+
 /**
  * Set the current output value for a channel.
  *
diff --git a/src/drivers/px4fmu/fmu.cpp b/src/drivers/px4fmu/fmu.cpp
index e4f41375230a3ec43ac97963de3cf30073b3638c..9aae69f5ac3b9494736970f8cf04c9b467fc39bc 100644
--- a/src/drivers/px4fmu/fmu.cpp
+++ b/src/drivers/px4fmu/fmu.cpp
@@ -72,6 +72,7 @@
 #include <systemlib/board_serial.h>
 #include <systemlib/param/param.h>
 #include <systemlib/perf_counter.h>
+#include <systemlib/scheduling_priorities.h>
 #include <drivers/drv_mixer.h>
 #include <drivers/drv_rc_input.h>
 #include <drivers/drv_input_capture.h>
@@ -187,7 +188,7 @@ private:
 	unsigned	_pwm_alt_rate;
 	uint32_t	_pwm_alt_rate_channels;
 	unsigned	_current_update_rate;
-	struct work_s	_work;
+	int 		_task;
 	int		_vehicle_cmd_sub;
 	int		_armed_sub;
 	int		_param_sub;
@@ -199,9 +200,13 @@ private:
 	orb_advert_t	_outputs_pub;
 	unsigned	_num_outputs;
 	int		_class_instance;
+	volatile bool	_task_should_exit;
 	int		_rcs_fd;
 	uint8_t _rcs_buf[SBUS_BUFFER_SIZE];
 
+	static void	task_main_trampoline(int argc, char *argv[]);
+	void		task_main();
+
 	volatile bool	_initialized;
 	bool		_throttle_armed;
 	bool		_pwm_on;
@@ -248,7 +253,6 @@ private:
 
 	static void	cycle_trampoline(void *arg);
 	void		cycle();
-	void		work_start();
 	void		work_stop();
 
 	static int	control_callback(uintptr_t handle,
@@ -258,8 +262,8 @@ private:
 	void		capture_callback(uint32_t chan_index,
 					 hrt_abstime edge_time, uint32_t edge_state, uint32_t overflow);
 	void		subscribe();
-	int		set_pwm_rate(unsigned rate_map, unsigned default_rate, unsigned alt_rate);
-	int		pwm_ioctl(file *filp, int cmd, unsigned long arg);
+	int			set_pwm_rate(unsigned rate_map, unsigned default_rate, unsigned alt_rate);
+	int			pwm_ioctl(file *filp, int cmd, unsigned long arg);
 	void		update_pwm_rev_mask();
 	void		publish_pwm_outputs(uint16_t *values, size_t numvalues);
 	void		update_pwm_out_state(bool on);
@@ -322,7 +326,7 @@ PX4FMU::PX4FMU() :
 	_pwm_alt_rate(50),
 	_pwm_alt_rate_channels(0),
 	_current_update_rate(0),
-	_work{},
+	_task(-1),
 	_vehicle_cmd_sub(-1),
 	_armed_sub(-1),
 	_param_sub(-1),
@@ -334,6 +338,7 @@ PX4FMU::PX4FMU() :
 	_outputs_pub(nullptr),
 	_num_outputs(0),
 	_class_instance(0),
+	_task_should_exit(false),
 	_rcs_fd(-1),
 	_initialized(false),
 	_throttle_armed(false),
@@ -457,11 +462,32 @@ PX4FMU::init()
 
 	_safety_disabled = circuit_breaker_enabled("CBRK_IO_SAFETY", CBRK_IO_SAFETY_KEY);
 
-	work_start();
+	/* start the IO interface task */
+	_task = px4_task_spawn_cmd("fmuservo",
+				   SCHED_DEFAULT,
+				   SCHED_PRIORITY_FAST_DRIVER - 1,
+				   1200,
+				   (main_t)&PX4FMU::task_main_trampoline,
+				   nullptr);
+
+	/* wait until the task is up and running or has failed */
+	while (_task > 0 && _task_should_exit) {
+		usleep(100);
+	}
+
+	if (_task < 0) {
+		return -PX4_ERROR;
+	}
 
 	return OK;
 }
 
+void
+PX4FMU::task_main_trampoline(int argc, char *argv[])
+{
+	g_fmu->task_main();
+}
+
 void
 PX4FMU::safety_check_button(void)
 {
@@ -835,13 +861,6 @@ PX4FMU::publish_pwm_outputs(uint16_t *values, size_t numvalues)
 }
 
 
-void
-PX4FMU::work_start()
-{
-	/* schedule a cycle to start things */
-	work_queue(HPWORK, &_work, (worker_t)&PX4FMU::cycle_trampoline, this, 0);
-}
-
 void
 PX4FMU::cycle_trampoline(void *arg)
 {
@@ -965,6 +984,7 @@ void
 PX4FMU::update_pwm_out_state(bool on)
 {
 	if (on && !_pwm_initialized && _pwm_mask != 0) {
+		up_pwm_set_oneshot_mode(true);
 		up_pwm_servo_init(_pwm_mask);
 		set_pwm_rate(_pwm_alt_rate_channels, _pwm_default_rate, _pwm_alt_rate);
 		_pwm_initialized = true;
@@ -974,669 +994,672 @@ PX4FMU::update_pwm_out_state(bool on)
 }
 
 void
-PX4FMU::cycle()
+PX4FMU::task_main()
 {
-	if (!_initialized) {
-		/* force a reset of the update rate */
-		_current_update_rate = 0;
+	while (!_task_should_exit) {
 
-		_armed_sub = orb_subscribe(ORB_ID(actuator_armed));
-		_param_sub = orb_subscribe(ORB_ID(parameter_update));
-		_adc_sub = orb_subscribe(ORB_ID(adc_report));
+		if (!_initialized) {
+			/* force a reset of the update rate */
+			_current_update_rate = 0;
 
-		/* initialize PWM limit lib */
-		pwm_limit_init(&_pwm_limit);
+			_armed_sub = orb_subscribe(ORB_ID(actuator_armed));
+			_param_sub = orb_subscribe(ORB_ID(parameter_update));
+			_adc_sub = orb_subscribe(ORB_ID(adc_report));
 
-		update_pwm_rev_mask();
+			/* initialize PWM limit lib */
+			pwm_limit_init(&_pwm_limit);
+
+			update_pwm_rev_mask();
 
 #ifdef RC_SERIAL_PORT
 
 #ifdef RF_RADIO_POWER_CONTROL
-		// power radio on
-		RF_RADIO_POWER_CONTROL(true);
+			// power radio on
+			RF_RADIO_POWER_CONTROL(true);
 #endif
-		_vehicle_cmd_sub = orb_subscribe(ORB_ID(vehicle_command));
-		// dsm_init sets some file static variables and returns a file descriptor
-		_rcs_fd = dsm_init(RC_SERIAL_PORT);
-		// assume SBUS input
-		sbus_config(_rcs_fd, false);
+			_vehicle_cmd_sub = orb_subscribe(ORB_ID(vehicle_command));
+			// dsm_init sets some file static variables and returns a file descriptor
+			_rcs_fd = dsm_init(RC_SERIAL_PORT);
+			// assume SBUS input
+			sbus_config(_rcs_fd, false);
 #ifdef GPIO_PPM_IN
-		// disable CPPM input by mapping it away from the timer capture input
-		px4_arch_unconfiggpio(GPIO_PPM_IN);
+			// disable CPPM input by mapping it away from the timer capture input
+			px4_arch_unconfiggpio(GPIO_PPM_IN);
 #endif
 #endif
 
-		param_find("MOT_SLEW_MAX");
-		param_find("THR_MDL_FAC");
+			param_find("MOT_SLEW_MAX");
+			param_find("THR_MDL_FAC");
 
-		for (unsigned i = 0; i < _max_actuators; i++) {
-			char pname[16];
-			sprintf(pname, "PWM_AUX_TRIM%d", i + 1);
-			param_find(pname);
-		}
+			for (unsigned i = 0; i < _max_actuators; i++) {
+				char pname[16];
+				sprintf(pname, "PWM_AUX_TRIM%d", i + 1);
+				param_find(pname);
+			}
 
-		_initialized = true;
-	}
+			_initialized = true;
+		}
 
-	if (_groups_subscribed != _groups_required) {
-		subscribe();
-		_groups_subscribed = _groups_required;
-		/* force setting update rate */
-		_current_update_rate = 0;
-	}
+		if (_groups_subscribed != _groups_required) {
+			subscribe();
+			_groups_subscribed = _groups_required;
+			/* force setting update rate */
+			_current_update_rate = 0;
+		}
 
-	/*
-	 * Adjust actuator topic update rate to keep up with
-	 * the highest servo update rate configured.
-	 *
-	 * We always mix at max rate; some channels may update slower.
-	 */
-	unsigned max_rate = (_pwm_default_rate > _pwm_alt_rate) ? _pwm_default_rate : _pwm_alt_rate;
+		/*
+		 * Adjust actuator topic update rate to keep up with
+		 * the highest servo update rate configured.
+		 *
+		 * We always mix at max rate; some channels may update slower.
+		 */
+		unsigned max_rate = (_pwm_default_rate > _pwm_alt_rate) ? _pwm_default_rate : _pwm_alt_rate;
 
-	if (_current_update_rate != max_rate) {
-		_current_update_rate = max_rate;
-		int update_rate_in_ms = int(1000 / _current_update_rate);
+		if (_current_update_rate != max_rate) {
+			_current_update_rate = max_rate;
+			int update_rate_in_ms = int(1000 / _current_update_rate);
 
-		/* reject faster than 500 Hz updates */
-		if (update_rate_in_ms < 2) {
-			update_rate_in_ms = 2;
-		}
+			/* reject faster than 500 Hz updates */
+			if (update_rate_in_ms < 2) {
+				update_rate_in_ms = 2;
+			}
 
-		/* reject slower than 10 Hz updates */
-		if (update_rate_in_ms > 100) {
-			update_rate_in_ms = 100;
-		}
+			/* reject slower than 10 Hz updates */
+			if (update_rate_in_ms > 100) {
+				update_rate_in_ms = 100;
+			}
 
-		PX4_DEBUG("adjusted actuator update interval to %ums", update_rate_in_ms);
+			PX4_DEBUG("adjusted actuator update interval to %ums", update_rate_in_ms);
 
-		for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
-			if (_control_subs[i] > 0) {
-				orb_set_interval(_control_subs[i], update_rate_in_ms);
+			for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
+				if (_control_subs[i] > 0) {
+					orb_set_interval(_control_subs[i], update_rate_in_ms);
+				}
 			}
-		}
 
-		// set to current max rate, even if we are actually checking slower/faster
-		_current_update_rate = max_rate;
-	}
+			// set to current max rate, even if we are actually checking slower/faster
+			_current_update_rate = max_rate;
+		}
 
-	/* check if anything updated */
-	int ret = ::poll(_poll_fds, _poll_fds_num, 0);
+		/* wait for an update */
+		int ret = ::poll(_poll_fds, _poll_fds_num, 20);
 
-	/* this would be bad... */
-	if (ret < 0) {
-		DEVICE_LOG("poll error %d", errno);
+		/* this would be bad... */
+		if (ret < 0) {
+			DEVICE_LOG("poll error %d", errno);
 
-	} else if (ret == 0) {
-		/* timeout: no control data, switch to failsafe values */
+		} else if (ret == 0) {
+			/* timeout: no control data, switch to failsafe values */
 //			warnx("no PWM: failsafe");
 
-	} else {
-		perf_begin(_ctl_latency);
+		} else {
+			perf_begin(_ctl_latency);
 
-		/* get controls for required topics */
-		unsigned poll_id = 0;
+			/* get controls for required topics */
+			unsigned poll_id = 0;
+			unsigned n_updates = 0;
 
-		for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
-			if (_control_subs[i] > 0) {
-				if (_poll_fds[poll_id].revents & POLLIN) {
-					orb_copy(_control_topics[i], _control_subs[i], &_controls[i]);
+			for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
+				if (_control_subs[i] > 0) {
+					if (_poll_fds[poll_id].revents & POLLIN) {
+						if (i == 0) {
+							n_updates++;
+						}
+
+						orb_copy(_control_topics[i], _control_subs[i], &_controls[i]);
 
 #if defined(DEBUG_BUILD)
 
-					static int main_out_latency = 0;
-					static int sum_latency = 0;
-					static uint64_t last_cycle_time = 0;
+						static int main_out_latency = 0;
+						static int sum_latency = 0;
+						static uint64_t last_cycle_time = 0;
 
-					if (i == 0) {
-						uint64_t now = hrt_absolute_time();
-						uint64_t latency = now - _controls[i].timestamp;
+						if (i == 0) {
+							uint64_t now = hrt_absolute_time();
+							uint64_t latency = now - _controls[i].timestamp;
 
-						if (latency > main_out_latency) { main_out_latency = latency; }
+							if (latency > main_out_latency) { main_out_latency = latency; }
 
-						sum_latency += latency;
+							sum_latency += latency;
 
-						if ((now - last_cycle_time) >= 1000000) {
-							last_cycle_time = now;
-							PX4_DEBUG("pwm max latency: %d, avg: %5.3f", main_out_latency, (double)(sum_latency / 100.0));
-							main_out_latency = latency;
-							sum_latency = 0;
+							if ((now - last_cycle_time) >= 1000000) {
+								last_cycle_time = now;
+								PX4_DEBUG("pwm max latency: %d, avg: %5.3f", main_out_latency, (double)(sum_latency / 100.0));
+								main_out_latency = latency;
+								sum_latency = 0;
+							}
 						}
-					}
 
 #endif
-				}
+					}
 
-				poll_id++;
-			}
+					poll_id++;
+				}
 
-			/* During ESC calibration, we overwrite the throttle value. */
-			if (i == 0 && _armed.in_esc_calibration_mode) {
+				/* During ESC calibration, we overwrite the throttle value. */
+				if (i == 0 && _armed.in_esc_calibration_mode) {
 
-				/* Set all controls to 0 */
-				memset(&_controls[i], 0, sizeof(_controls[i]));
+					/* Set all controls to 0 */
+					memset(&_controls[i], 0, sizeof(_controls[i]));
 
-				/* except thrust to maximum. */
-				_controls[i].control[3] = 1.0f;
+					/* except thrust to maximum. */
+					_controls[i].control[3] = 1.0f;
 
-				/* Switch off the PWM limit ramp for the calibration. */
-				_pwm_limit.state = PWM_LIMIT_STATE_ON;
+					/* Switch off the PWM limit ramp for the calibration. */
+					_pwm_limit.state = PWM_LIMIT_STATE_ON;
+				}
 			}
-		}
-	} // poll_fds
 
-	/* run the mixers on every cycle */
-	{
-		/* can we mix? */
-		if (_mixers != nullptr) {
+			// only mix if we have a new actuator_controls message
+			if ((n_updates > 0) && (_mixers != nullptr)) {
 
-			size_t num_outputs;
+				size_t num_outputs;
 
-			switch (_mode) {
-			case MODE_1PWM:
-				num_outputs = 1;
-				break;
+				switch (_mode) {
+				case MODE_1PWM:
+					num_outputs = 1;
+					break;
 
-			case MODE_2PWM:
-			case MODE_2PWM2CAP:
-				num_outputs = 2;
-				break;
+				case MODE_2PWM:
+				case MODE_2PWM2CAP:
+					num_outputs = 2;
+					break;
 
-			case MODE_3PWM:
-			case MODE_3PWM1CAP:
-				num_outputs = 3;
-				break;
+				case MODE_3PWM:
+				case MODE_3PWM1CAP:
+					num_outputs = 3;
+					break;
 
-			case MODE_4PWM:
-				num_outputs = 4;
-				break;
+				case MODE_4PWM:
+					num_outputs = 4;
+					break;
 
-			case MODE_6PWM:
-				num_outputs = 6;
-				break;
+				case MODE_6PWM:
+					num_outputs = 6;
+					break;
 
-			case MODE_8PWM:
-				num_outputs = 8;
-				break;
+				case MODE_8PWM:
+					num_outputs = 8;
+					break;
 
-			default:
-				num_outputs = 0;
-				break;
-			}
+				default:
+					num_outputs = 0;
+					break;
+				}
 
-			hrt_abstime now = hrt_absolute_time();
-			float dt = (now - _time_last_mix) / 1e6f;
-			_time_last_mix = now;
+				hrt_abstime now = hrt_absolute_time();
+				float dt = (now - _time_last_mix) / 1e6f;
+				_time_last_mix = now;
 
-			if (dt < 0.0001f) {
-				dt = 0.0001f;
+				if (dt < 0.0001f) {
+					dt = 0.0001f;
 
-			} else if (dt > 0.02f) {
-				dt = 0.02f;
-			}
+				} else if (dt > 0.02f) {
+					dt = 0.02f;
+				}
 
-			if (_mot_t_max > FLT_EPSILON) {
-				// maximum value the ouputs of the multirotor mixer are allowed to change in this cycle
-				// factor 2 is needed because actuator ouputs are in the range [-1,1]
-				float delta_out_max = 2.0f * 1000.0f * dt / (_max_pwm[0] - _min_pwm[0]) / _mot_t_max;
-				_mixers->set_max_delta_out_once(delta_out_max);
-			}
+				if (_mot_t_max > FLT_EPSILON) {
+					// maximum value the ouputs of the multirotor mixer are allowed to change in this cycle
+					// factor 2 is needed because actuator ouputs are in the range [-1,1]
+					float delta_out_max = 2.0f * 1000.0f * dt / (_max_pwm[0] - _min_pwm[0]) / _mot_t_max;
+					_mixers->set_max_delta_out_once(delta_out_max);
+				}
 
-			if (_thr_mdl_fac > FLT_EPSILON) {
-				_mixers->set_thrust_factor(_thr_mdl_fac);
-			}
+				if (_thr_mdl_fac > FLT_EPSILON) {
+					_mixers->set_thrust_factor(_thr_mdl_fac);
+				}
 
-			/* do mixing */
-			float outputs[_max_actuators];
-			num_outputs = _mixers->mix(outputs, num_outputs, NULL);
+				/* do mixing */
+				float outputs[_max_actuators];
+				num_outputs = _mixers->mix(outputs, num_outputs, NULL);
 
-			/* publish mixer status */
-			multirotor_motor_limits_s multirotor_motor_limits = {};
-			multirotor_motor_limits.saturation_status = _mixers->get_saturation_status();
+				/* publish mixer status */
+				multirotor_motor_limits_s multirotor_motor_limits = {};
+				multirotor_motor_limits.saturation_status = _mixers->get_saturation_status();
 
-			if (_to_mixer_status == nullptr) {
-				/* publish limits */
-				int instance = _class_instance;
-				_to_mixer_status = orb_advertise_multi(ORB_ID(multirotor_motor_limits), &multirotor_motor_limits, &instance,
-								       ORB_PRIO_DEFAULT);
+				if (_to_mixer_status == nullptr) {
+					/* publish limits */
+					int instance = _class_instance;
+					_to_mixer_status = orb_advertise_multi(ORB_ID(multirotor_motor_limits), &multirotor_motor_limits, &instance,
+									       ORB_PRIO_DEFAULT);
 
-			} else {
-				orb_publish(ORB_ID(multirotor_motor_limits), _to_mixer_status, &multirotor_motor_limits);
+				} else {
+					orb_publish(ORB_ID(multirotor_motor_limits), _to_mixer_status, &multirotor_motor_limits);
 
-			}
+				}
 
-			/* disable unused ports by setting their output to NaN */
-			for (size_t i = 0; i < sizeof(outputs) / sizeof(outputs[0]); i++) {
-				if (i >= num_outputs) {
-					outputs[i] = NAN_VALUE;
+				/* disable unused ports by setting their output to NaN */
+				for (size_t i = 0; i < sizeof(outputs) / sizeof(outputs[0]); i++) {
+					if (i >= num_outputs) {
+						outputs[i] = NAN_VALUE;
+					}
 				}
-			}
 
-			uint16_t pwm_limited[_max_actuators];
+				uint16_t pwm_limited[_max_actuators];
 
-			/* the PWM limit call takes care of out of band errors, NaN and constrains */
-			pwm_limit_calc(_throttle_armed, arm_nothrottle(), num_outputs, _reverse_pwm_mask,
-				       _disarmed_pwm, _min_pwm, _max_pwm, outputs, pwm_limited, &_pwm_limit);
+				/* the PWM limit call takes care of out of band errors, NaN and constrains */
+				pwm_limit_calc(_throttle_armed, arm_nothrottle(), num_outputs, _reverse_pwm_mask,
+					       _disarmed_pwm, _min_pwm, _max_pwm, outputs, pwm_limited, &_pwm_limit);
 
 
-			/* overwrite outputs in case of force_failsafe with _failsafe_pwm PWM values */
-			if (_armed.force_failsafe) {
-				for (size_t i = 0; i < num_outputs; i++) {
-					pwm_limited[i] = _failsafe_pwm[i];
+				/* overwrite outputs in case of force_failsafe with _failsafe_pwm PWM values */
+				if (_armed.force_failsafe) {
+					for (size_t i = 0; i < num_outputs; i++) {
+						pwm_limited[i] = _failsafe_pwm[i];
+					}
 				}
-			}
 
-			/* overwrite outputs in case of lockdown with disarmed PWM values */
-			if (_armed.lockdown || _armed.manual_lockdown) {
+				/* overwrite outputs in case of lockdown with disarmed PWM values */
+				if (_armed.lockdown || _armed.manual_lockdown) {
+					for (size_t i = 0; i < num_outputs; i++) {
+						pwm_limited[i] = _disarmed_pwm[i];
+					}
+				}
+
+				/* output to the servos */
 				for (size_t i = 0; i < num_outputs; i++) {
-					pwm_limited[i] = _disarmed_pwm[i];
+					pwm_output_set(i, pwm_limited[i]);
 				}
-			}
 
-			/* output to the servos */
-			for (size_t i = 0; i < num_outputs; i++) {
-				pwm_output_set(i, pwm_limited[i]);
-			}
+				// force an update if in oneshot mode
+				if (up_pwm_get_oneshot_mode()) {
+					up_pwm_force_update();
+				}
 
-			publish_pwm_outputs(pwm_limited, num_outputs);
-			perf_end(_ctl_latency);
-		}
-	}
-//	} // poll_fds
+				publish_pwm_outputs(pwm_limited, num_outputs);
+				perf_end(_ctl_latency);
+			} // new actuator_controls message
+
+		} // poll_fds
 
-	_cycle_timestamp = hrt_absolute_time();
+		_cycle_timestamp = hrt_absolute_time();
 
 #ifdef GPIO_BTN_SAFETY
 
-	if (_cycle_timestamp - _last_safety_check >= (unsigned int)1e5) {
-		_last_safety_check = _cycle_timestamp;
+		if (_cycle_timestamp - _last_safety_check >= (unsigned int)1e5) {
+			_last_safety_check = _cycle_timestamp;
 
-		/**
-		 * Get and handle the safety status at 10Hz
-		 */
-		struct safety_s safety = {};
+			/**
+			 * Get and handle the safety status at 10Hz
+			 */
+			struct safety_s safety = {};
 
-		if (_safety_disabled) {
-			_safety_off = true;
+			if (_safety_disabled) {
+				_safety_off = true;
 
-		} else {
-			/* read safety switch input and control safety switch LED at 10Hz */
-			safety_check_button();
-		}
+			} else {
+				/* read safety switch input and control safety switch LED at 10Hz */
+				safety_check_button();
+			}
 
-		/* Make the safety button flash anyway, no matter if it's used or not. */
-		flash_safety_button();
+			/* Make the safety button flash anyway, no matter if it's used or not. */
+			flash_safety_button();
 
-		safety.timestamp = hrt_absolute_time();
+			safety.timestamp = hrt_absolute_time();
 
-		if (_safety_off) {
-			safety.safety_off = true;
-			safety.safety_switch_available = true;
+			if (_safety_off) {
+				safety.safety_off = true;
+				safety.safety_switch_available = true;
 
-		} else {
-			safety.safety_off = false;
-			safety.safety_switch_available = true;
-		}
+			} else {
+				safety.safety_off = false;
+				safety.safety_switch_available = true;
+			}
 
-		/* lazily publish the safety status */
-		if (_to_safety != nullptr) {
-			orb_publish(ORB_ID(safety), _to_safety, &safety);
+			/* lazily publish the safety status */
+			if (_to_safety != nullptr) {
+				orb_publish(ORB_ID(safety), _to_safety, &safety);
 
-		} else {
-			int instance = _class_instance;
-			_to_safety = orb_advertise_multi(ORB_ID(safety), &safety, &instance, ORB_PRIO_DEFAULT);
+			} else {
+				int instance = _class_instance;
+				_to_safety = orb_advertise_multi(ORB_ID(safety), &safety, &instance, ORB_PRIO_DEFAULT);
+			}
 		}
-	}
 
 #endif
-	/* check arming state */
-	bool updated = false;
-	orb_check(_armed_sub, &updated);
+		/* check arming state */
+		bool updated = false;
+		orb_check(_armed_sub, &updated);
 
-	if (updated) {
-		orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
+		if (updated) {
+			orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
 
-		/* Update the armed status and check that we're not locked down.
-		 * We also need to arm throttle for the ESC calibration. */
-		_throttle_armed = (_safety_off && _armed.armed && !_armed.lockdown) ||
-				  (_safety_off && _armed.in_esc_calibration_mode);
+			/* Update the armed status and check that we're not locked down.
+			 * We also need to arm throttle for the ESC calibration. */
+			_throttle_armed = (_safety_off && _armed.armed && !_armed.lockdown) ||
+					  (_safety_off && _armed.in_esc_calibration_mode);
 
 
-		/* update PWM status if armed or if disarmed PWM values are set */
-		bool pwm_on = _armed.armed || _num_disarmed_set > 0 || _armed.in_esc_calibration_mode;
+			/* update PWM status if armed or if disarmed PWM values are set */
+			bool pwm_on = _armed.armed || _num_disarmed_set > 0 || _armed.in_esc_calibration_mode;
 
-		if (_pwm_on != pwm_on) {
-			_pwm_on = pwm_on;
+			if (_pwm_on != pwm_on) {
+				_pwm_on = pwm_on;
 
-			update_pwm_out_state(pwm_on);
+				update_pwm_out_state(pwm_on);
+			}
 		}
-	}
 
 #ifdef RC_SERIAL_PORT
-	/* vehicle command */
-	orb_check(_vehicle_cmd_sub, &updated);
+		/* vehicle command */
+		orb_check(_vehicle_cmd_sub, &updated);
 
-	if (updated) {
-		struct vehicle_command_s cmd;
-		orb_copy(ORB_ID(vehicle_command), _vehicle_cmd_sub, &cmd);
+		if (updated) {
+			struct vehicle_command_s cmd;
+			orb_copy(ORB_ID(vehicle_command), _vehicle_cmd_sub, &cmd);
 
-		// Check for a DSM pairing command
-		if (((unsigned int)cmd.command == vehicle_command_s::VEHICLE_CMD_START_RX_PAIR) && ((int)cmd.param1 == 0)) {
-			dsm_bind_ioctl((int)cmd.param2);
-		}
+			// Check for a DSM pairing command
+			if (((unsigned int)cmd.command == vehicle_command_s::VEHICLE_CMD_START_RX_PAIR) && ((int)cmd.param1 == 0)) {
+				dsm_bind_ioctl((int)cmd.param2);
+			}
 
-	}
+		}
 
 #endif
 
-	orb_check(_param_sub, &updated);
+		orb_check(_param_sub, &updated);
 
-	if (updated) {
-		parameter_update_s pupdate;
-		orb_copy(ORB_ID(parameter_update), _param_sub, &pupdate);
+		if (updated) {
+			parameter_update_s pupdate;
+			orb_copy(ORB_ID(parameter_update), _param_sub, &pupdate);
 
-		update_pwm_rev_mask();
-		update_pwm_trims();
+			update_pwm_rev_mask();
+			update_pwm_trims();
 
-		int32_t dsm_bind_val;
-		param_t param_handle;
+			int32_t dsm_bind_val;
+			param_t param_handle;
 
-		/* see if bind parameter has been set, and reset it to -1 */
-		param_get(param_handle = param_find("RC_DSM_BIND"), &dsm_bind_val);
+			/* see if bind parameter has been set, and reset it to -1 */
+			param_get(param_handle = param_find("RC_DSM_BIND"), &dsm_bind_val);
 
-		if (dsm_bind_val > -1) {
-			dsm_bind_ioctl(dsm_bind_val);
-			dsm_bind_val = -1;
-			param_set(param_handle, &dsm_bind_val);
-		}
+			if (dsm_bind_val > -1) {
+				dsm_bind_ioctl(dsm_bind_val);
+				dsm_bind_val = -1;
+				param_set(param_handle, &dsm_bind_val);
+			}
 
-		// maximum motor slew rate parameter
-		param_handle = param_find("MOT_SLEW_MAX");
+			// maximum motor slew rate parameter
+			param_handle = param_find("MOT_SLEW_MAX");
 
-		if (param_handle != PARAM_INVALID) {
-			param_get(param_handle, &_mot_t_max);
-		}
+			if (param_handle != PARAM_INVALID) {
+				param_get(param_handle, &_mot_t_max);
+			}
 
-		// thrust to pwm modelling factor
-		param_handle = param_find("THR_MDL_FAC");
+			// thrust to pwm modelling factor
+			param_handle = param_find("THR_MDL_FAC");
 
-		if (param_handle != PARAM_INVALID) {
-			param_get(param_handle, &_thr_mdl_fac);
+			if (param_handle != PARAM_INVALID) {
+				param_get(param_handle, &_thr_mdl_fac);
+			}
 		}
-	}
 
-	/* update ADC sampling */
+		/* update ADC sampling */
 #ifdef ADC_RC_RSSI_CHANNEL
-	orb_check(_adc_sub, &updated);
+		orb_check(_adc_sub, &updated);
 
-	if (updated) {
+		if (updated) {
 
-		struct adc_report_s adc;
-		orb_copy(ORB_ID(adc_report), _adc_sub, &adc);
-		const unsigned adc_chans = sizeof(adc.channel_id) / sizeof(adc.channel_id[0]);
+			struct adc_report_s adc;
+			orb_copy(ORB_ID(adc_report), _adc_sub, &adc);
+			const unsigned adc_chans = sizeof(adc.channel_id) / sizeof(adc.channel_id[0]);
 
-		for (unsigned i = 0; i < adc_chans; i++) {
-			if (adc.channel_id[i] == ADC_RC_RSSI_CHANNEL) {
+			for (unsigned i = 0; i < adc_chans; i++) {
+				if (adc.channel_id[i] == ADC_RC_RSSI_CHANNEL) {
 
-				if (_analog_rc_rssi_volt < 0.0f) {
-					_analog_rc_rssi_volt = adc.channel_value[i];
-				}
+					if (_analog_rc_rssi_volt < 0.0f) {
+						_analog_rc_rssi_volt = adc.channel_value[i];
+					}
 
-				_analog_rc_rssi_volt = _analog_rc_rssi_volt * 0.995f + adc.channel_value[i] * 0.005f;
+					_analog_rc_rssi_volt = _analog_rc_rssi_volt * 0.995f + adc.channel_value[i] * 0.005f;
 
-				/* only allow this to be used if we see a high RSSI once */
-				if (_analog_rc_rssi_volt > 2.5f) {
-					_analog_rc_rssi_stable = true;
+					/* only allow this to be used if we see a high RSSI once */
+					if (_analog_rc_rssi_volt > 2.5f) {
+						_analog_rc_rssi_stable = true;
+					}
 				}
 			}
 		}
-	}
 
 #endif
 
-	bool rc_updated = false;
+		bool rc_updated = false;
 
 #ifdef RC_SERIAL_PORT
-	// This block scans for a supported serial RC input and locks onto the first one found
-	// Scan for 300 msec, then switch protocol
-	constexpr hrt_abstime rc_scan_max = 300 * 1000;
+		// This block scans for a supported serial RC input and locks onto the first one found
+		// Scan for 300 msec, then switch protocol
+		constexpr hrt_abstime rc_scan_max = 300 * 1000;
 
-	bool sbus_failsafe, sbus_frame_drop;
-	unsigned frame_drops;
-	bool dsm_11_bit;
+		bool sbus_failsafe, sbus_frame_drop;
+		unsigned frame_drops;
+		bool dsm_11_bit;
 
 
-	if (_report_lock && _rc_scan_locked) {
-		_report_lock = false;
-		//warnx("RCscan: %s RC input locked", RC_SCAN_STRING[_rc_scan_state]);
-	}
-
-	// read all available data from the serial RC input UART
-	int newBytes = ::read(_rcs_fd, &_rcs_buf[0], SBUS_BUFFER_SIZE);
+		if (_report_lock && _rc_scan_locked) {
+			_report_lock = false;
+			//warnx("RCscan: %s RC input locked", RC_SCAN_STRING[_rc_scan_state]);
+		}
 
-	switch (_rc_scan_state) {
-	case RC_SCAN_SBUS:
-		if (_rc_scan_begin == 0) {
-			_rc_scan_begin = _cycle_timestamp;
-			// Configure serial port for SBUS
-			sbus_config(_rcs_fd, false);
-			rc_io_invert(true);
+		// read all available data from the serial RC input UART
+		int newBytes = ::read(_rcs_fd, &_rcs_buf[0], SBUS_BUFFER_SIZE);
 
-		} else if (_rc_scan_locked
-			   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
+		switch (_rc_scan_state) {
+		case RC_SCAN_SBUS:
+			if (_rc_scan_begin == 0) {
+				_rc_scan_begin = _cycle_timestamp;
+				// Configure serial port for SBUS
+				sbus_config(_rcs_fd, false);
+				rc_io_invert(true);
 
-			// parse new data
-			if (newBytes > 0) {
-				rc_updated = sbus_parse(_cycle_timestamp, &_rcs_buf[0], newBytes, &raw_rc_values[0], &raw_rc_count, &sbus_failsafe,
-							&sbus_frame_drop, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
+			} else if (_rc_scan_locked
+				   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
 
-				if (rc_updated) {
-					// we have a new SBUS frame. Publish it.
-					_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SBUS;
-					fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
-						   sbus_frame_drop, sbus_failsafe, frame_drops);
-					_rc_scan_locked = true;
+				// parse new data
+				if (newBytes > 0) {
+					rc_updated = sbus_parse(_cycle_timestamp, &_rcs_buf[0], newBytes, &raw_rc_values[0], &raw_rc_count, &sbus_failsafe,
+								&sbus_frame_drop, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
+
+					if (rc_updated) {
+						// we have a new SBUS frame. Publish it.
+						_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SBUS;
+						fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
+							   sbus_frame_drop, sbus_failsafe, frame_drops);
+						_rc_scan_locked = true;
+					}
 				}
-			}
 
-		} else {
-			// Scan the next protocol
-			set_rc_scan_state(RC_SCAN_DSM);
-		}
+			} else {
+				// Scan the next protocol
+				set_rc_scan_state(RC_SCAN_DSM);
+			}
 
-		break;
+			break;
 
-	case RC_SCAN_DSM:
-		if (_rc_scan_begin == 0) {
-			_rc_scan_begin = _cycle_timestamp;
+		case RC_SCAN_DSM:
+			if (_rc_scan_begin == 0) {
+				_rc_scan_begin = _cycle_timestamp;
 //			// Configure serial port for DSM
-			dsm_config(_rcs_fd);
-			rc_io_invert(false);
-
-		} else if (_rc_scan_locked
-			   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
-
-			if (newBytes > 0) {
-				// parse new data
-				rc_updated = dsm_parse(_cycle_timestamp, &_rcs_buf[0], newBytes, &raw_rc_values[0], &raw_rc_count,
-						       &dsm_11_bit, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
-
-				if (rc_updated) {
-					// we have a new DSM frame. Publish it.
-					_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_DSM;
-					fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
-						   false, false, frame_drops);
-					_rc_scan_locked = true;
+				dsm_config(_rcs_fd);
+				rc_io_invert(false);
+
+			} else if (_rc_scan_locked
+				   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
+
+				if (newBytes > 0) {
+					// parse new data
+					rc_updated = dsm_parse(_cycle_timestamp, &_rcs_buf[0], newBytes, &raw_rc_values[0], &raw_rc_count,
+							       &dsm_11_bit, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
+
+					if (rc_updated) {
+						// we have a new DSM frame. Publish it.
+						_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_DSM;
+						fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
+							   false, false, frame_drops);
+						_rc_scan_locked = true;
+					}
 				}
-			}
 
-		} else {
-			// Scan the next protocol
-			set_rc_scan_state(RC_SCAN_ST24);
-		}
+			} else {
+				// Scan the next protocol
+				set_rc_scan_state(RC_SCAN_ST24);
+			}
 
-		break;
+			break;
 
-	case RC_SCAN_ST24:
-		if (_rc_scan_begin == 0) {
-			_rc_scan_begin = _cycle_timestamp;
-			// Configure serial port for DSM
-			dsm_config(_rcs_fd);
-			rc_io_invert(false);
+		case RC_SCAN_ST24:
+			if (_rc_scan_begin == 0) {
+				_rc_scan_begin = _cycle_timestamp;
+				// Configure serial port for DSM
+				dsm_config(_rcs_fd);
+				rc_io_invert(false);
 
-		} else if (_rc_scan_locked
-			   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
+			} else if (_rc_scan_locked
+				   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
 
-			if (newBytes > 0) {
-				// parse new data
-				uint8_t st24_rssi, lost_count;
+				if (newBytes > 0) {
+					// parse new data
+					uint8_t st24_rssi, lost_count;
 
-				rc_updated = false;
+					rc_updated = false;
 
-				for (unsigned i = 0; i < (unsigned)newBytes; i++) {
-					/* set updated flag if one complete packet was parsed */
-					st24_rssi = RC_INPUT_RSSI_MAX;
-					rc_updated = (OK == st24_decode(_rcs_buf[i], &st24_rssi, &lost_count,
-									&raw_rc_count, raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS));
-				}
+					for (unsigned i = 0; i < (unsigned)newBytes; i++) {
+						/* set updated flag if one complete packet was parsed */
+						st24_rssi = RC_INPUT_RSSI_MAX;
+						rc_updated = (OK == st24_decode(_rcs_buf[i], &st24_rssi, &lost_count,
+										&raw_rc_count, raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS));
+					}
 
-				// The st24 will keep outputting RC channels and RSSI even if RC has been lost.
-				// The only way to detect RC loss is therefore to look at the lost_count.
+					// The st24 will keep outputting RC channels and RSSI even if RC has been lost.
+					// The only way to detect RC loss is therefore to look at the lost_count.
 
-				if (rc_updated && lost_count == 0) {
-					// we have a new ST24 frame. Publish it.
-					_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_ST24;
-					fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
-						   false, false, frame_drops, st24_rssi);
-					_rc_scan_locked = true;
+					if (rc_updated && lost_count == 0) {
+						// we have a new ST24 frame. Publish it.
+						_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_ST24;
+						fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
+							   false, false, frame_drops, st24_rssi);
+						_rc_scan_locked = true;
+					}
 				}
+
+			} else {
+				// Scan the next protocol
+				set_rc_scan_state(RC_SCAN_SUMD);
 			}
 
-		} else {
-			// Scan the next protocol
-			set_rc_scan_state(RC_SCAN_SUMD);
-		}
+			break;
 
-		break;
+		case RC_SCAN_SUMD:
+			if (_rc_scan_begin == 0) {
+				_rc_scan_begin = _cycle_timestamp;
+				// Configure serial port for DSM
+				dsm_config(_rcs_fd);
+				rc_io_invert(false);
 
-	case RC_SCAN_SUMD:
-		if (_rc_scan_begin == 0) {
-			_rc_scan_begin = _cycle_timestamp;
-			// Configure serial port for DSM
-			dsm_config(_rcs_fd);
-			rc_io_invert(false);
+			} else if (_rc_scan_locked
+				   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
 
-		} else if (_rc_scan_locked
-			   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
+				if (newBytes > 0) {
+					// parse new data
+					uint8_t sumd_rssi, rx_count;
+					bool sumd_failsafe;
 
-			if (newBytes > 0) {
-				// parse new data
-				uint8_t sumd_rssi, rx_count;
-				bool sumd_failsafe;
+					rc_updated = false;
 
-				rc_updated = false;
+					for (unsigned i = 0; i < (unsigned)newBytes; i++) {
+						/* set updated flag if one complete packet was parsed */
+						sumd_rssi = RC_INPUT_RSSI_MAX;
+						rc_updated = (OK == sumd_decode(_rcs_buf[i], &sumd_rssi, &rx_count,
+										&raw_rc_count, raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS, &sumd_failsafe));
+					}
 
-				for (unsigned i = 0; i < (unsigned)newBytes; i++) {
-					/* set updated flag if one complete packet was parsed */
-					sumd_rssi = RC_INPUT_RSSI_MAX;
-					rc_updated = (OK == sumd_decode(_rcs_buf[i], &sumd_rssi, &rx_count,
-									&raw_rc_count, raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS, &sumd_failsafe));
+					if (rc_updated) {
+						// we have a new SUMD frame. Publish it.
+						_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SUMD;
+						fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
+							   false, sumd_failsafe, frame_drops, sumd_rssi);
+						_rc_scan_locked = true;
+					}
 				}
 
-				if (rc_updated) {
-					// we have a new SUMD frame. Publish it.
-					_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SUMD;
-					fill_rc_in(raw_rc_count, raw_rc_values, _cycle_timestamp,
-						   false, sumd_failsafe, frame_drops, sumd_rssi);
-					_rc_scan_locked = true;
-				}
+			} else {
+				// Scan the next protocol
+				set_rc_scan_state(RC_SCAN_PPM);
 			}
 
-		} else {
-			// Scan the next protocol
-			set_rc_scan_state(RC_SCAN_PPM);
-		}
-
-		break;
+			break;
 
-	case RC_SCAN_PPM:
-		// skip PPM if it's not supported
+		case RC_SCAN_PPM:
+			// skip PPM if it's not supported
 #ifdef HRT_PPM_CHANNEL
-		if (_rc_scan_begin == 0) {
-			_rc_scan_begin = _cycle_timestamp;
-			// Configure timer input pin for CPPM
-			px4_arch_configgpio(GPIO_PPM_IN);
-			rc_io_invert(false);
-
-		} else if (_rc_scan_locked
-			   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
-
-			// see if we have new PPM input data
-			if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal)
-			    && ppm_decoded_channels > 3) {
-				// we have a new PPM frame. Publish it.
-				rc_updated = true;
-				_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_PPM;
-				fill_rc_in(ppm_decoded_channels, ppm_buffer, _cycle_timestamp,
-					   false, false, 0);
-				_rc_scan_locked = true;
-				_rc_in.rc_ppm_frame_length = ppm_frame_length;
-				_rc_in.timestamp_last_signal = ppm_last_valid_decode;
-			}
+			if (_rc_scan_begin == 0) {
+				_rc_scan_begin = _cycle_timestamp;
+				// Configure timer input pin for CPPM
+				px4_arch_configgpio(GPIO_PPM_IN);
+				rc_io_invert(false);
+
+			} else if (_rc_scan_locked
+				   || _cycle_timestamp - _rc_scan_begin < rc_scan_max) {
+
+				// see if we have new PPM input data
+				if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal)
+				    && ppm_decoded_channels > 3) {
+					// we have a new PPM frame. Publish it.
+					rc_updated = true;
+					_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_PPM;
+					fill_rc_in(ppm_decoded_channels, ppm_buffer, _cycle_timestamp,
+						   false, false, 0);
+					_rc_scan_locked = true;
+					_rc_in.rc_ppm_frame_length = ppm_frame_length;
+					_rc_in.timestamp_last_signal = ppm_last_valid_decode;
+				}
 
-		} else {
-			// disable CPPM input by mapping it away from the timer capture input
-			px4_arch_unconfiggpio(GPIO_PPM_IN);
-			// Scan the next protocol
-			set_rc_scan_state(RC_SCAN_SBUS);
-		}
+			} else {
+				// disable CPPM input by mapping it away from the timer capture input
+				px4_arch_unconfiggpio(GPIO_PPM_IN);
+				// Scan the next protocol
+				set_rc_scan_state(RC_SCAN_SBUS);
+			}
 
 #else   // skip PPM if it's not supported
-		set_rc_scan_state(RC_SCAN_SBUS);
+			set_rc_scan_state(RC_SCAN_SBUS);
 
 #endif  // HRT_PPM_CHANNEL
 
-		break;
-	}
+			break;
+		}
 
 #else  // RC_SERIAL_PORT not defined
 #ifdef HRT_PPM_CHANNEL
 
-	// see if we have new PPM input data
-	if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal)
-	    && ppm_decoded_channels > 3) {
-		// we have a new PPM frame. Publish it.
-		rc_updated = true;
-		fill_rc_in(ppm_decoded_channels, ppm_buffer, hrt_absolute_time(),
-			   false, false, 0);
-	}
+		// see if we have new PPM input data
+		if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal)
+		    && ppm_decoded_channels > 3) {
+			// we have a new PPM frame. Publish it.
+			rc_updated = true;
+			fill_rc_in(ppm_decoded_channels, ppm_buffer, hrt_absolute_time(),
+				   false, false, 0);
+		}
 
 #endif  // HRT_PPM_CHANNEL
 #endif  // RC_SERIAL_PORT
 
-	if (rc_updated) {
-		/* lazily advertise on first publication */
-		if (_to_input_rc == nullptr) {
-			int instance = _class_instance;
-			_to_input_rc = orb_advertise_multi(ORB_ID(input_rc), &_rc_in, &instance, ORB_PRIO_DEFAULT);
+		if (rc_updated) {
+			/* lazily advertise on first publication */
+			if (_to_input_rc == nullptr) {
+				int instance = _class_instance;
+				_to_input_rc = orb_advertise_multi(ORB_ID(input_rc), &_rc_in, &instance, ORB_PRIO_DEFAULT);
 
-		} else {
-			orb_publish(ORB_ID(input_rc), _to_input_rc, &_rc_in);
-		}
+			} else {
+				orb_publish(ORB_ID(input_rc), _to_input_rc, &_rc_in);
+			}
 
-	} else if (!rc_updated && ((hrt_absolute_time() - _rc_in.timestamp_last_signal) > 1000 * 1000)) {
-		_rc_scan_locked = false;
+		} else if (!rc_updated && ((hrt_absolute_time() - _rc_in.timestamp_last_signal) > 1000 * 1000)) {
+			_rc_scan_locked = false;
+		}
 	}
-
-	/*
-	 * schedule next cycle
-	 */
-	work_queue(HPWORK, &_work, (worker_t)&PX4FMU::cycle_trampoline, this, USEC2TICK(SCHEDULE_INTERVAL));
-//		   USEC2TICK(SCHEDULE_INTERVAL - main_out_latency));
 }
 
 void PX4FMU::work_stop()
 {
-	work_cancel(HPWORK, &_work);
 
 	for (unsigned i = 0; i < actuator_controls_s::NUM_ACTUATOR_CONTROL_GROUPS; i++) {
 		if (_control_subs[i] > 0) {
@@ -3154,18 +3177,18 @@ test(void)
 			}
 		}
 
-		/* readback servo values */
-		for (unsigned i = 0; i < servo_count; i++) {
-			servo_position_t value;
-
-			if (ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&value)) {
-				err(1, "error reading PWM servo %d", i);
-			}
-
-			if (value != servos[i]) {
-				errx(1, "servo %d readback error, got %u expected %u", i, value, servos[i]);
-			}
-		}
+//		/* readback servo values */
+//		for (unsigned i = 0; i < servo_count; i++) {
+//			servo_position_t value;
+//
+//			if (ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&value)) {
+//				err(1, "error reading PWM servo %d", i);
+//			}
+//
+//			if (value != servos[i]) {
+//				errx(1, "servo %d readback error, got %u expected %u", i, value, servos[i]);
+//			}
+//		}
 
 		if (capture_count != 0 && (++rate_limit % 500 == 0)) {
 			for (unsigned i = 0; i < capture_count; i++) {
diff --git a/src/drivers/px4io/px4io.cpp b/src/drivers/px4io/px4io.cpp
index 92ee3fa79974315643d32f9e921d051e42d01958..f606cd62367f5c69e98e7f679d4a4022d3bb5432 100644
--- a/src/drivers/px4io/px4io.cpp
+++ b/src/drivers/px4io/px4io.cpp
@@ -951,7 +951,7 @@ PX4IO::task_main()
 	 * primary PWM output or not.
 	 */
 	_t_actuator_controls_0 = orb_subscribe(ORB_ID(actuator_controls_0));
-	orb_set_interval(_t_actuator_controls_0, 20);		/* default to 50Hz */
+//	orb_set_interval(_t_actuator_controls_0, 20);		/* default to 50Hz */
 	_t_actuator_controls_1 = orb_subscribe(ORB_ID(actuator_controls_1));
 	orb_set_interval(_t_actuator_controls_1, 33);		/* default to 30Hz */
 	_t_actuator_controls_2 = orb_subscribe(ORB_ID(actuator_controls_2));
@@ -3527,6 +3527,8 @@ test(void)
 			}
 		}
 
+		usleep(250);
+
 		/* readback servo values */
 		for (unsigned i = 0; i < servo_count; i++) {
 			servo_position_t value;
@@ -3536,7 +3538,7 @@ test(void)
 			}
 
 			if (value != servos[i]) {
-				errx(1, "servo %u readback error, got %hu expected %hu", i, value, servos[i]);
+				warnx("servo %u readback error, got %hu expected %hu", i, value, servos[i]);
 			}
 		}
 
diff --git a/src/drivers/stm32/drv_io_timer.c b/src/drivers/stm32/drv_io_timer.c
index 46e5cace3e818a70552fa98800060ebd6d8dd0a7..82eae6776f75c7c64c9d63509dad541ec859ad3c 100644
--- a/src/drivers/stm32/drv_io_timer.c
+++ b/src/drivers/stm32/drv_io_timer.c
@@ -103,13 +103,17 @@
 
 #define CCMR_C1_PWMOUT_INIT (GTIM_CCMR_MODE_PWM1 << GTIM_CCMR1_OC1M_SHIFT) | GTIM_CCMR1_OC1PE
 
+#define CCMR_C1_ONESHOT_INIT (GTIM_CCMR_MODE_PWM2 << GTIM_CCMR1_OC1M_SHIFT) | GTIM_CCMR1_OC1PE
+
 #define CCMR_C1_PWMIN_INIT 0 // TBD
 
-//												 				  NotUsed   PWMOut  PWMIn Capture
-io_timer_channel_allocation_t channel_allocations[IOTimerChanModeSize] = { UINT8_MAX,   0,  0,  0 };
+//												 				  NotUsed   PWMOut  PWMIn Capture OneShot
+io_timer_channel_allocation_t channel_allocations[IOTimerChanModeSize] = { UINT8_MAX,   0,  0,  0, 0 };
 
 typedef uint8_t io_timer_allocation_t; /* big enough to hold MAX_IO_TIMERS */
 
+static uint8_t timer_freq[MAX_IO_TIMERS] = { 1, 1, 1, 1 };	/* default 1 MHz counter frequency */
+
 static io_timer_allocation_t once = 0;
 
 typedef struct channel_stat_t {
@@ -242,7 +246,7 @@ static uint32_t get_timer_channels(unsigned timer)
 
 	if (validate_timer_index(timer) == 0) {
 		const io_timers_t *tmr = &io_timers[timer];
-		/* Gather the channel bit that belong to the timer */
+		/* Gather the channel bits that belong to the timer */
 
 		for (unsigned chan_index = tmr->first_channel_index; chan_index <= tmr->last_channel_index; chan_index++) {
 			channels |= 1 << chan_index;
@@ -376,7 +380,7 @@ static int timer_set_rate(unsigned timer, unsigned rate)
 {
 
 	/* configure the timer to update at the desired rate */
-	rARR(timer) = 1000000 / rate;
+	rARR(timer) = timer_freq[timer] * 1000000 / rate;
 
 	/* generate an update event; reloads the counter and all registers */
 	rEGR(timer) = GTIM_EGR_UG;
@@ -384,6 +388,31 @@ static int timer_set_rate(unsigned timer, unsigned rate)
 	return 0;
 }
 
+//#define FAKE_ONESHOT
+void io_timer_set_oneshot_mode(unsigned timer)
+{
+	timer_freq[timer] = 8;
+
+#ifdef FAKE_ONESHOT
+	// to eliminate jitter with max additional latency of 500usec, set PWM rate to 2KHz.
+	// Note that the FMU mixer jitter is on the order of 2msec.
+	timer_set_rate(timer, 2000);
+#else
+	// set rate to 125Hz (lowest possible at 8MHz is ~122Hz)
+	// io_timer_set_ccr() must be called at > 125Hz to minimize latency
+	timer_set_rate(timer, 125);
+#endif
+}
+
+extern void io_timer_force_update(unsigned timer)
+{
+#ifdef FAKE_ONESHOT
+	// let's not and say we did
+#else
+	// force update of channel compare register
+	rEGR(timer) |= GTIM_EGR_UG;
+#endif
+}
 
 int io_timer_init_timer(unsigned timer)
 {
@@ -423,12 +452,19 @@ int io_timer_init_timer(unsigned timer)
 			rBDTR(timer) = ATIM_BDTR_MOE;
 		}
 
-		/* If the timer clock source provided as clock_freq is the STM32_APBx_TIMx_CLKIN
-		 * then configure the timer to free-run at 1MHz.
-		 * Otherwize, other frequencies are attainable by adjusting .clock_freq accordingly.
+		/* If in oneshot mode, configure the prescaler for 8MHz output.
+		 * else set prescaler for 1MHz.
 		 */
 
-		rPSC(timer) = (io_timers[timer].clock_freq / 1000000) - 1;
+		if (timer_freq[timer] == 8) {
+			rPSC(timer) = (io_timers[timer].clock_freq / 8000000) - 1;
+			// can't find a way to trigger in this mode
+//			/* set one pulse mode */
+//			rCR1(timer) |= 1 << 3;
+
+		} else {
+			rPSC(timer) = (io_timers[timer].clock_freq / 1000000) - 1;
+		}
 
 		/*
 		 * Note we do the Standard PWM Out init here
@@ -455,7 +491,7 @@ int io_timer_init_timer(unsigned timer)
 
 int io_timer_set_rate(unsigned timer, unsigned rate)
 {
-	/* Gather the channel bit that belong to the timer */
+	/* Gather the channel bits that belong to the timer */
 
 	uint32_t channels = get_timer_channels(timer);
 
@@ -484,6 +520,12 @@ int io_timer_channel_init(unsigned channel, io_timer_channel_mode_t mode,
 	/* figure out the GPIO config first */
 
 	switch (mode) {
+
+	case IOTimerChanMode_OneShot:
+		io_timer_set_oneshot_mode(timer_io_channels[channel].timer_index);
+
+	// intentional fallthrough
+
 	case IOTimerChanMode_PWMOut:
 		ccer_setbits = 0;
 		dier_setbits = 0;
@@ -514,7 +556,7 @@ int io_timer_channel_init(unsigned channel, io_timer_channel_mode_t mode,
 
 	if (rv >= 0) {
 
-		/* Blindly try to initialize the time - it will only do it once */
+		/* Blindly try to initialize the timer - it will only do it once */
 
 		io_timer_init_timer(channels_timer(channel));
 
@@ -593,6 +635,7 @@ int io_timer_set_enable(bool state, io_timer_channel_mode_t mode, io_timer_chann
 	switch (mode) {
 	case IOTimerChanMode_NotUsed:
 	case IOTimerChanMode_PWMOut:
+	case IOTimerChanMode_OneShot:
 		dier_bit = 0;
 		break;
 
@@ -635,7 +678,7 @@ int io_timer_set_enable(bool state, io_timer_channel_mode_t mode, io_timer_chann
 			action_cache[timer].dier_clearbits |= GTIM_DIER_CC1IE  << shifts;
 			action_cache[timer].dier_setbits   |= dier_bit << shifts;
 
-			if (state && mode == IOTimerChanMode_PWMOut) {
+			if ((state && mode == IOTimerChanMode_PWMOut) || (state && mode == IOTimerChanMode_OneShot)) {
 				action_cache[timer].gpio[shifts] = timer_io_channels[chan_index].gpio_out;
 			}
 		}
@@ -689,13 +732,16 @@ int io_timer_set_ccr(unsigned channel, uint16_t value)
 	int rv = io_timer_validate_channel_index(channel);
 
 	if (rv == 0) {
-		if (io_timer_get_channel_mode(channel) != IOTimerChanMode_PWMOut) {
+		if ((io_timer_get_channel_mode(channel) != IOTimerChanMode_PWMOut) &&
+		    (io_timer_get_channel_mode(channel) != IOTimerChanMode_OneShot)) {
 
 			rv = -EIO;
 
 		} else {
 
 			/* configure the channel */
+
+			// this hack won't work correctly in oneshot mode; would be OK if it just inverted the output
 #ifdef BOARD_PWM_DRIVE_ACTIVE_LOW
 			unsigned period = rARR(channels_timer(channel));
 			value = period - value;
@@ -705,6 +751,7 @@ int io_timer_set_ccr(unsigned channel, uint16_t value)
 				value--;
 			}
 
+			// write PWM value into CCR preload register
 			REG(channels_timer(channel), timer_io_channels[channel].ccr_offset) = value;
 		}
 	}
@@ -717,7 +764,8 @@ uint16_t io_channel_get_ccr(unsigned channel)
 	uint16_t value = 0;
 
 	if (io_timer_validate_channel_index(channel) == 0 &&
-	    io_timer_get_channel_mode(channel) == IOTimerChanMode_PWMOut) {
+	    ((io_timer_get_channel_mode(channel) == IOTimerChanMode_PWMOut) ||
+	     (io_timer_get_channel_mode(channel) == IOTimerChanMode_OneShot))) {
 		value = REG(channels_timer(channel), timer_io_channels[channel].ccr_offset) + 1;
 
 #ifdef BOARD_PWM_DRIVE_ACTIVE_LOW
diff --git a/src/drivers/stm32/drv_io_timer.h b/src/drivers/stm32/drv_io_timer.h
index b1a19803256533342d800e5240336927eddb3f66..9bc61ea5e13dd209e56f10b189bbc39272653db5 100644
--- a/src/drivers/stm32/drv_io_timer.h
+++ b/src/drivers/stm32/drv_io_timer.h
@@ -58,12 +58,19 @@ typedef enum io_timer_channel_mode_t {
 	IOTimerChanMode_PWMOut  = 1,
 	IOTimerChanMode_PWMIn   = 2,
 	IOTimerChanMode_Capture = 3,
+	IOTimerChanMode_OneShot = 4,
 	IOTimerChanModeSize
 } io_timer_channel_mode_t;
 
 typedef uint8_t io_timer_channel_allocation_t; /* big enough to hold MAX_TIMER_IO_CHANNELS */
 
-/* array of timers dedicated to PWM in and out and capture use */
+/* array of timers dedicated to PWM in and out and capture use
+ *** Note that the clock_freq field must be set to the frequency (in Hz) of the selected clock.
+ *** Normal PWM timers set the prescaler to achieve a counter frequency of 1MHz
+ *** and OneShot PWM timers set the prescaler to achieve a counter frequency of 8MHz.
+ *** These counter frequencies are specified (in MHz) in array timer_freq[MAX_IO_TIMERS].
+ *** Beware that legacy code *assumes* that the counter frequency is 1MHz.
+ */
 typedef struct io_timers_t {
 	uint32_t	base;
 	uint32_t	clock_register;
@@ -108,6 +115,7 @@ __EXPORT int io_timer_channel_init(unsigned channel, io_timer_channel_mode_t mod
 				   channel_handler_t channel_handler, void *context);
 
 __EXPORT int io_timer_init_timer(unsigned timer);
+__EXPORT void io_timer_set_oneshot_mode(unsigned timer);
 
 __EXPORT int io_timer_set_rate(unsigned timer, unsigned rate);
 __EXPORT int io_timer_set_enable(bool state, io_timer_channel_mode_t mode,
@@ -121,4 +129,11 @@ __EXPORT int io_timer_is_channel_free(unsigned channel);
 __EXPORT int io_timer_free_channel(unsigned channel);
 __EXPORT int io_timer_get_channel_mode(unsigned channel);
 __EXPORT int io_timer_get_mode_channels(io_timer_channel_mode_t mode);
+/**
+ * Force update of all timer channels
+ *
+ * @param timer	The timer to force.
+ */
+__EXPORT extern void io_timer_force_update(unsigned timer);
+
 __END_DECLS
diff --git a/src/drivers/stm32/drv_pwm_servo.c b/src/drivers/stm32/drv_pwm_servo.c
index a0183d50c52a3473b100ae91720867cd1486a393..0810ed50d36e77b4247c75e235e09c6cbaee99de 100644
--- a/src/drivers/stm32/drv_pwm_servo.c
+++ b/src/drivers/stm32/drv_pwm_servo.c
@@ -63,6 +63,22 @@
 
 #include <stm32_tim.h>
 
+/* pwm_oneshot_mode true implies all servo outputs are oneshot */
+static bool pwm_oneshot_mode = false;
+
+/* this is a bitfield: if bit N is set, that timer is used for oneshot only */
+static uint8_t oneshot_timers;
+
+void up_pwm_set_oneshot_mode(bool value)
+{
+	pwm_oneshot_mode = value;
+}
+
+bool up_pwm_get_oneshot_mode()
+{
+	return pwm_oneshot_mode;
+}
+
 int up_pwm_servo_set(unsigned channel, servo_position_t value)
 {
 	return io_timer_set_ccr(channel, value);
@@ -76,7 +92,13 @@ servo_position_t up_pwm_servo_get(unsigned channel)
 int up_pwm_servo_init(uint32_t channel_mask)
 {
 	/* Init channels */
-	uint32_t current = io_timer_get_mode_channels(IOTimerChanMode_PWMOut);
+	io_timer_channel_mode_t chmode = IOTimerChanMode_PWMOut;
+
+	if (pwm_oneshot_mode) {
+		chmode = IOTimerChanMode_OneShot;
+	}
+
+	uint32_t current = io_timer_get_mode_channels(chmode);
 
 	// First free the current set of PWMs
 
@@ -87,6 +109,8 @@ int up_pwm_servo_init(uint32_t channel_mask)
 		}
 	}
 
+	oneshot_timers = 0;
+
 	// Now allocate the new set
 
 	for (unsigned channel = 0; channel_mask != 0 &&  channel < MAX_TIMER_IO_CHANNELS; channel++) {
@@ -98,8 +122,13 @@ int up_pwm_servo_init(uint32_t channel_mask)
 				io_timer_free_channel(channel);
 			}
 
-			io_timer_channel_init(channel, IOTimerChanMode_PWMOut, NULL, NULL);
+			io_timer_channel_init(channel, chmode, NULL, NULL);
 			channel_mask &= ~(1 << channel);
+
+			if (pwm_oneshot_mode) {
+				// update the oneshot_timers bitfield
+				oneshot_timers |= (1 << timer_io_channels[channel].timer_index);
+			}
 		}
 	}
 
@@ -132,6 +161,15 @@ int up_pwm_servo_set_rate_group_update(unsigned group, unsigned rate)
 	return OK;
 }
 
+void up_pwm_force_update(void)
+{
+	for (unsigned i = 0; i < 8; i++) {
+		if (oneshot_timers & (1 << i)) {
+			io_timer_force_update(i);
+		}
+	}
+}
+
 int up_pwm_servo_set_rate(unsigned rate)
 {
 	for (unsigned i = 0; i < MAX_IO_TIMERS; i++) {
@@ -149,5 +187,10 @@ uint32_t up_pwm_servo_get_rate_group(unsigned group)
 void
 up_pwm_servo_arm(bool armed)
 {
-	io_timer_set_enable(armed, IOTimerChanMode_PWMOut, IO_TIMER_ALL_MODES_CHANNELS);
+	if (pwm_oneshot_mode) {
+		io_timer_set_enable(armed, IOTimerChanMode_OneShot, IO_TIMER_ALL_MODES_CHANNELS);
+
+	} else {
+		io_timer_set_enable(armed, IOTimerChanMode_PWMOut, IO_TIMER_ALL_MODES_CHANNELS);
+	}
 }
diff --git a/src/modules/px4iofirmware/mixer.cpp b/src/modules/px4iofirmware/mixer.cpp
index 48514799b3b1e62907d4bde16de8729bdd09022e..284d194bff257dc1636a43df39f63ed8f993988d 100644
--- a/src/modules/px4iofirmware/mixer.cpp
+++ b/src/modules/px4iofirmware/mixer.cpp
@@ -75,6 +75,9 @@ static volatile bool should_arm_nothrottle = false;
 static volatile bool should_always_enable_pwm = false;
 static volatile bool in_mixer = false;
 
+static bool new_fmu_data = false;
+static uint64_t last_fmu_update = 0;
+
 extern int _sbus_fd;
 
 /* selected control values and count for mixing */
@@ -133,6 +136,11 @@ mixer_tick(void)
 
 		/* this flag is never cleared once OK */
 		r_status_flags |= PX4IO_P_STATUS_FLAGS_FMU_INITIALIZED;
+
+		if (system_state.fmu_data_received_time > last_fmu_update) {
+			new_fmu_data = true;
+			last_fmu_update = system_state.fmu_data_received_time;
+		}
 	}
 
 	/* default to failsafe mixing - it will be forced below if flag is set */
@@ -301,6 +309,15 @@ mixer_tick(void)
 		for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) {
 			r_page_actuators[i] = FLOAT_TO_REG(outputs[i]);
 		}
+
+		if (new_fmu_data) {
+			new_fmu_data = false;
+
+			if (up_pwm_get_oneshot_mode()) {
+				up_pwm_force_update();
+			}
+		}
+
 	}
 
 	/* set arming */
diff --git a/src/modules/px4iofirmware/px4io.c b/src/modules/px4iofirmware/px4io.c
index b20a6e1f06ac80191dcf464db3de3a6b72581076..ee424a0bb3bf0355097c42ffd68d9456794e3b92 100644
--- a/src/modules/px4iofirmware/px4io.c
+++ b/src/modules/px4iofirmware/px4io.c
@@ -235,6 +235,7 @@ int
 user_start(int argc, char *argv[])
 {
 	/* configure the first 8 PWM outputs (i.e. all of them) */
+	up_pwm_set_oneshot_mode(true);
 	up_pwm_servo_init(0xff);
 
 #if defined(CONFIG_HAVE_CXX) && defined(CONFIG_HAVE_CXXINITIALIZE)
diff --git a/src/modules/px4iofirmware/registers.c b/src/modules/px4iofirmware/registers.c
index 5b5696836b2200f353017e3d34909674cee533fd..5de27777a2aa5c21a1b3a1cadeb74daeaa168857 100644
--- a/src/modules/px4iofirmware/registers.c
+++ b/src/modules/px4iofirmware/registers.c
@@ -325,6 +325,10 @@ registers_set(uint8_t page, uint8_t offset, const uint16_t *values, unsigned num
 		system_state.fmu_data_received_time = hrt_absolute_time();
 		r_status_flags |= PX4IO_P_STATUS_FLAGS_RAW_PWM;
 
+		if (up_pwm_get_oneshot_mode()) {
+			up_pwm_force_update();
+		}
+
 		break;
 
 	/* handle setup for servo failsafe values */
diff --git a/src/systemcmds/pwm/pwm.c b/src/systemcmds/pwm/pwm.c
index 26178d04df2a6cbf8301a0b436567e578cc5bc4c..e5fd2f636880200918ddaf2d6303d50854753218 100644
--- a/src/systemcmds/pwm/pwm.c
+++ b/src/systemcmds/pwm/pwm.c
@@ -61,6 +61,7 @@
 #include "systemlib/err.h"
 #include "systemlib/param/param.h"
 #include "drivers/drv_pwm_output.h"
+#include "drivers/stm32/drv_io_timer.h"
 
 static void	usage(const char *reason);
 __EXPORT int	pwm_main(int argc, char *argv[]);
@@ -617,6 +618,7 @@ pwm_main(int argc, char *argv[])
 		warnx("Press CTRL-C or 'c' to abort.");
 
 		while (1) {
+
 			for (unsigned i = 0; i < servo_count; i++) {
 				if (set_mask & 1 << i) {
 					ret = px4_ioctl(fd, PWM_SERVO_SET(i), pwm_value);
@@ -654,7 +656,7 @@ pwm_main(int argc, char *argv[])
 				}
 			}
 
-			usleep(2000);
+			usleep(2500);
 		}
 
 		return 0;