dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
manualcontrol.c
Go to the documentation of this file.
1 
23 /*
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; either version 3 of the License, or
27  * (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful, but
30  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
32  * for more details.
33  *
34  * You should have received a copy of the GNU General Public License along
35  * with this program; if not, see <http://www.gnu.org/licenses/>
36  */
37 
38 #include "openpilot.h"
39 
40 #include "pios_thread.h"
41 #include "pios_rcvr.h"
42 
43 #include "control.h"
44 #include "failsafe_control.h"
45 #include "tablet_control.h"
46 #include "transmitter_control.h"
47 #include "geofence_control.h"
48 
49 #include "flightstatus.h"
50 #include "manualcontrolcommand.h"
51 #include "manualcontrolsettings.h"
52 #include "systemalarms.h"
53 
54 // Private constants
55 #if defined(PIOS_MANUAL_STACK_SIZE)
56 #define STACK_SIZE_BYTES PIOS_MANUAL_STACK_SIZE
57 #else
58 #define STACK_SIZE_BYTES 1200
59 #endif
60 
61 #define TASK_PRIORITY PIOS_THREAD_PRIO_HIGHEST
62 #define UPDATE_PERIOD_MS 20
63 
64 // Private variables
65 static struct pios_thread *taskHandle;
66 
67 // Private functions
68 static void manualControlTask(void *parameters);
69 static FlightStatusControlSourceOptions control_source_select();
70 
71 bool vehicle_is_armed = false;
72 
73 // This is exposed to transmitter_control
74 bool ok_to_arm(void);
75 
80 {
81  // Watchdog must be registered before starting task
83 
84  // Start main task
86  TaskMonitorAdd(TASKINFO_RUNNING_MANUALCONTROL, taskHandle);
87 
88  return 0;
89 }
90 
95 {
96  if (failsafe_control_initialize() == -1 \
98  || tablet_control_initialize() == -1 \
99  || geofence_control_initialize() == -1) {
100 
101  return -1;
102  }
103 
104  return 0;
105 }
106 
108 
112 static void manualControlTask(void *parameters)
113 {
114  /* Make sure disarmed on power up */
115  FlightStatusData flightStatus;
116  FlightStatusGet(&flightStatus);
117  flightStatus.Armed = FLIGHTSTATUS_ARMED_DISARMED;
118  FlightStatusSet(&flightStatus);
119 
120  // Select failsafe before run
122 
123  uint32_t arm_time = 1000;
124 
125  enum arm_state {
126  ARM_STATE_SAFETY,
127  ARM_STATE_DISARMED,
128  ARM_STATE_ARMING,
129  ARM_STATE_HOLDING,
130  ARM_STATE_ARMED,
131  } arm_state = ARM_STATE_SAFETY;
132 
133  uint32_t arm_state_time = 0;
134 
135  while (1) {
136 
137  // Process periodic data for each of the controllers, including reading
138  // all available inputs
143 
144  // Initialize to invalid value to ensure first update sets FlightStatus
145  static FlightStatusControlSourceOptions last_control_selection = -1;
146 
147  // Control logic to select the valid controller
148  FlightStatusControlSourceOptions control_selection =
150  bool reset_controller = control_selection != last_control_selection;
151 
152  int ret = -1;
153 
155 
156  // This logic would be better collapsed into control_source_select but
157  // in the case of the tablet we need to be able to abort and fall back
158  // to failsafe for now
159  switch(control_selection) {
160  case FLIGHTSTATUS_CONTROLSOURCE_TRANSMITTER:
161  ret = transmitter_control_select(reset_controller);
162  break;
163  case FLIGHTSTATUS_CONTROLSOURCE_TABLET:
164  ret = tablet_control_select(reset_controller);
165  break;
166  case FLIGHTSTATUS_CONTROLSOURCE_GEOFENCE:
167  ret = geofence_control_select(reset_controller);
168  break;
169  default:
170  /* Fall through into failsafe */
171  break;
172  }
173 
174  if (ret) {
175  failsafe_control_select(last_control_selection !=
176  FLIGHTSTATUS_CONTROLSOURCE_FAILSAFE);
177  control_selection = FLIGHTSTATUS_CONTROLSOURCE_FAILSAFE;
178  }
179 
180  if (control_selection != last_control_selection) {
181  FlightStatusControlSourceSet(&control_selection);
182  last_control_selection = control_selection;
183  }
184 
185  uint32_t now = PIOS_Thread_Systime();
186 
187 #define GO_STATE(x) \
188  do { \
189  arm_state = (x); \
190  arm_state_time = (now); \
191  } while (0)
192 
193  /* Global state transitions: weird stuff means disarmed +
194  * safety hold-down.
195  */
196  if ((status == STATUS_ERROR) ||
197  (status == STATUS_SAFETYTIMEOUT)) {
198  GO_STATE(ARM_STATE_SAFETY);
199  }
200 
201  /* If there's an alarm that prevents arming, enter a 0.2s
202  * no-arming-holddown. This also stretches transient alarms
203  * and makes them more visible.
204  */
205  if (!ok_to_arm() && (arm_state != ARM_STATE_ARMED)) {
206  GO_STATE(ARM_STATE_SAFETY);
207  }
208 
209  switch (arm_state) {
210  case ARM_STATE_SAFETY:
211  /* state_time > 0.2s + "NONE" or "DISARM" -> DISARMED
212  *
213  * !"NONE" and !"DISARM" -> SAFETY, reset state_time
214  */
215  if ((status == STATUS_NORMAL) ||
216  (status == STATUS_DISARM)) {
217  if (PIOS_Thread_Period_Elapsed(arm_state_time, 200)) {
218  GO_STATE(ARM_STATE_DISARMED);
219  }
220  } else {
221  GO_STATE(ARM_STATE_SAFETY);
222  }
223 
224  break;
225  case ARM_STATE_DISARMED:
226  /* "ARM_*" -> ARMING
227  * "DISCONNECTED" -> SAFETY
228  * "INVALID_FOR_DISARMED" -> SAFETY
229  */
230 
231  if ((status == STATUS_ARM_INVALID) ||
232  (status == STATUS_ARM_VALID) ||
233  (status == STATUS_TOGGLE_ARM)) {
234  GO_STATE(ARM_STATE_ARMING);
235  } else if ((status == STATUS_DISCONNECTED) ||
236  (status == STATUS_INVALID_FOR_DISARMED)) {
237  GO_STATE(ARM_STATE_SAFETY);
238  }
239 
240  ManualControlSettingsArmTimeOptions arm_enum;
241  ManualControlSettingsArmTimeGet(&arm_enum);
242 
243  arm_time = (arm_enum == MANUALCONTROLSETTINGS_ARMTIME_250) ? 250 :
244  (arm_enum == MANUALCONTROLSETTINGS_ARMTIME_500) ? 500 :
245  (arm_enum == MANUALCONTROLSETTINGS_ARMTIME_1000) ? 1000 :
246  (arm_enum == MANUALCONTROLSETTINGS_ARMTIME_2000) ? 2000 : 1000;
247 
248  break;
249  case ARM_STATE_ARMING:
250  /* Anything !"ARM_*" -> SAFETY
251  *
252  * state_time > ArmTime + ARM_INVALID -> HOLDING
253  * state_time > ArmTime + ARM_VALID -> ARMED
254  */
255 
256  if ((status != STATUS_ARM_INVALID) &&
257  (status != STATUS_ARM_VALID) &&
258  (status != STATUS_TOGGLE_ARM)) {
259  GO_STATE(ARM_STATE_SAFETY);
260  } else if (PIOS_Thread_Period_Elapsed(arm_state_time, arm_time)) {
261  if (status == STATUS_ARM_VALID) {
262  GO_STATE(ARM_STATE_ARMED);
263  } else {
264  /* Must be STATUS_ARM_INVALID
265  * or STATUS_TOGGLE_ARM */
266  GO_STATE(ARM_STATE_HOLDING);
267  }
268  }
269 
270  break;
271  case ARM_STATE_HOLDING:
272  /* ARM_VALID -> ARMED
273  * NONE -> ARMED
274  *
275  * ARM_INVALID -> Stay here
276  *
277  * Anything else -> SAFETY
278  */
279  if ((status == STATUS_ARM_VALID) ||
280  (status == STATUS_NORMAL)) {
281  GO_STATE(ARM_STATE_ARMED);
282  } else if ((status == STATUS_ARM_INVALID) ||
283  (status == STATUS_TOGGLE_ARM)) {
284  /* TODO: could consider having a maximum
285  * time before we go to safety */
286  } else {
287  GO_STATE(ARM_STATE_SAFETY);
288  }
289 
290  break;
291  case ARM_STATE_ARMED:
292  /* "DISARM" -> SAFETY (lower layer's job to check
293  * DisarmTime)
294  */
295  if (status == STATUS_DISARM ||
296  status == STATUS_TOGGLE_ARM) {
297  GO_STATE(ARM_STATE_SAFETY);
298  }
299 
300  break;
301  }
302 
303  FlightStatusArmedOptions armed, prev_armed;
304 
305  FlightStatusArmedGet(&prev_armed);
306 
307  switch (arm_state) {
308  default:
309  case ARM_STATE_SAFETY:
310  case ARM_STATE_DISARMED:
311  armed = FLIGHTSTATUS_ARMED_DISARMED;
312  vehicle_is_armed = false;
313  break;
314  case ARM_STATE_ARMING:
315  armed = FLIGHTSTATUS_ARMED_ARMING;
316  break;
317  case ARM_STATE_HOLDING:
318  /* For now consider "HOLDING" an armed state,
319  * like old code does. (Necessary to get the
320  * "initial spin while armed" that causes user
321  * to release arming position).
322  *
323  * TODO: do something different because
324  * control position invalid.
325  */
326  case ARM_STATE_ARMED:
327  armed = FLIGHTSTATUS_ARMED_ARMED;
328  vehicle_is_armed = true;
329  break;
330  }
331 
332  if (armed != prev_armed) {
333  FlightStatusArmedSet(&armed);
334  }
335 
336  // Wait until next update
339  }
340 }
341 
342 
353 static FlightStatusControlSourceOptions control_source_select()
354 {
355  // If the geofence controller is triggered, it takes precendence
356  // over even transmitter or failsafe. This means this must be
357  // EXTREMELY robust. The current behavior of geofence is simply
358  // to shut off the motors.
360  return FLIGHTSTATUS_CONTROLSOURCE_GEOFENCE;
361  }
362 
363  ManualControlCommandData cmd;
364  ManualControlCommandGet(&cmd);
365  if (cmd.Connected != MANUALCONTROLCOMMAND_CONNECTED_TRUE) {
366  return FLIGHTSTATUS_CONTROLSOURCE_FAILSAFE;
368  MANUALCONTROLSETTINGS_FLIGHTMODEPOSITION_TABLETCONTROL) {
369  return FLIGHTSTATUS_CONTROLSOURCE_TABLET;
370  } else {
371  return FLIGHTSTATUS_CONTROLSOURCE_TRANSMITTER;
372  }
373 
374 }
379 bool ok_to_arm(void)
380 {
381  // read alarms
382  SystemAlarmsData alarms;
383  SystemAlarmsGet(&alarms);
384 
385  // Check each alarm
386  for (int i = 0; i < SYSTEMALARMS_ALARM_NUMELEM; i++)
387  {
388  if (alarms.Alarm[i] >= SYSTEMALARMS_ALARM_ERROR &&
389  i != SYSTEMALARMS_ALARM_GPS &&
390  i != SYSTEMALARMS_ALARM_TELEMETRY)
391  {
392  return false;
393  }
394  }
395 
396  uint8_t flight_mode;
397  FlightStatusFlightModeGet(&flight_mode);
398 
399  if (flight_mode == FLIGHTSTATUS_FLIGHTMODE_FAILSAFE) {
400  /* Separately mask FAILSAFE arming here. */
401  return false;
402  }
403 
404  return true;
405 }
406 
Use tablet for control source.
static void manualControlTask(void *parameters)
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
Disconnected timeout has occurred.
Definition: control.h:36
User requested disarm, or low throt timeout.
Definition: control.h:38
int32_t ManualControlStart()
Definition: manualcontrol.c:79
Process transmitter inputs and use as control source.
static struct pios_thread * taskHandle
Definition: manualcontrol.c:65
bool vehicle_is_armed
Definition: manualcontrol.c:71
int32_t failsafe_control_select(bool reset_controller)
Use failsafe mode.
bool PIOS_WDG_RegisterFlag(uint16_t flag_requested)
Register a module against the watchdog.
Definition: pios_wdg.c:86
bool ok_to_arm(void)
Determine if the aircraft is safe to arm based on alarms.
bool PIOS_WDG_UpdateFlag(uint16_t flag)
Function called by modules to indicate they are still running.
Definition: pios_wdg.c:102
int32_t tablet_control_update()
Process updates for the tablet controller.
int32_t transmitter_control_select(bool reset_controller)
Select and use transmitter control.
#define UPDATE_PERIOD_MS
Definition: manualcontrol.c:62
Geofence controller that is enabled when out of zone.
int32_t failsafe_control_update()
Perform any updates to the failsafe controller.
int16_t status
Definition: main.c:61
enum control_status transmitter_control_get_status()
Get any control events.
int32_t geofence_control_update()
Perform any updates to the geofence controller.
#define STACK_SIZE_BYTES
Definition: manualcontrol.c:58
int32_t geofence_control_select(bool reset_controller)
Use geofence control mode.
int32_t ManualControlInitialize()
Definition: manualcontrol.c:94
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
Definition: taskmonitor.c:67
uint8_t i
Definition: msp_messages.h:97
Something is fundamentally wrong.
Definition: control.h:35
Failsafe controller when transmitter control is lost.
control_status
Definition: control.h:33
RCVR layer functions header.
#define GO_STATE(x)
bool PIOS_Thread_Period_Elapsed(const uint32_t prev_systime, const uint32_t increment_ms)
Determine if a period has elapsed since a datum.
Definition: pios_thread.c:281
#define PIOS_WDG_MANUAL
Definition: pios_wdg.h:38
bool geofence_control_activate()
Query if out of bounds and the geofence controller should take over.
RX is disconnected.
Definition: control.h:34
int32_t tablet_control_initialize()
Initialize the tablet controller.
Things are "normal".
Definition: control.h:40
User requested arm, controls in valid pos.
Definition: control.h:43
bool PIOS_RCVR_WaitActivity(uint32_t timeout_ms)
#define TASK_PRIORITY
Definition: manualcontrol.c:61
Includes PiOS and core architecture components.
int32_t transmitter_control_update()
Process inputs and arming.
static FlightStatusControlSourceOptions control_source_select()
control_source_select Determine which sub-module to use for the main control source of the flight con...
int32_t failsafe_control_initialize()
Initialize the failsafe controller.
User requested arm, controls in invalid pos.
Definition: control.h:42
int32_t transmitter_control_initialize()
Initialize the transmitter control mode.
Arming switch is in a state that doesn't.
Definition: control.h:46
For corners. Request we be the "opposite" mode.
Definition: control.h:41
static ManualControlCommandData cmd
uint8_t transmitter_control_get_flight_mode()
Query what the flight mode would be if this is selected.
int32_t tablet_control_select(bool reset_controller)
Use the values for the tablet controller.
MODULE_HIPRI_INITCALL(ManualControlInitialize, ManualControlStart)
int32_t geofence_control_initialize()
Initialize the geofence controller.