ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
fc_core.c
Go to the documentation of this file.
1
6#include "fc_core.h"
7#include "app_link.h"
8#include "params.h"
9#include "proto_v2.h"
10#include "spsc.h"
11#include "alt_ctrl.h"
12#include <string.h>
13#include <math.h>
14
15#define DEG2RAD 0.017453292519943295f
16
17volatile uint8_t g_fc_dbg_in_air;
18
19SPSC_DEFINE(s_cmdq, fc_cmd_t, 8u);
20SPSC_DEFINE(s_resq, fc_res_t, 8u);
23
25static volatile uint16_t s_sensors;
26
27/* ---- ground information from the AppTask ---- */
28static volatile uint8_t s_checks_ready, s_batt_fail, s_calibrating;
29
30/* ---- flight state (owned by the flight-control context) ---- */
32static uint8_t s_fs_long, s_fs_short_hold;
33static uint8_t s_took_off;
35static uint8_t s_have_last;
37static uint32_t s_rc_ms;
38static uint8_t s_have_rc;
39static float s_last_thr = -1.0f;
40static float s_stick[4];
41static uint8_t s_rc_stale = 1;
42static uint32_t s_silence;
43
44/* vertical state and automatic flight */
46static uint8_t s_have_v;
47static float s_h_ref; /* take-off point (FLIGHT.altRel = 0) */
48static float s_h_ground; /* ground level for lift-off detection */
49static float s_h_sp; /* height held */
50static float s_to_target; /* TAKEOFF target height (h frame) */
51static uint32_t s_to_t0;
53static uint32_t s_touch_ms, s_flow_bad_ms;
56
57/* battery */
59static volatile uint32_t s_batt_hb_ms;
60static volatile uint8_t s_batt_hb_set;
61
62/* motor test */
63static uint8_t s_test_motor, s_test_pct;
64static uint32_t s_test_until;
65
66/* ---- published snapshot ---- */
67static volatile uint32_t s_snap_seq;
69
70/* ================================================================== */
71void fc_init(const fc_config_t *cfg)
72{
73 s_cfg = *cfg;
74 s_sensors = cfg->sensors;
75 if (s_cfg.n_motors == 0u || s_cfg.n_motors > 8u) s_cfg.n_motors = 4u;
76 spsc_reset(&s_cmdq); spsc_reset(&s_resq); spsc_reset(&s_evq);
80 s_flight_ms = 0u; s_have_last = 0u; s_have_rc = 0u;
81 s_test_motor = 0u;
83 g_fc_dbg_in_air = 0u;
84 s_have_v = 0u; s_dbg_forced = 0u; s_landed = 0u; s_geofence = s_geofence_ev = 0u;
86 alt_ctrl_init(&s_ac, 0.45f);
87 memset(&s_sp, 0, sizeof s_sp);
88 spsc_reset(&s_batq);
90 s_batt_stale = cfg->batt_monitor; /* unknown until the first heartbeat */
92}
93
94int fc_batt_signal(const fc_batt_sig_t *b) { return spsc_push(&s_batq, b); }
95void fc_batt_heartbeat(uint32_t now) { s_batt_hb_ms = now; SPSC_BARRIER(); s_batt_hb_set = 1u; }
96
97void fc_set_sensors(uint16_t sensors) { s_sensors = sensors; }
98void fc_setpoint(fc_setpoint_t *out) { *out = s_sp; }
99
100uint8_t fc_is_core_op(uint8_t op)
101{
102 switch (op) {
103 case PV2_OP_ARM: case PV2_OP_DISARM: case PV2_OP_TAKEOFF: case PV2_OP_LAND:
104 case PV2_OP_RTH: case PV2_OP_HOLD: case PV2_OP_KILL: case PV2_OP_SET_MODE:
106 return 1u;
107 default:
108 return 0u;
109 }
110}
111
112int fc_post(const fc_cmd_t *c) { return spsc_push(&s_cmdq, c); }
113int fc_take_result(fc_res_t *r) { return spsc_pop(&s_resq, r); }
114int fc_take_event(fc_event_t *e) { return spsc_pop(&s_evq, e); }
115
116void fc_set_ground_info(uint8_t ready, uint8_t batt_fail, uint8_t calibrating)
117{
118 s_checks_ready = ready; s_batt_fail = batt_fail; s_calibrating = calibrating;
119}
120
122{
123 for (int tries = 0; tries < 8; ++tries) {
124 const uint32_t s0 = s_snap_seq;
125 if (s0 & 1u) continue;
126 SPSC_BARRIER();
127 fc_state_t v = s_snap;
128 SPSC_BARRIER();
129 if (s_snap_seq == s0) { *out = v; return; }
130 }
131 /* writer kept busy (cannot happen at 250 Hz): leave *out unchanged */
132}
133
134/* ================================================================== */
135static void event(uint8_t level, uint16_t code, int32_t arg)
136{
137 const fc_event_t e = { level, code, arg };
138 (void)spsc_push(&s_evq, &e); /* full: the AppTask is stalled, nothing to do */
139 if (s_cfg.on_event) s_cfg.on_event(&e);
140}
141
142static uint8_t has(uint16_t bits) { return (uint8_t)((s_sensors & bits) != 0u); }
143static uint8_t gnss(void) { return has(PV2_S_GNSS); }
144static uint8_t home_set(void) { return 0u; } /* no GNSS on this hardware yet */
145static uint8_t alt_sensor(void) { return has(PV2_S_BARO | PV2_S_TOF); }
146static uint8_t alt_ok(void) { return (uint8_t)(alt_sensor() && s_have_v && s_v.valid); }
147static uint8_t indoor(void) { return (uint8_t)(has(PV2_S_TOF) && !gnss()); }
148static float clampf(float v, float lo, float hi) { return v < lo ? lo : (v > hi ? hi : v); }
149
150/* top of the allowed space, in the h frame; *kind = EVENT 23 arg (0 alt, 1 ceiling) */
151static float ceiling_h_kind(int32_t *kind)
152{
153 float c = s_h_ref + PARAM(P_GF_MAX_ALT);
154 int32_t k = 0;
155 if (indoor() && s_v.tof_valid) {
156 const float floor_h = s_v.h - s_v.tof_h;
157 const float ci = floor_h + PARAM(P_GF_CEILING);
158 if (ci < c) { c = ci; k = 1; }
159 }
160 if (kind) *kind = k;
161 return c;
162}
163static float ceiling_h(void) { return ceiling_h_kind(0); }
164
165static void lift_off(void)
166{
167 s_in_air = 1u; s_took_off = 1u; s_landed = 0u; s_touch_ms = 0u;
168}
169
170static void touch_down(uint32_t now)
171{
172 s_in_air = 0u;
173 s_landed = 1u;
176 s_landed_ms = now;
177 s_touch_ms = 0u;
178}
179static uint8_t hold_mode(void) { return s_mode == PV2_MODE_ALT_HOLD || s_mode == PV2_MODE_POS_HOLD; }
180
181static void test_stop(void)
182{
183 if (s_test_motor && s_cfg.motor_test) s_cfg.motor_test(0u, 0.0f);
184 s_test_motor = 0u;
185}
186
187static void disarm(void)
188{
189 test_stop();
191 const uint8_t was = s_armed;
192 s_armed = 0u;
194 s_failsafe = 0u;
196 s_in_air = 0u; /* motors off: not flying any more */
197 s_dbg_forced = 0u;
198 s_geofence = 0u;
199 s_batt_lock = 0u;
200 if (was) event(PV2_EV_INFO, PV2_E_DISARMED, 0);
201}
202
203/* the battery monitor says no flight: level CRITICAL or worse, or it stopped */
204static uint8_t batt_unusable(void)
205{
206 return (uint8_t)(s_cfg.batt_monitor && (s_batt_stale || s_batt_level >= PV2_BATT_CRITICAL));
207}
208/* a battery failsafe is flying the drone down: other failsafes do not take over */
209static uint8_t batt_owns_auto(void)
210{
211 return (uint8_t)((s_failsafe & PV2_FS_BATTERY) && (s_auto == PV2_AUTO_LAND || s_auto == PV2_AUTO_RTH));
212}
213
214/* failsafe action value -> automatic action; RTH without GNSS + home -> land */
215static uint8_t fs_action(int p_idx)
216{
217 const int a = (int)(PARAM(p_idx) + 0.5f);
218 if (a == 2) return (gnss() && home_set()) ? PV2_AUTO_RTH : PV2_AUTO_LAND;
219 if (a == 1) return PV2_AUTO_LAND;
220 return PV2_AUTO_HOLD;
221}
222
223/* ================================================================== */
224static void result(const fc_cmd_t *c, uint8_t res, uint8_t why)
225{
226 if (c->op == FC_OP_MOTOR_STOP) return; /* internal: no ACK */
227 const fc_res_t r = { c->op, c->seq, res, why };
228 (void)spsc_push(&s_resq, &r);
229}
230
231static void exec(const fc_cmd_t *c, uint32_t now, uint8_t est_running)
232{
233#define OK() result(c, PV2_ACK_OK, PV2_R_NONE)
234#define REJ(why) result(c, PV2_ACK_REJECTED, (why))
235#define INVALID(w) result(c, PV2_ACK_INVALID, (w))
236 const uint8_t *p = c->p;
237
238 /* a running motor test stops on any other command */
239 if (c->op != PV2_OP_MOTOR_TEST) test_stop();
240
241 /* battery emergency: landing, no override except KILL (ยง6) */
242 if (s_batt_lock && (c->op == PV2_OP_SET_MODE || c->op == PV2_OP_HOLD || c->op == PV2_OP_RTH ||
243 c->op == PV2_OP_TAKEOFF)) { REJ(PV2_R_BATTERY_LOW); return; }
244
245 /* a cable to a PC: no arming, no take-off, no spinning motors (ADR 0009) */
246 if ((c->op == PV2_OP_ARM || c->op == PV2_OP_TAKEOFF || c->op == PV2_OP_MOTOR_TEST) &&
248 if (c->op == PV2_OP_ARM && s_armed) { REJ(PV2_R_ARMED); return; }
249 REJ(PV2_R_USB_CONNECTED); return;
250 }
251
252 switch (c->op) {
253 case PV2_OP_ARM: {
254 if (c->len < 2u || p[0] != PV2_ARM_MAGIC0 || p[1] != PV2_ARM_MAGIC1) { INVALID(PV2_R_NONE); return; }
255 if (s_armed) { REJ(PV2_R_ARMED); return; }
256 if (s_calibrating) { REJ(PV2_R_CALIBRATING); return; }
257 if (s_in_air) { REJ(PV2_R_NOT_LANDED); return; }
258 /* ELRS in control -> RADIO_OVERRIDE: no receiver on this hardware */
259 if (s_have_rc && (now - s_rc_ms) < FC_ARM_RC_FRESH_MS &&
260 s_rc.thr > (hold_mode() ? 100 : -900)) { REJ(PV2_R_THROTTLE_NOT_LOW); return; }
261 if (s_batt_fail || batt_unusable()) { REJ(PV2_R_BATTERY_LOW); return; }
262 /* never arm without a running attitude estimate, whatever armRequireChecks says */
263 if (!est_running || (PARAM(P_ARM_REQUIRE_CHECKS) > 0.5f && !s_checks_ready)) {
264 REJ(PV2_R_CHECKS_FAILING); return;
265 }
266 s_armed = 1u;
268 s_took_off = 0u; s_arm_ms = now; s_flight_ms = 0u;
269 s_landed = 0u; s_geofence_ev = 0u;
270 if (s_have_v && s_v.valid) { s_h_ref = s_h_ground = s_h_sp = s_v.h; }
271 alt_ctrl_reset(&s_ac, 0.0f);
273 OK();
275 return;
276 }
277 case PV2_OP_DISARM:
278 if (!s_armed) { OK(); return; }
279 if (s_in_air) { REJ(PV2_R_NOT_LANDED); return; }
280 OK();
281 disarm();
282 return;
283 case PV2_OP_TAKEOFF: {
285 if (!s_armed) { REJ(PV2_R_NOT_ARMED); return; }
286 if (s_in_air) { REJ(PV2_R_NOT_LANDED); return; }
287 if (!alt_ok()) { REJ(PV2_R_SENSOR_MISSING); return; } /* baro/ToF not ready yet */
288 if (batt_unusable()) { REJ(PV2_R_BATTERY_LOW); return; }
289 float alt = (c->len >= 2u) ? (float)pv2_rd_u16(p) / 100.0f : 0.0f;
290 if (alt <= 0.0f) alt = PARAM(P_TAKEOFF_ALT);
291 if (alt < 0.5f) alt = 0.5f;
292 if (alt > 3.0f) alt = 3.0f;
293 /* climb from where the drone stands, never above the ceiling */
294 s_h_ground = s_v.h;
295 s_to_target = s_h_ground + alt;
296 {
297 const float ct = ceiling_h() - 0.2f;
298 if (s_to_target > ct) s_to_target = ct;
299 }
301 s_to_t0 = now; s_to_lifted = 0u;
302 alt_ctrl_reset(&s_ac, 0.0f);
303 lift_off(); /* motors spool up: not landed any more (as the app simulator) */
305 OK();
306 event(PV2_EV_INFO, PV2_E_TAKEOFF_STARTED, (int32_t)(alt * 100.0f + 0.5f));
307 return;
308 }
309 case PV2_OP_LAND:
310 if (!s_armed || !s_in_air) { REJ(PV2_R_NOT_ARMED); return; }
312 s_touch_ms = 0u;
313 OK();
314 return;
315 case PV2_OP_RTH:
316 if (!gnss()) { REJ(PV2_R_NO_GPS); return; }
317 if (!home_set()) { REJ(PV2_R_NO_HOME); return; }
318 if (!s_armed || !s_in_air) { REJ(PV2_R_NOT_ARMED); return; }
320 OK();
322 return;
323 case PV2_OP_HOLD:
324 if (s_in_air) { s_auto = PV2_AUTO_HOLD; s_h_sp = s_v.h; }
325 OK();
326 return;
327 case PV2_OP_KILL: {
328 /* always executed, any state, in flight too */
329 if (c->len < 4u || p[0] != 0x4Bu || p[1] != 0x49u || p[2] != 0x4Cu || p[3] != 0x4Cu) {
330 INVALID(PV2_R_NONE); return;
331 }
332 if (s_cfg.motors_arm) s_cfg.motors_arm(0u); /* motors off before anything else */
333 test_stop();
334 OK();
335 disarm();
337 return;
338 }
339 case PV2_OP_SET_MODE: {
340 if (c->len < 1u || p[0] > PV2_MODE_POS_HOLD) { INVALID(PV2_R_NONE); return; }
341 const uint8_t m = p[0];
343 if (m == PV2_MODE_ALT_HOLD && !alt_sensor()) { REJ(PV2_R_SENSOR_MISSING); return; }
344 if (m == PV2_MODE_ACRO && PARAM(P_BEGINNER) > 0.5f) { REJ(PV2_R_NONE); return; }
345 if (m != s_mode && (m == PV2_MODE_ALT_HOLD || m == PV2_MODE_POS_HOLD)) s_h_sp = s_v.h;
346 s_mode = m;
347 /* a mode change cancels an automatic action (also a kept failsafe action) */
349 OK();
351 return;
352 }
353 case PV2_OP_MOTOR_TEST: {
355 if (c->len < 5u) { INVALID(PV2_R_NONE); return; }
356 const uint8_t m = p[0], pct = p[1];
357 const uint16_t ms = pv2_rd_u16(&p[2]);
358 if (p[4] != PV2_PROPS_OFF_MAGIC) { REJ(PV2_R_PROPS_CONFIRM_MISSING); return; }
359 if (s_armed) { REJ(PV2_R_ARMED); return; }
360 if (m < 1u || m > s_cfg.n_motors || pct > 20u || ms > 3000u) { INVALID(PV2_R_OUT_OF_RANGE); return; }
361 test_stop();
362 s_test_motor = m; s_test_pct = pct; s_test_until = now + ms;
363 if (s_cfg.motor_test) s_cfg.motor_test(m, (float)pct / 100.0f);
364 OK();
365 return;
366 }
367 case FC_OP_MOTOR_STOP:
368 return; /* test_stop() done above */
369 default:
371 return;
372 }
373#undef OK
374#undef REJ
375#undef INVALID
376}
377
378/* ================================================================== */
379static void update_sticks(uint32_t now)
380{
381 appl_rc_t rc;
382 uint32_t t;
383 if (appl_rc_read(&rc, &t)) { s_rc = rc; s_rc_ms = t; s_have_rc = 1u; }
384 s_rc_stale = (uint8_t)(!s_have_rc || (now - s_rc_ms) > FC_RC_STALE_MS);
385 if (!s_rc_stale) {
386 s_stick[0] = (float)s_rc.thr / 1000.0f;
387 s_stick[1] = (float)s_rc.yaw / 1000.0f;
388 s_stick[2] = (float)s_rc.pitch / 1000.0f;
389 s_stick[3] = (float)s_rc.roll / 1000.0f;
390 s_last_thr = s_stick[0];
391 } else {
392 /* stale RC: the last mode's neutral -- level attitude, no yaw rate;
393 * altitude hold modes: centre stick = hold altitude; otherwise keep
394 * the last collective (no altitude sensor to do better) */
395 s_stick[0] = hold_mode() ? 0.0f : (s_armed ? s_last_thr : -1.0f);
396 s_stick[1] = s_stick[2] = s_stick[3] = 0.0f;
397 }
398}
399
400static int stick_moved(void)
401{
402 if (s_rc_stale) return 0;
403 const int16_t v[4] = { s_rc.thr, s_rc.yaw, s_rc.pitch, s_rc.roll };
404 for (int i = 0; i < 4; ++i) if (v[i] > FC_STICK_CANCEL || v[i] < -FC_STICK_CANCEL) return 1;
405 return 0;
406}
407
408static void app_link_failsafe(uint32_t now)
409{
410 /* a GAP disconnection counts as at least the short silence: earlier, never later */
411 const uint32_t sil = appl_silence_ms(now, FC_FS_SHORT_MS + 1u);
412 s_silence = sil;
413 if (!s_armed) return;
414
415 if (!s_in_air) {
416 /* armed on the ground: disarm after 3 s of silence */
417 if (sil > FC_GROUND_DISARM_MS) disarm();
418 return;
419 }
420 /* ELRS in control: the app link does not matter (no receiver on this hardware) */
421 if (batt_owns_auto()) {
422 /* landing for the battery: the link loss is only flagged */
424 else if (sil < FC_LINK_BACK_MS) s_failsafe &= (uint8_t)~PV2_FS_APP_LINK;
425 return;
426 }
427 if (sil > FC_FS_LONG_MS && !s_fs_long) {
430 s_fs_long = 1u;
431 s_fs_short_hold = 0u;
433 } else if (sil > FC_FS_SHORT_MS && !(s_failsafe & PV2_FS_APP_LINK)) {
436 s_fs_short_hold = (uint8_t)(s_auto == PV2_AUTO_HOLD);
437 } else if (sil < FC_LINK_BACK_MS && (s_failsafe & PV2_FS_APP_LINK)) {
438 s_failsafe &= (uint8_t)~PV2_FS_APP_LINK;
439 /* short HOLD only: control back to the pilot. A long action is kept
440 * until the pilot changes mode or cancels it. */
442 s_fs_short_hold = 0u;
443 }
444}
445
446/* ---- battery (ยง6): signals from the BattTask, failsafes ---- */
447static void battery(uint32_t now)
448{
449 if (!s_cfg.batt_monitor) return;
451 while (spsc_pop(&s_batq, &b)) {
452 if (b.level > s_batt_level) {
453 /* crossing a threshold downwards: one event (ยง11 codes 30/31/32) */
454 static const uint8_t lv[4] = { 0, PV2_EV_WARN, PV2_EV_ERROR, PV2_EV_CRITICAL };
455 static const uint16_t code[4] = { 0, PV2_E_BATTERY_LOW, PV2_E_BATTERY_CRITICAL, PV2_E_BATTERY_EMERGENCY };
456 if (b.level <= PV2_BATT_EMERGENCY) event(lv[b.level], code[b.level], b.cell_mv);
457 }
459 s_batt_present = b.present;
460 }
461
462 /* the BattTask must be alive: its heartbeat is the only proof */
463 uint8_t stale = 1u;
464 if (s_batt_hb_set) {
465 SPSC_BARRIER();
466 stale = (uint8_t)((now - s_batt_hb_ms) > FC_BATT_STALE_MS);
467 s_batt_have_hb = 1u;
468 }
469 if (stale && !s_batt_stale && s_batt_have_hb)
471 s_batt_stale = stale;
472
473 if (!s_armed || !s_in_air) return;
475 /* RTH needs GNSS, home and enough charge to get there: none on this hardware */
476 const uint8_t act = (gnss() && home_set()) ? PV2_AUTO_RTH : PV2_AUTO_LAND;
478 if (s_auto != PV2_AUTO_LAND && s_auto != PV2_AUTO_RTH) { s_auto = act; s_touch_ms = 0u; }
479 s_fs_short_hold = 0u;
481 }
483 s_batt_lock = 1u;
486 }
487 if (s_batt_lock) s_auto = PV2_AUTO_LAND; /* nothing but KILL overrides it */
488}
489
490/* ---- in air / on ground ---- */
491static void air_ground(uint32_t now, uint32_t dt)
492{
493 /* bench override (no altitude sensor, or forced): debugger flag */
494 if (s_armed && g_fc_dbg_in_air && !s_in_air) {
495 lift_off(); s_dbg_forced = 1u;
497 return;
498 }
499 if (s_dbg_forced) {
500 if (!g_fc_dbg_in_air && s_in_air) { s_dbg_forced = 0u; touch_down(now); }
501 return;
502 }
503 if (!s_armed || !alt_ok()) return;
504
505 const float dh = s_v.h - s_h_ground;
506 if (!s_in_air) {
507 /* manual flight (or lifted by hand on the bench) */
508 if (dh > FC_LIFTOFF_DH + 0.05f && !s_v.tof_ground) lift_off();
509 return;
510 }
511
512 if (s_auto == PV2_AUTO_TAKEOFF) {
513 if (!s_to_lifted) {
514 if (dh > FC_LIFTOFF_DH) s_to_lifted = 1u;
515 else if ((now - s_to_t0) > FC_LIFTOFF_TIMEOUT_MS) {
516 /* no lift-off: stop climbing, back on the ground, motors at idle */
518 s_in_air = 0u;
519 s_took_off = 0u;
520 s_arm_ms = now; /* the no-take-off auto-disarm counts again */
522 return;
523 }
524 }
525 if (s_v.h >= s_to_target - 0.05f) {
526 /* target reached: hold it in the mode the sensors allow (ยง7.2) */
531 }
532 return; /* no touchdown while taking off */
533 }
534
535 /* touchdown: descent commanded, no vertical motion, close to the floor */
536 uint8_t descending;
537 if (s_auto == PV2_AUTO_LAND || s_auto == PV2_AUTO_RTH) descending = 1u;
538 else if (s_auto != PV2_AUTO_NONE) descending = 0u;
539 else if (hold_mode()) descending = (uint8_t)(s_stick[0] < -0.3f);
540 else descending = (uint8_t)(s_stick[0] < -0.9f);
541 const uint8_t tof = (uint8_t)(has(PV2_S_TOF) && (s_v.tof_valid || s_v.tof_ground));
542 const uint8_t near = tof ? (uint8_t)(s_v.tof_ground || s_v.tof_h < 0.08f) : 1u;
543 if (descending && near && fabsf(s_v.vz) < FC_TOUCH_VZ) s_touch_ms += dt;
544 else s_touch_ms = 0u;
546}
547
548/* ---- flow lost in POS_HOLD without GNSS (ยง6, fsFlowLost) ---- */
549static void flow_failsafe(uint32_t dt)
550{
551 if (!has(PV2_S_FLOW) || gnss()) { s_failsafe &= (uint8_t)~PV2_FS_FLOW; return; }
552 const uint8_t q = s_have_v ? s_v.flow_q : 0xFFu;
553 const uint8_t bad = (uint8_t)(q == 0xFFu || q < FC_FLOW_LOST_Q || !s_v.flow_usable);
554 /* below the PMW3901 minimum height (on the floor, lift-off, last centimetres of
555 * a landing) the flow is invalid by design: not a failure */
556 const uint8_t low = (uint8_t)(s_v.tof_ground || (s_v.tof_valid && s_v.tof_h < FC_FLOW_CHECK_H));
557 const uint8_t judged = (uint8_t)(s_auto != PV2_AUTO_TAKEOFF && s_auto != PV2_AUTO_LAND && !low);
558 if (s_armed && s_in_air && s_mode == PV2_MODE_POS_HOLD && judged && bad) {
559 s_flow_bad_ms += dt;
564 else { s_mode = PV2_MODE_ALT_HOLD; s_h_sp = s_v.h; }
565 }
566 } else {
567 s_flow_bad_ms = 0u;
568 if ((s_failsafe & PV2_FS_FLOW) && q != 0xFFu && q >= FC_FLOW_BACK_Q) s_failsafe &= (uint8_t)~PV2_FS_FLOW;
569 }
570}
571
572/* ---- setpoints for the control loops ---- */
573static void setpoints(uint32_t now, uint32_t dt_ms)
574{
575 (void)now;
576 static uint8_t prev_auto;
577 const float dt = (float)dt_ms * 1e-3f;
578 const float beg = (PARAM(P_BEGINNER) > 0.5f) ? 0.5f : 1.0f;
580 memset(&sp, 0, sizeof sp);
581 sp.armed = s_armed; sp.mode = s_mode; sp.autom = s_auto;
582
583 /* entering HOLD / failsafe HOLD: hold the current height */
584 if (s_auto != prev_auto && (s_auto == PV2_AUTO_HOLD || s_auto == PV2_AUTO_FAILSAFE)) s_h_sp = s_v.h;
585 prev_auto = s_auto;
586
587 /* attitude from the sticks (neutral when the RC is stale) */
588 const float tilt = PARAM(P_MAX_TILT) * beg * DEG2RAD;
589 const float yawr = PARAM(P_MAX_YAW_RATE) * beg * DEG2RAD;
590 sp.roll = s_stick[3] * tilt;
591 sp.pitch = s_stick[2] * tilt;
592 sp.roll_rate = s_stick[3] * 4.0f * yawr; /* ACRO: 4x the yaw rate scale */
593 sp.pitch_rate = s_stick[2] * 4.0f * yawr;
594 sp.yaw_rate = s_stick[1] * yawr;
595
596 if (!s_armed) { s_sp = sp; return; }
597 sp.pos_hold = (uint8_t)(s_in_air && (s_mode == PV2_MODE_POS_HOLD || s_auto == PV2_AUTO_HOLD) &&
599
600 const uint8_t vert = (uint8_t)(s_in_air && alt_ok() &&
601 (s_auto != PV2_AUTO_NONE || hold_mode()));
602 if (!vert) {
603 /* manual collective (ACRO / ANGLE, or no altitude estimate) */
604 sp.thrust = clampf(0.5f * (s_stick[0] + 1.0f), 0.0f, 1.0f);
605 if (hold_mode() && !s_in_air) sp.thrust = 0.0f; /* hold modes: take off with TAKEOFF */
606 s_sp = sp;
607 return;
608 }
609
610 const float maxc = PARAM(P_MAX_CLIMB) * beg;
611 const float land = PARAM(P_LAND_SPEED);
612 float vz;
613 switch (s_auto) {
614 case PV2_AUTO_TAKEOFF:
616 s_h_sp = s_v.h;
617 break;
618 case PV2_AUTO_LAND:
619 case PV2_AUTO_RTH: /* no GNSS here: RTH lands in place */
620 vz = -((has(PV2_S_TOF) && s_v.tof_valid && s_v.tof_h < 1.0f) ? FC_LAND_SLOW_VZ : land);
621 if (s_v.tof_ground) vz = -FC_LAND_SLOW_VZ;
622 s_h_sp = s_v.h;
623 break;
624 case PV2_AUTO_HOLD:
626 vz = alt_ctrl_vz_sp(&s_ac, s_h_sp, s_v.h, maxc, land);
627 break;
628 default: /* ALT_HOLD / POS_HOLD, pilot: climb rate */
629 if (fabsf(s_stick[0]) < 0.05f) vz = alt_ctrl_vz_sp(&s_ac, s_h_sp, s_v.h, maxc, land);
630 else { vz = s_stick[0] * maxc; s_h_sp = s_v.h; }
631 break;
632 }
633
634 /* geofence: no climb above the ceiling, come back below it (ยง6, gfMaxAlt / gfCeiling) */
635 int32_t gf_kind;
636 const float ceil = ceiling_h_kind(&gf_kind);
637 if (s_v.h >= ceil && vz > 0.0f) {
638 vz = (s_v.h > ceil + 0.3f) ? -0.3f : 0.0f;
639 if (s_h_sp > ceil) s_h_sp = ceil;
641 if (!s_geofence) {
642 s_geofence = 1u;
645 }
646 } else if (s_v.h < ceil - 0.2f && s_geofence) {
647 s_geofence = 0u;
648 s_failsafe &= (uint8_t)~PV2_FS_GEOFENCE;
649 s_geofence_ev = 0u;
650 }
651
652 /* take-off spool-up: thrust ramps from idle until the drone leaves the ground;
653 * the ramp is the controller's upper limit, so the integrator does not wind up */
654 const float thr_max = s_ac.thr_max;
656 const float ramp = 0.10f + 0.40f * (float)(now - s_to_t0) * 1e-3f;
657 if (ramp < s_ac.thr_max) s_ac.thr_max = ramp;
658 }
659 const float thr = alt_ctrl_step(&s_ac, vz, s_v.vz, s_v.cos_tilt, dt);
660 s_ac.thr_max = thr_max;
661 sp.alt_ctrl = 1u;
662 sp.thrust = thr;
663 sp.vz_sp = vz;
664 sp.h_sp = s_h_sp;
665 s_sp = sp;
666}
667
668static void publish(void)
669{
670 s_snap_seq++;
671 SPSC_BARRIER();
677 s_snap.flight_time_s = (uint16_t)((s_flight_ms / 1000u > 0xFFFFu) ? 0xFFFFu : s_flight_ms / 1000u);
678 memset(s_snap.motor_out_pct, 0, sizeof s_snap.motor_out_pct);
685 if (s_have_v && s_v.valid) {
686 const float a = (s_v.h - (s_armed || s_took_off ? s_h_ref : s_v.h)) * 100.0f;
687 s_snap.alt_rel_cm = (int32_t)(a + (a >= 0.0f ? 0.5f : -0.5f));
688 const float w = clampf(s_v.vz * 100.0f, -32767.0f, 32767.0f);
689 s_snap.vz_cms = (int16_t)w;
690 } else {
692 }
694 memcpy(s_snap.stick, s_stick, sizeof s_stick);
695 SPSC_BARRIER();
696 s_snap_seq++;
697}
698
699void fc_service(uint32_t now, uint8_t est_running, const fc_vert_t *v)
700{
701 const uint32_t dt = s_have_last ? (now - s_last_ms) : 0u;
702 s_last_ms = now; s_have_last = 1u;
703 if (v) { s_v = *v; s_have_v = 1u; } else { s_have_v = 0u; s_v.valid = 0u; s_v.flow_q = 0xFFu; }
704
705 update_sticks(now);
706
707 fc_cmd_t c;
708 while (spsc_pop(&s_cmdq, &c)) exec(&c, now, est_running);
709
710 air_ground(now, dt);
711
713 flow_failsafe(dt);
714 battery(now);
715
716 /* ---- pilot cancels HOLD (and RTH, if allowed) with the sticks ---- */
719 if (s_auto == PV2_AUTO_RTH && PARAM(P_RTH_STICK_CANCEL) > 0.5f &&
723 }
724
725 /* ---- automatic disarm ---- */
726 if (s_armed && !s_in_air) {
727 if (!s_took_off && (now - s_arm_ms) > FC_NO_TAKEOFF_MS) disarm();
728 else if (s_took_off && (now - s_landed_ms) > (uint32_t)(PARAM(P_AUTO_DISARM) * 1000.0f)) disarm();
729 }
730 if (s_armed && s_in_air) s_flight_ms += dt;
731
732 /* ---- motor test: stops after ms, on link loss ---- */
733 if (s_test_motor && ((int32_t)(now - s_test_until) >= 0 || s_silence > FC_FS_SHORT_MS)) test_stop();
734
735 setpoints(now, dt);
736 publish();
737}
float alt_ctrl_vz_sp(const alt_ctrl_t *c, float h_sp, float h, float up, float dn)
Definition alt_ctrl.c:19
void alt_ctrl_reset(alt_ctrl_t *c, float integ)
Definition alt_ctrl.c:17
void alt_ctrl_init(alt_ctrl_t *c, float hover)
Definition alt_ctrl.c:8
float alt_ctrl_step(alt_ctrl_t *c, float vz_sp, float vz, float ct, float dt)
Definition alt_ctrl.c:24
Vertical control: height -> climb rate -> collective thrust.
double sp
Definition calib_mgr.c:42
double b[6]
Definition calib_mgr.c:53
static uint8_t s_batt_level
Definition fc_core.c:58
static void setpoints(uint32_t now, uint32_t dt_ms)
Definition fc_core.c:573
int fc_batt_signal(const fc_batt_sig_t *b)
0 = queued, -1 = full
Definition fc_core.c:94
static uint32_t s_last_ms
Definition fc_core.c:34
uint8_t fc_is_core_op(uint8_t op)
ops executed by the core
Definition fc_core.c:100
static uint8_t s_failsafe
Definition fc_core.c:31
static uint32_t s_rc_ms
Definition fc_core.c:37
static uint8_t s_armed
Definition fc_core.c:31
void fc_service(uint32_t now, uint8_t est_running, const fc_vert_t *v)
v: NULL = no vertical estimate (in air only via g_fc_dbg_in_air)
Definition fc_core.c:699
static fc_state_t s_snap
Definition fc_core.c:68
void fc_set_ground_info(uint8_t ready, uint8_t batt_fail, uint8_t calibrating)
ground information computed by the AppTask (atomic byte stores)
Definition fc_core.c:116
static uint8_t gnss(void)
Definition fc_core.c:143
static uint8_t has(uint16_t bits)
Definition fc_core.c:142
static uint8_t s_test_pct
Definition fc_core.c:63
static void app_link_failsafe(uint32_t now)
Definition fc_core.c:408
int fc_take_result(fc_res_t *r)
1 if a result was taken
Definition fc_core.c:113
static uint8_t batt_owns_auto(void)
Definition fc_core.c:209
static appl_rc_t s_rc
Definition fc_core.c:36
static float s_to_target
Definition fc_core.c:50
#define INVALID(w)
static uint32_t s_landed_ms
Definition fc_core.c:34
static uint8_t s_batt_have_hb
Definition fc_core.c:58
static void event(uint8_t level, uint16_t code, int32_t arg)
Definition fc_core.c:135
static uint8_t s_took_off
Definition fc_core.c:33
static fc_setpoint_t s_sp
Definition fc_core.c:55
static void flow_failsafe(uint32_t dt)
Definition fc_core.c:549
static uint8_t batt_unusable(void)
Definition fc_core.c:204
void fc_setpoint(fc_setpoint_t *out)
last setpoints, same context
Definition fc_core.c:98
static uint8_t s_auto
Definition fc_core.c:31
static alt_ctrl_t s_ac
Definition fc_core.c:54
static uint8_t home_set(void)
Definition fc_core.c:144
static void publish(void)
Definition fc_core.c:668
static fc_vert_t s_v
Definition fc_core.c:45
static uint8_t s_batt_lock
Definition fc_core.c:58
void fc_batt_heartbeat(uint32_t now)
every BattTask step
Definition fc_core.c:95
static uint8_t alt_sensor(void)
Definition fc_core.c:145
static uint32_t s_silence
Definition fc_core.c:42
static uint8_t s_batt_present
Definition fc_core.c:58
static void update_sticks(uint32_t now)
Definition fc_core.c:379
static uint8_t s_have_last
Definition fc_core.c:35
#define OK()
static int stick_moved(void)
Definition fc_core.c:400
static uint8_t s_fs_long
Definition fc_core.c:32
static float s_last_thr
Definition fc_core.c:39
static uint32_t s_flow_bad_ms
Definition fc_core.c:53
static void disarm(void)
Definition fc_core.c:187
static void test_stop(void)
Definition fc_core.c:181
static uint32_t s_flight_ms
Definition fc_core.c:34
static volatile uint8_t s_checks_ready
Definition fc_core.c:28
static void exec(const fc_cmd_t *c, uint32_t now, uint8_t est_running)
Definition fc_core.c:231
static volatile uint32_t s_batt_hb_ms
Definition fc_core.c:59
static uint8_t s_geofence_ev
Definition fc_core.c:52
static uint8_t s_fs_short_hold
Definition fc_core.c:32
static uint32_t s_arm_ms
Definition fc_core.c:34
static void battery(uint32_t now)
Definition fc_core.c:447
static void air_ground(uint32_t now, uint32_t dt)
Definition fc_core.c:491
static float ceiling_h_kind(int32_t *kind)
Definition fc_core.c:151
static void lift_off(void)
Definition fc_core.c:165
static uint32_t s_test_until
Definition fc_core.c:64
static void touch_down(uint32_t now)
Definition fc_core.c:170
static volatile uint8_t s_batt_hb_set
Definition fc_core.c:60
static uint8_t s_dbg_forced
Definition fc_core.c:52
static uint8_t fs_action(int p_idx)
Definition fc_core.c:215
int fc_post(const fc_cmd_t *c)
0 = queued, -1 = full
Definition fc_core.c:112
static uint8_t s_geofence
Definition fc_core.c:52
static uint8_t s_have_v
Definition fc_core.c:46
static uint8_t indoor(void)
Definition fc_core.c:147
void fc_init(const fc_config_t *cfg)
Definition fc_core.c:71
static uint8_t s_have_rc
Definition fc_core.c:38
static float s_h_ground
Definition fc_core.c:48
static volatile uint8_t s_batt_fail
Definition fc_core.c:28
static volatile uint8_t s_calibrating
Definition fc_core.c:28
static uint8_t s_test_motor
Definition fc_core.c:63
static uint8_t hold_mode(void)
Definition fc_core.c:179
static float s_stick[4]
Definition fc_core.c:40
static uint8_t s_batt_stale
Definition fc_core.c:58
static float s_h_ref
Definition fc_core.c:47
static uint8_t s_mode
Definition fc_core.c:31
void fc_set_sensors(uint16_t sensors)
sensors detected at run time (a late expansion board): atomic store
Definition fc_core.c:97
static float ceiling_h(void)
Definition fc_core.c:163
void fc_snapshot(fc_state_t *out)
Definition fc_core.c:121
static float s_h_sp
Definition fc_core.c:49
static fc_config_t s_cfg
Definition fc_core.c:24
static void result(const fc_cmd_t *c, uint8_t res, uint8_t why)
Definition fc_core.c:224
static uint32_t s_touch_ms
Definition fc_core.c:53
static volatile uint16_t s_sensors
Definition fc_core.c:25
int fc_take_event(fc_event_t *e)
Definition fc_core.c:114
volatile uint8_t g_fc_dbg_in_air
Definition fc_core.c:17
static uint8_t s_rc_stale
Definition fc_core.c:41
static float clampf(float v, float lo, float hi)
Definition fc_core.c:148
static uint8_t s_landed
Definition fc_core.c:52
static uint8_t alt_ok(void)
Definition fc_core.c:146
static uint32_t s_to_t0
Definition fc_core.c:51
#define DEG2RAD
Definition fc_core.c:15
static uint8_t s_to_lifted
Definition fc_core.c:52
static volatile uint32_t s_snap_seq
Definition fc_core.c:67
static uint8_t s_in_air
Definition fc_core.c:31
#define REJ(why)
Flight core: arming, modes, automatic actions, failsafes, motor test (HANDOVER_FW....
#define FC_FS_LONG_MS
fsAppLong + EVENT 20
Definition fc_core.h:30
#define FC_FS_SHORT_MS
fsAppShort
Definition fc_core.h:29
#define FC_LIFTOFF_TIMEOUT_MS
no lift-off -> TAKEOFF aborted
Definition fc_core.h:39
#define FC_ARM_RC_FRESH_MS
throttle rule applies to RC this recent
Definition fc_core.h:33
#define FC_LIFTOFF_DH
m above the ground level = airborne
Definition fc_core.h:38
#define FC_FLOW_LOST_Q
flow quality below this ...
Definition fc_core.h:44
#define FC_FLOW_LOST_MS
Definition fc_core.h:46
#define FC_TOUCH_MS
... for this long (descent commanded)
Definition fc_core.h:42
#define FC_GROUND_DISARM_MS
armed on the ground, app silent
Definition fc_core.h:32
#define FC_NO_TAKEOFF_MS
auto-disarm if not taken off
Definition fc_core.h:34
#define FC_FLOW_BACK_Q
Definition fc_core.h:45
#define FC_LINK_BACK_MS
link considered back
Definition fc_core.h:31
#define FC_TOUCH_MS_NO_TOF
Definition fc_core.h:43
#define FC_OP_MOTOR_STOP
stop a running motor test
Definition fc_core.h:53
#define FC_FLOW_CHECK_H
Definition fc_core.h:47
#define FC_TOUCH_VZ
|vz| below this ...
Definition fc_core.h:41
#define FC_BATT_STALE_MS
no BattTask heartbeat: battery unknown
Definition fc_core.h:50
#define FC_STICK_CANCEL
30 % of the stick
Definition fc_core.h:35
#define FC_RC_STALE_MS
stop using the last RC values
Definition fc_core.h:28
#define FC_LAND_SLOW_VZ
m/s below 1 m with ToF
Definition fc_core.h:40
#define FC_TAKEOFF_VZ
m/s, climb during TAKEOFF
Definition fc_core.h:37
uint16_t ms
Definition log_task.c:42
Drone parameters of protocol v2 (HANDOVER_FW.md ยง8, ids.ts PARAMS).
#define PARAM(i)
Definition params.h:43
@ P_TAKEOFF_ALT
Definition params.h:19
@ P_FS_APP_LONG
Definition params.h:21
@ P_MAX_YAW_RATE
Definition params.h:19
@ P_GF_CEILING
Definition params.h:23
@ P_AUTO_DISARM
Definition params.h:19
@ P_LAND_SPEED
Definition params.h:19
@ P_FS_APP_SHORT
Definition params.h:21
@ P_MAX_TILT
Definition params.h:19
@ P_GF_MAX_ALT
Definition params.h:23
@ P_RTH_STICK_CANCEL
Definition params.h:24
@ P_MAX_CLIMB
Definition params.h:19
@ P_ARM_REQUIRE_CHECKS
Definition params.h:25
@ P_BEGINNER
Definition params.h:20
@ P_FS_FLOW_LOST
Definition params.h:21
ACwing Pilot app protocol v2: identifiers and frame layouts.
@ PV2_ACK_UNSUPPORTED
Definition proto_v2.h:88
static uint16_t pv2_rd_u16(const uint8_t *p)
little-endian readers for command payloads (unaligned-safe)
Definition proto_v2.h:329
@ PV2_R_NONE
Definition proto_v2.h:91
@ PV2_R_CHECKS_FAILING
Definition proto_v2.h:91
@ PV2_R_SENSOR_MISSING
Definition proto_v2.h:95
@ PV2_R_NOT_LANDED
Definition proto_v2.h:91
@ PV2_R_ARMED
Definition proto_v2.h:93
@ PV2_R_USB_CONNECTED
ARM / TAKEOFF / MOTOR_TEST while a USB host is connected (ADR 0009)
Definition proto_v2.h:96
@ PV2_R_CALIBRATING
Definition proto_v2.h:93
@ PV2_R_NO_GPS
Definition proto_v2.h:92
@ PV2_R_OUT_OF_RANGE
Definition proto_v2.h:94
@ PV2_R_NO_HOME
Definition proto_v2.h:92
@ PV2_R_NOT_ARMED
Definition proto_v2.h:93
@ PV2_R_BATTERY_LOW
Definition proto_v2.h:92
@ PV2_R_THROTTLE_NOT_LOW
Definition proto_v2.h:91
@ PV2_R_PROPS_CONFIRM_MISSING
Definition proto_v2.h:93
#define PV2_OP_HOLD
Definition proto_v2.h:56
#define PV2_ARM_MAGIC0
Definition proto_v2.h:81
#define PV2_PROPS_OFF_MAGIC
'P'
Definition proto_v2.h:83
#define PV2_OP_RTH
Definition proto_v2.h:55
#define PV2_OP_MOTOR_TEST
Definition proto_v2.h:70
@ PV2_E_MODE_CHANGED
Definition proto_v2.h:141
@ PV2_E_DISARMED
Definition proto_v2.h:140
@ PV2_E_TAKEOFF_STARTED
Definition proto_v2.h:140
@ PV2_E_RTH_STARTED
Definition proto_v2.h:141
@ PV2_E_BATTERY_EMERGENCY
Definition proto_v2.h:145
@ PV2_E_FAILSAFE_BATTERY
Definition proto_v2.h:143
@ PV2_E_TAKEOFF_ABORTED
firmware extension (WARN): no lift-off; the app shows the text
Definition proto_v2.h:142
@ PV2_E_BATTERY_LOW
Definition proto_v2.h:145
@ PV2_E_FAILSAFE_APP_LINK
Definition proto_v2.h:143
@ PV2_E_LANDED
Definition proto_v2.h:140
@ PV2_E_BATTERY_MONITOR_LOST
firmware extension (ERROR): BattTask silent, battery unknown
Definition proto_v2.h:146
@ PV2_E_BATTERY_CRITICAL
Definition proto_v2.h:145
@ PV2_E_ARMED
Definition proto_v2.h:140
@ PV2_E_FLOW_LOST
Definition proto_v2.h:144
@ PV2_E_MOTOR_KILLED
Definition proto_v2.h:149
@ PV2_E_GEOFENCE_BREACH
Definition proto_v2.h:144
#define PV2_FS_GEOFENCE
Definition proto_v2.h:110
#define PV2_S_TOF
Definition proto_v2.h:127
#define PV2_S_FLOW
Definition proto_v2.h:126
#define PV2_S_BARO
Definition proto_v2.h:122
#define PV2_FS_BATTERY
Definition proto_v2.h:109
#define PV2_FS_APP_LINK
Definition proto_v2.h:107
#define PV2_OP_SET_MODE
Definition proto_v2.h:58
#define PV2_OP_DISARM
Definition proto_v2.h:52
@ PV2_INPUT_NONE
Definition proto_v2.h:105
@ PV2_INPUT_APP
Definition proto_v2.h:105
#define PV2_FS_FLOW
Definition proto_v2.h:112
#define PV2_ARM_MAGIC1
Definition proto_v2.h:82
#define PV2_OP_KILL
Definition proto_v2.h:57
#define PV2_OP_ARM
Definition proto_v2.h:51
@ PV2_EV_INFO
Definition proto_v2.h:137
@ PV2_EV_WARN
Definition proto_v2.h:137
@ PV2_EV_ERROR
Definition proto_v2.h:137
@ PV2_EV_CRITICAL
Definition proto_v2.h:137
@ PV2_AUTO_NONE
Definition proto_v2.h:103
@ PV2_AUTO_TAKEOFF
Definition proto_v2.h:103
@ PV2_AUTO_LAND
Definition proto_v2.h:103
@ PV2_AUTO_RTH
Definition proto_v2.h:103
@ PV2_AUTO_FAILSAFE
Definition proto_v2.h:104
@ PV2_AUTO_HOLD
Definition proto_v2.h:104
#define PV2_OP_LAND
Definition proto_v2.h:54
#define PV2_S_GNSS
Definition proto_v2.h:125
@ PV2_MODE_POS_HOLD
Definition proto_v2.h:102
@ PV2_MODE_ACRO
Definition proto_v2.h:102
@ PV2_MODE_ANGLE
Definition proto_v2.h:102
@ PV2_MODE_ALT_HOLD
Definition proto_v2.h:102
@ PV2_BATT_OK
Definition proto_v2.h:136
@ PV2_BATT_EMERGENCY
Definition proto_v2.h:136
@ PV2_BATT_CRITICAL
Definition proto_v2.h:136
#define PV2_OP_TAKEOFF
Definition proto_v2.h:53
Lock-free single-producer single-consumer queue of fixed-size items.
#define SPSC_BARRIER()
Definition spsc.h:18
#define SPSC_DEFINE(name, type, n)
Definition spsc.h:29
static int spsc_push(spsc_t *q, const void *it)
Definition spsc.h:35
static void spsc_reset(spsc_t *q)
Definition spsc.h:33
static int spsc_pop(spsc_t *q, void *it)
Definition spsc.h:45
float thr_max
Definition alt_ctrl.h:17
int16_t roll
-1000..1000: thr -1000 = stick bottom
Definition app_link.h:37
int16_t yaw
Definition app_link.h:37
int16_t thr
Definition app_link.h:37
int16_t pitch
Definition app_link.h:37
battery signal (BattTask -> core): sent when the level or presence changes
Definition fc_core.h:63
uint8_t seq
Definition fc_core.h:55
uint8_t len
len = payload bytes after seq
Definition fc_core.h:55
uint8_t p[18]
Definition fc_core.h:56
uint8_t op
Definition fc_core.h:55
void(* motor_test)(uint8_t motor, float duty)
motor 0 = all off
Definition fc_core.h:75
uint8_t batt_monitor
a BattTask measures the battery
Definition fc_core.h:78
uint8_t motors_built
0: MOTOR_TEST -> UNSUPPORTED
Definition fc_core.h:72
uint8_t n_motors
4, 6 or 8
Definition fc_core.h:71
uint8_t(* usb_host)(void)
1 while a USB host is connected: ARM, TAKEOFF and MOTOR_TEST are refused (USB_CONNECTED,...
Definition fc_core.h:81
void(* on_event)(const fc_event_t *e)
every event also goes here (flight-control context), e.g.
Definition fc_core.h:77
void(* motors_arm)(uint8_t armed)
outputs (ImuTask context); may be NULL on the host
Definition fc_core.h:74
uint16_t sensors
CAPS bits detected at boot
Definition fc_core.h:70
setpoints for the attitude / rate loops (phase 3) and the motor mixer
Definition fc_core.h:115
uint8_t pos_hold
Definition fc_core.h:123
published state (AppTask builds FLIGHT / MOTORS from it)
Definition fc_core.h:85
uint16_t flight_time_s
Definition fc_core.h:89
uint8_t armed
Definition fc_core.h:86
uint8_t failsafe
Definition fc_core.h:86
uint8_t geofence
a limit is active
Definition fc_core.h:93
int32_t alt_rel_cm
above the take-off point
Definition fc_core.h:96
uint8_t fs_long
the long app-link action fired
Definition fc_core.h:88
uint8_t batt_stale
BattTask silent: battery state unknown
Definition fc_core.h:95
float stick[4]
setpoint inputs -1..1: thr, yaw, pitch, roll
Definition fc_core.h:99
uint8_t motor_out_pct[8]
Definition fc_core.h:90
uint8_t input
Definition fc_core.h:86
uint8_t landed
touched down after a flight (until disarm)
Definition fc_core.h:92
uint8_t batt_level
last level signalled by the BattTask
Definition fc_core.h:94
uint8_t autom
Definition fc_core.h:86
uint8_t mode
Definition fc_core.h:86
uint8_t in_air
Definition fc_core.h:86
uint32_t silence_ms
app link silence seen by the failsafe
Definition fc_core.h:98
uint8_t test_motor
motor under test (1..n), 0 none
Definition fc_core.h:91
int16_t vz_cms
Definition fc_core.h:97
uint8_t rc_stale
RC older than FC_RC_STALE_MS: neutral sticks in use.
Definition fc_core.h:87
vertical state from the estimator (flight-control context)
Definition fc_core.h:103
float h
Definition fc_core.h:105
uint8_t tof_valid
Definition fc_core.h:106
uint8_t valid
an altitude source is running (baro and/or ToF)
Definition fc_core.h:104
float vz
m, m/s, + up (alt_est)
Definition fc_core.h:105
uint8_t flow_usable
Definition fc_core.h:110
float cos_tilt
R33 of the attitude
Definition fc_core.h:108
uint8_t tof_ground
ToF used / ToF below its minimum range
Definition fc_core.h:106
float tof_h
m above the floor
Definition fc_core.h:107
uint8_t flow_q
0..100, 0xFF n/a
Definition fc_core.h:109