ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
app_task.c
Go to the documentation of this file.
1
10#include "app_task.h"
11#include "proto_v2.h"
12#include "ack_cache.h"
13#include "app_link.h"
14#include "tlm_ble.h"
15#include "fc_core.h"
16#include "params.h"
17#include "cfg_store.h"
18#include "checks.h"
19#include "fc_shared.h"
20#include "ext_sens.h"
21#include "batt_task.h"
22#include "log_task.h"
23#include "usb_link.h"
24#include <string.h>
25#include <math.h>
26
27#define FRAME_CAP 64u
28#define CORE_PENDING_MS 1000u /* a core command not answered after 1 s: stop waiting */
29#define REPEAT_N 3u /* ERROR / CRITICAL events: 3 copies ... */
30#define REPEAT_MS 100u /* ... 100 ms apart */
31#define CAL_LINK_ABORT_MS 3000u /* calibration aborted when the app is silent */
32#define LIST_HEADROOM 256u /* bytes left in the HI ring for ACKs while listing */
33#define EV_RATE_MS 30000u /* MAG_INTERFERENCE / HIGH_VIBRATION */
34#define VIB_WARN_MG 1500.0f
35
36/* ------------------------------------------------------------------ state */
38static cfg_image_t s_img; /* what is (or will be) in flash */
39static cal_set_t s_cal; /* calibrations in effect (RAM) */
40static uint16_t s_sensors;
41static uint8_t s_frame[FRAME_CAP];
42
43/* core command waiting for its result */
44static uint8_t s_pend, s_pend_op, s_pend_seq;
45static uint32_t s_pend_t;
46
47/* stream rates (Hz) and next send times */
48static uint8_t s_hz_flight = 10, s_hz_power = 5, s_hz_nav = 5, s_hz_motors = 5, s_hz_system = 2;
49static uint32_t s_t_power;
50static uint16_t s_fl_count;
52
53/* paced PARAM list */
54static int s_list_next = -1; /* table index, -1 = no list running */
55
56/* ERROR+ event copies */
57typedef struct { uint8_t f[FRAME_CAP]; uint8_t len, left; uint32_t next; } rep_t;
58static rep_t s_rep[6];
59
60/* rate-limited events */
61static uint32_t s_t_ev_mag, s_t_ev_vib;
63
64/* checks */
65static uint8_t s_ready, s_batt_fail;
66
67/* links: BLE (the app, can fly) and USB (ground link: telemetry, parameters,
68 * calibrations; never flight commands, ADR 0009). The first link opened has
69 * control of the configuration; the other one reads only. */
70enum { LINK_BLE = 0, LINK_USB = 1, LINK_NONE = 0xFF, DEST_ALL = 0xFE };
71static uint8_t s_owner = LINK_NONE;
72static uint8_t s_dest = DEST_ALL; /* where tx() goes: DEST_ALL or one link */
73static uint8_t s_usb_was; /* USB link open at the previous step */
74static uint8_t s_usb_now; /* USB link open at this step */
75static uint8_t s_cal_link = LINK_BLE; /* link that started the calibration */
76
77/* BLE log download: one answer frame of the LogTask waiting for room in the HI ring */
79static uint8_t s_have_logmsg;
80
81/* ------------------------------------------------------------------ helpers */
82static uint8_t armed_now(void);
83static void tx(const uint8_t *f, size_t len)
84{
85 if (!len) return;
86 if (s_dest == DEST_ALL || s_dest == LINK_BLE) (void)tlm_ble_push_hi(f, (uint16_t)len);
87 if ((s_dest == DEST_ALL || s_dest == LINK_USB) && s_usb_now) (void)usbl_put_frame(USBL_SRC_APP, f, (uint32_t)len);
88}
89
90/* frame buffer for frames longer than FRAME_CAP (log data over BLE) */
91static uint8_t s_big[4u + 255u + 2u];
92
93static uint32_t bit(uint8_t type) { return 1u << (type - 1u); }
94
95/* the ACK cache keeps the two links apart: USB entries have bit 7 of op set
96 * (every opcode is < 0x80) */
97static uint8_t ack_key(uint8_t link, uint8_t op) { return (uint8_t)(link == LINK_USB ? (op | 0x80u) : op); }
98
99static void ack_link(uint8_t link, uint8_t op, uint8_t seq, uint8_t res, uint8_t why)
100{
101 ackc_finish(ack_key(link, op), seq, res, why);
102 const uint8_t d = s_dest;
103 s_dest = link; /* an ACK goes back where the command came from */
104 tx(s_frame, pv2_ack(op, seq, res, why, s_frame, sizeof s_frame));
105 s_dest = d;
106}
107
108/* the link of the command being executed (local_cmd) */
109static uint8_t s_link = LINK_BLE;
110static void ack(uint8_t op, uint8_t seq, uint8_t res, uint8_t why) { ack_link(s_link, op, seq, res, why); }
111
112static const char *ev_text(uint16_t code)
113{
114 switch (code) {
115 case PV2_E_ARMED: return "Armed";
116 case PV2_E_DISARMED: return "Disarmed";
117 case PV2_E_TAKEOFF_STARTED: return "Takeoff";
118 case PV2_E_LANDED: return "Landed";
119 case PV2_E_MODE_CHANGED: return "Mode changed";
120 case PV2_E_RTH_STARTED: return "Return to home";
121 case PV2_E_HOME_SET: return "Home set";
122 case PV2_E_TAKEOFF_ABORTED: return "Takeoff aborted: no lift-off";
123 case PV2_E_FAILSAFE_APP_LINK: return "App link lost: failsafe";
124 case PV2_E_FAILSAFE_RADIO: return "Radio lost: failsafe";
125 case PV2_E_FAILSAFE_BATTERY: return "Battery failsafe";
126 case PV2_E_GEOFENCE_BREACH: return "Geofence reached";
127 case PV2_E_GPS_LOST: return "GPS lost";
128 case PV2_E_FLOW_LOST: return "Optical flow lost";
129 case PV2_E_BATTERY_LOW: return "Battery low";
130 case PV2_E_BATTERY_CRITICAL: return "Battery critical";
131 case PV2_E_BATTERY_EMERGENCY: return "Battery emergency: landing";
132 case PV2_E_BATTERY_MONITOR_LOST: return "Battery monitor stopped";
133 case PV2_E_MAG_INTERFERENCE: return "Magnetic interference";
134 case PV2_E_HIGH_VIBRATION: return "High vibration";
135 case PV2_E_MOTOR_HOT: return "Motor hot";
136 case PV2_E_RADIO_OVERRIDE: return "Radio took control";
137 case PV2_E_CALIB_DONE: return "Calibration saved";
138 case PV2_E_CALIB_FAILED: return "Calibration failed";
139 case PV2_E_PARAMS_SAVED: return "Settings saved";
140 case PV2_E_MOTOR_KILLED: return "Motors killed";
141 default: return "";
142 }
143}
144
145static void send_event(uint8_t level, uint16_t code, int32_t arg, uint32_t now)
146{
147 const size_t n = pv2_event(level, code, arg, ev_text(code), s_frame, sizeof s_frame);
148 tx(s_frame, n);
149 if (level < PV2_EV_ERROR || !n) return;
150 for (unsigned i = 0; i < sizeof s_rep / sizeof s_rep[0]; ++i) {
151 if (s_rep[i].left) continue;
152 memcpy(s_rep[i].f, s_frame, n);
153 s_rep[i].len = (uint8_t)n;
154 s_rep[i].left = REPEAT_N - 1u;
155 s_rep[i].next = now + REPEAT_MS;
156 return;
157 }
158}
159
160static void repeats(uint32_t now)
161{
162 for (unsigned i = 0; i < sizeof s_rep / sizeof s_rep[0]; ++i) {
163 rep_t *r = &s_rep[i];
164 if (!r->left || (int32_t)(now - r->next) < 0) continue;
165 tx(r->f, r->len);
166 r->left--;
167 r->next = now + REPEAT_MS;
168 }
169}
170
171static uint16_t sensors_now(void)
172{
173 fsh_est_t e;
174 memset(&e, 0, sizeof e);
175 fsh_est_read(&e);
176 return (uint16_t)(e.sensors | ext_sensors() | s_cfg.board_sensors);
177}
178
180{
182 memset(&b, 0, sizeof b);
184 return b;
185}
186
187/* ------------------------------------------------------------------ frames */
188static void send_caps(void)
189{
190 pv2_caps_t c;
191 memset(&c, 0, sizeof c);
193 c.fw[0] = APP_FW_MAJOR; c.fw[1] = APP_FW_MINOR; c.fw[2] = APP_FW_PATCH;
194 c.hw_rev = (uint8_t)s_cfg.hw_rev;
195 c.model = (s_sensors & PV2_S_GNSS) ? 1u : 0u;
196 c.sensors = s_sensors;
198 c.frame = (s_cfg.n_motors == 8u) ? 2u : (s_cfg.n_motors == 6u ? 1u : 0u);
199 c.cells = batt_now().cells; /* 0 = not known yet */
200 memcpy(c.name, s_img.name, sizeof c.name);
201 {
202 uint32_t h = 2166136261u; /* FNV-1a of the 96-bit UID */
203 for (int i = 0; i < 3; ++i)
204 for (int b = 0; b < 4; ++b) { h ^= (s_cfg.uid[i] >> (8 * b)) & 0xFFu; h *= 16777619u; }
205 static const char hex[] = "0123456789ABCDEF";
206 memcpy(c.serial, "ACW-", 4);
207 for (int i = 0; i < 8; ++i) c.serial[4 + i] = hex[(h >> (28 - 4 * i)) & 0xFu];
208 }
209 tx(s_frame, pv2_frame(PV2_FT_CAPS, &c, sizeof c, s_frame, sizeof s_frame));
210}
211
212static float yaw_heading_deg(const float q[4])
213{
214 const float w = q[0], x = q[1], y = q[2], z = q[3];
215 const float yaw = atan2f(2.0f * (w*z + x*y), 1.0f - 2.0f * (y*y + z*z)); /* NWU, CCW + */
216 float h = -yaw * 57.29578f; /* compass, CW + */
217 while (h < 0.0f) h += 360.0f;
218 while (h >= 360.0f) h -= 360.0f;
219 return h;
220}
221
222static void send_flight(void)
223{
224 fc_state_t f;
225 memset(&f, 0, sizeof f);
226 fc_snapshot(&f);
227 fsh_est_t e;
228 memset(&e, 0, sizeof e);
229 fsh_est_read(&e);
230 pv2_flight_t m;
231 memset(&m, 0, sizeof m);
232 m.arm = f.armed; m.mode = f.mode; m.autom = f.autom; m.failsafe = f.failsafe;
233 uint16_t fl = 0;
234 if (f.in_air) fl |= PV2_FF_IN_AIR;
235 if (f.landed) fl |= PV2_FF_LANDED;
237 if (PARAM(P_BEGINNER) > 0.5f) fl |= PV2_FF_BEGINNER;
238 if (!f.armed && !s_ready) fl |= PV2_FF_ARM_BLOCKED;
239 m.flags = fl;
240 m.input = f.input;
243 m.vz_cms = f.vz_cms;
244 if (e.flow_usable) {
245 const float gs = sqrtf(e.vel_body[0]*e.vel_body[0] + e.vel_body[1]*e.vel_body[1]) * 100.0f;
246 m.ground_speed_cms = (uint16_t)(gs > 65535.0f ? 65535.0f : gs);
247 }
248 m.heading_cdeg = (uint16_t)(yaw_heading_deg(e.q) * 100.0f + 0.5f) % 36000u;
249 m.home_dist_m = 0xFFFFu;
250 m.home_bearing_cdeg = 0u;
251 m.rth_eta_s = 0xFFFFu;
252 tx(s_frame, pv2_frame(PV2_FT_FLIGHT, &m, sizeof m, s_frame, sizeof s_frame));
253}
254
255static int16_t sat16(float v) { return (int16_t)(v > 32767.0f ? 32767 : (v < -32768.0f ? -32768 : (int)v)); }
256
257static void send_nav(void)
258{
259 fsh_est_t e;
260 memset(&e, 0, sizeof e);
261 fsh_est_read(&e);
262 pv2_nav_t n;
263 memset(&n, 0, sizeof n);
264 n.fix = 0xFFu; /* no GNSS hardware */
265 n.hdop_x100 = 0xFFFFu;
266 if (e.baro_pa) {
267 n.alt_msl_cm = (int32_t)(44330.0f * (1.0f - powf((float)e.baro_pa / 101325.0f, 0.190295f)) * 100.0f);
268 n.baro_pa = e.baro_pa;
269 }
270 {
271 const float psi = -yaw_heading_deg(e.q) * 0.01745329f; /* NWU yaw */
272 float vn = 0.0f, ve = 0.0f;
273 if (e.flow_usable) {
274 const float vx = e.vel_body[0], vy = e.vel_body[1]; /* body x fwd, y left */
275 vn = cosf(psi) * vx - sinf(psi) * vy;
276 ve = -(sinf(psi) * vx + cosf(psi) * vy); /* E = -W */
277 }
278 n.vel_ned_cms[0] = sat16(vn * 100.0f);
279 n.vel_ned_cms[1] = sat16(ve * 100.0f);
280 n.vel_ned_cms[2] = e.alt_valid ? sat16(-e.vz * 100.0f) : 0;
281 }
282 n.tof_mm = e.tof_mm;
283 n.flow_q = e.flow_q;
284 n.wind_cms = 0xFFFFu;
285 tx(s_frame, pv2_frame(PV2_FT_NAV, &n, sizeof n, s_frame, sizeof s_frame));
286}
287
288static void send_motors(void)
289{
290 fc_state_t f;
291 memset(&f, 0, sizeof f);
292 fc_snapshot(&f);
295 for (uint8_t i = 0; i < n; ++i) { m[i].rpm = 0xFFFFu; m[i].temp_c = -128; m[i].out_pct = f.motor_out_pct[i]; }
296 tx(s_frame, pv2_motors(m, n, s_frame, sizeof s_frame));
297}
298
299static void send_system(void)
300{
301 fsh_est_t e;
302 memset(&e, 0, sizeof e);
303 fsh_est_read(&e);
305 memset(&s, 0, sizeof s);
306 s.lq = 0xFFu;
307 s.cpu_pct = (uint8_t)(e.cpu_pct > 100.0f ? 100.0f : e.cpu_pct);
308 s.loop_hz = (uint16_t)(e.loop_hz + 0.5f);
309 s.jitter_us = (uint16_t)(e.jitter_us > 65535.0f ? 65535.0f : e.jitter_us);
310 s.imu_temp_cc = sat16(e.imu_temp_c * 100.0f);
311 s.vibration_mg = (uint16_t)(e.vib_mg > 65535.0f ? 65535.0f : e.vib_mg);
312 s.acc_clip = e.acc_clip;
313 s.att_unc_cdeg = (uint16_t)(e.att_unc_deg * 100.0f > 65535.0f ? 65535.0f : e.att_unc_deg * 100.0f);
314 s.est_flags = (uint8_t)((e.running ? 1u : 0u) | (e.acc_gated ? 2u : 0u) | (e.mag_gated ? 4u : 0u) |
315 (e.mag_interf_pct > 30.0f ? 8u : 0u));
316 s.mag_interf_pct = (uint8_t)(e.mag_interf_pct > 100.0f ? 100.0f : e.mag_interf_pct);
317 s.log_pct = s_cfg.log_pct ? s_cfg.log_pct() : 0u;
318 if (s.log_pct == 0xFFu) s.log_pct = 0u;
319 tx(s_frame, pv2_frame(PV2_FT_SYSTEM, &s, sizeof s, s_frame, sizeof s_frame));
320}
321
322static uint16_t u16c(float v) { return (uint16_t)(v > 65535.0f ? 65535.0f : (v < 0.0f ? 0.0f : v + 0.5f)); }
323
324static void send_power(void)
325{
326 const batt_state_t b = batt_now();
327 pv2_power_t p;
328 memset(&p, 0, sizeof p);
329 p.vbat_mv = u16c(b.vbat * 1000.0f);
330 p.current_ca = (s_sensors & PV2_S_CURRENT) ? sat16(b.current * 100.0f) : 0;
331 p.used_mah = u16c(b.used_mah);
332 p.cells = b.cells;
333 p.cell_mv = u16c(b.cell_v * 1000.0f);
334 p.pct = b.pct;
335 p.remain_s = (b.remain_s < 0.0f) ? 0xFFFFu : u16c(b.remain_s > 65534.0f ? 65534.0f : b.remain_s);
336 p.warn = b.level;
337 tx(s_frame, pv2_frame(PV2_FT_POWER, &p, sizeof p, s_frame, sizeof s_frame));
338}
339
340static void build_checks(pv2_check_t *it, uint8_t *n)
341{
342 fsh_est_t e;
343 memset(&e, 0, sizeof e);
344 fsh_est_read(&e);
345 fc_state_t f;
346 memset(&f, 0, sizeof f);
347 fc_snapshot(&f);
348 checks_in_t in;
349 memset(&in, 0, sizeof in);
350 in.sensors = s_sensors;
351 in.imu_ok = e.imu_ok;
352 in.est_running = e.running;
356 {
357 appl_rc_t rc; uint32_t t;
358 if (appl_rc_read(&rc, &t) && (f.silence_ms < FC_ARM_RC_FRESH_MS) && !f.rc_stale) {
359 in.rc_fresh = 1u; in.rc_thr = rc.thr;
360 }
361 }
362 in.hold_mode = (uint8_t)(f.mode == PV2_MODE_ALT_HOLD || f.mode == PV2_MODE_POS_HOLD);
365 in.alt_valid = e.alt_valid;
366 in.flow_q = e.flow_q;
367 in.flow_ok = e.flow_ok;
368 in.in_air = f.in_air;
369 if (s_cfg.batt_monitor) {
370 const batt_state_t b = batt_now();
371 in.batt_monitor = 1u;
372 in.batt_stale = f.batt_stale;
373 in.batt_present = b.present;
374 in.batt_level = f.batt_level;
375 in.batt_cell_v = b.cell_v;
378 }
379 in.usb_host = usbl_host();
380 *n = checks_build(&in, it, &s_ready, &s_batt_fail);
381}
382
383static void send_checks(void)
384{
386 uint8_t n = 0;
387 build_checks(it, &n);
388 tx(s_frame, pv2_checks(s_ready, it, n, s_frame, sizeof s_frame));
389}
390
391static void send_init(void)
392{
393 if (!s_cfg.get_init) return;
394 static uint8_t f[160];
395 const size_t n = s_cfg.get_init(f, sizeof f);
396 tx(f, n);
397}
398
399static void send_param(int idx)
400{
401 const param_def_t *d = &g_param_def[idx];
402 const int pos = params_visible_pos(idx, s_sensors);
403 const uint16_t count = params_visible_count(s_sensors);
404 tx(s_frame, pv2_param(d->id, param_get(idx), params_dirty(idx) ? 1u : 0u,
405 (uint16_t)(pos < 0 ? 0 : pos), count, s_frame, sizeof s_frame));
406}
407
408static void list_start(void) { s_list_next = 0; }
409
419
420static void send_calib(void)
421{
422 const size_t n = cal_frame(s_frame, sizeof s_frame);
423 tx(s_frame, n);
424}
425
426/* ------------------------------------------------------------------ flash */
427static int save_image(void)
428{
429 float v[PARAM_COUNT];
432 return cfg_save(&s_img);
433}
434
435/* the flash image as it is, with the parameters last saved (no unsaved edits) */
436static int save_image_keep_params(void) { return cfg_save(&s_img); }
437
438/* calibration apply hook (calib_mgr -> ImuTask, BATTERY -> BattTask) */
439static void cal_apply(uint8_t type, const cal_set_t *set)
440{
441 if (type == PV2_CAL_BATTERY) (void)batt_task_post_scales(set->battery.scale_v, set->battery.scale_i);
442 else (void)fsh_apply_post(type, set);
443}
444
445/* BATTERY calibration readings */
446static void cal_batt(float *vbat, float *used, float *scale_i)
447{
448 *vbat = batt_now().vbat;
450 *scale_i = s_img.batt_used_scale_i;
451}
452
453/* after a flight: keep its charge for the BATTERY calibration (§10.6) */
454static void battery_flight(void)
455{
456 if (!s_cfg.batt_monitor) return;
457 batt_flight_t fl;
458 memset(&fl, 0, sizeof fl);
460 if (fl.count == s_fl_count) return;
461 s_fl_count = fl.count;
462 if (fl.used_mah < 20.0f || armed_now()) return; /* not a flight worth keeping */
465 (void)save_image_keep_params(); /* disarmed: flash write allowed */
466}
467
468/* ------------------------------------------------------------------ commands */
469static uint8_t armed_now(void)
470{
471 fc_state_t f;
472 memset(&f, 0, sizeof f);
473 fc_snapshot(&f);
474 return f.armed;
475}
476
477static void local_cmd(uint8_t op, uint8_t seq, const uint8_t *p, uint8_t plen, uint32_t now)
478{
479 uint8_t why = PV2_R_NONE, res;
480 switch (op) {
481 case PV2_OP_PARAM_GET: {
482 if (plen < 2u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
483 const int idx = params_index(pv2_rd_u16(p));
484 if (idx < 0 || !params_visible(idx, s_sensors)) { ack(op, seq, PV2_ACK_INVALID, PV2_R_UNKNOWN_PARAM); return; }
486 send_param(idx);
487 return;
488 }
489 case PV2_OP_PARAM_SET: {
490 if (plen < 6u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
491 const uint16_t id = pv2_rd_u16(p);
492 const int idx = params_index(id);
493 if (idx >= 0 && !params_visible(idx, s_sensors)) { ack(op, seq, PV2_ACK_INVALID, PV2_R_UNKNOWN_PARAM); return; }
494 res = params_set(id, pv2_rd_f32(&p[2]), &why);
495 ack(op, seq, res, why);
496 if (res == PV2_ACK_OK) send_param(idx);
497 return;
498 }
500 if (armed_now()) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_ARMED); return; }
501 if (save_image() != 0) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_NONE); return; } /* flash error */
505 list_start();
506 return;
508 if (armed_now()) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_ARMED); return; }
511 list_start();
512 return;
515 list_start();
516 return;
517
518 case PV2_OP_CAL_START:
519 if (plen < 1u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
520 res = cal_start(p[0], plen >= 2u ? p[1] : 0u, armed_now(), now, &why);
521 ack(op, seq, res, why);
522 if (res == PV2_ACK_OK) { s_cal_link = s_link; send_calib(); }
523 return;
525 ack(op, seq, cal_capture(now, &why), why);
526 return;
527 case PV2_OP_CAL_ABORT:
528 ack(op, seq, cal_abort(&why), why);
529 send_calib();
530 return;
531 case PV2_OP_CAL_SAVE: {
532 res = cal_save_check(&why);
533 if (res != PV2_ACK_OK) { ack(op, seq, res, why); return; }
534 const uint8_t t = cal_type();
535 /* the set saved until now becomes the backup of this type */
537 s_img.bak_mask |= bit(t);
539 s_img.cal.done_mask |= bit(t);
540 s_cal.done_mask |= bit(t);
541 if (save_image() != 0) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_NONE); return; }
542 cal_saved();
544 send_calib();
546 return;
547 }
548 case PV2_OP_CAL_RESTORE: {
549 if (plen < 1u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
550 const uint8_t t = p[0];
551 if (t < PV2_CAL_ACCEL || t > PV2_CAL_STICKS) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
552 if (armed_now()) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_ARMED); return; }
553 if (cal_running()) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_CALIBRATING); return; }
554 if (!(s_img.bak_mask & bit(t))) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_NONE); return; }
555 cal_set_t cur = s_img.cal;
556 cal_copy_type(&s_img.cal, &s_img.bak, t); /* swap: a second RESTORE undoes it */
557 cal_copy_type(&s_img.bak, &cur, t);
558 if (save_image() != 0) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_NONE); return; }
560 cal_apply(t, &s_cal);
562 return;
563 }
565 if (plen < 6u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
566 ack(op, seq, cal_set_ref(p[0], p[1], pv2_rd_f32(&p[2]), &why), why);
567 return;
568
571 ack(op, seq, PV2_ACK_UNSUPPORTED, PV2_R_NONE); /* no DShot / ESC link yet */
572 return;
573
574 case PV2_OP_STREAM_RATES: {
575 if (plen < 5u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
576#define RATE(dst, v, lo, hi) do { if ((v) != 0u) dst = (uint8_t)((v) < (lo) ? (lo) : ((v) > (hi) ? (hi) : (v))); } while (0)
577 RATE(s_hz_flight, p[0], 1u, 20u);
578 RATE(s_hz_power, p[1], 1u, 10u);
579 RATE(s_hz_nav, p[2], 1u, 10u);
580 RATE(s_hz_motors, p[3], 1u, 10u);
581 RATE(s_hz_system, p[4], 1u, 5u);
582#undef RATE
584 return;
585 }
586 case PV2_OP_REQUEST:
587 if (plen < 1u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
588 switch (p[0]) {
589 case PV2_FT_CAPS: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_caps(); return;
590 case PV2_FT_CHECKS: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_checks(); return;
591 case PV2_FT_INIT: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_init(); return;
592 case PV2_FT_FLIGHT: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_flight(); return;
593 case PV2_FT_NAV: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_nav(); return;
594 case PV2_FT_MOTORS: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_motors(); return;
595 case PV2_FT_SYSTEM: ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_system(); return;
596 case PV2_FT_POWER:
597 if (!s_cfg.batt_monitor) { ack(op, seq, PV2_ACK_UNSUPPORTED, PV2_R_NONE); return; }
598 ack(op, seq, PV2_ACK_OK, PV2_R_NONE); send_power(); return;
599 default: ack(op, seq, PV2_ACK_UNSUPPORTED, PV2_R_NONE); return;
600 }
601 case PV2_OP_SET_NAME: {
602 if (armed_now()) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_ARMED); return; }
603 if (plen < 1u) { ack(op, seq, PV2_ACK_INVALID, PV2_R_NONE); return; }
604 char old[16];
605 memcpy(old, s_img.name, sizeof old);
606 memset(s_img.name, 0, sizeof s_img.name);
607 memcpy(s_img.name, p, plen > 16u ? 16u : plen);
608 if (save_image() != 0) { memcpy(s_img.name, old, sizeof old); ack(op, seq, PV2_ACK_REJECTED, PV2_R_NONE); return; }
610 send_caps();
611 return;
612 }
613 default:
615 return;
616 }
617}
618
619/* commands the USB link may carry (ground link, ADR 0009) */
620static int usb_allowed(uint8_t op)
621{
622 switch (op) {
628 return 1;
629 default:
630 return 0;
631 }
632}
633
634/* commands that change the configuration: only from the link in control */
635static int config_op(uint8_t op)
636{
637 switch (op) {
643 return 1;
644 default: /* CAL_ABORT from any link: stopping is always allowed */
645 return 0;
646 }
647}
648
649static uint8_t other_link(uint8_t link) { return s_owner != LINK_NONE && s_owner != link; }
650
651static void usb_commands(uint32_t now)
652{
653 appl_cmd_t c;
654 while (log_task_usb_app_take(&c)) {
655 if (c.len < 2u) continue;
656 const uint8_t op = c.b[0], seq = c.b[1];
657 const uint8_t plen = (uint8_t)(c.len - 2u);
658 uint8_t res, why;
659 const uint8_t st = ackc_begin(ack_key(LINK_USB, op), seq, &res, &why);
660 if (st == ACKC_DONE) { ack_link(LINK_USB, op, seq, res, why); continue; }
661 if (st == ACKC_PENDING) continue;
662 if (!usb_allowed(op)) {
664 op < 0x10u ? PV2_R_NONE : PV2_R_NOT_OVER_USB);
665 continue;
666 }
669 local_cmd(op, seq, &c.b[2], plen, now);
671 }
672}
673
674static void commands(uint32_t now)
675{
676 /* results of the flight core first: they free the pipeline */
677 fc_res_t r;
678 while (fc_take_result(&r)) {
679 ack(r.op, r.seq, r.result, r.reason);
680 if (s_pend && r.op == s_pend_op && r.seq == s_pend_seq) s_pend = 0u;
681 }
682 if (s_pend && (now - s_pend_t) > CORE_PENDING_MS) s_pend = 0u; /* cannot happen: the IWDG would reset first */
683
684 appl_cmd_t c;
685 while (!s_pend && appl_pop(&c)) {
686 if (c.len < 2u) continue;
687 const uint8_t op = c.b[0], seq = c.b[1];
688 const uint8_t plen = (uint8_t)(c.len - 2u);
689 uint8_t res, why;
690 const uint8_t st = ackc_begin(op, seq, &res, &why);
691 if (st == ACKC_DONE) { ack_link(LINK_BLE, op, seq, res, why); continue; }
692 if (st == ACKC_PENDING) continue; /* its ACK is on the way */
693
694 if (op == PV2_OP_LOG_LIST || op == PV2_OP_LOG_READ || op == PV2_OP_LOG_ERASE || op == PV2_OP_SET_TIME) {
695 /* the LogTask owns the log and the wall clock: it answers (ACK after the data) through log_ble_out() */
696 log_cmd_t lc;
697 memset(&lc, 0, sizeof lc);
698 lc.op = op; lc.seq = seq;
699 lc.len = (uint8_t)(plen > sizeof lc.p ? sizeof lc.p : plen);
700 memcpy(lc.p, &c.b[2], lc.len);
701 if (log_task_ble_cmd(&lc) != 0) ack(op, seq, PV2_ACK_BUSY, PV2_R_NONE);
702 continue;
703 }
704 if (config_op(op) && other_link(LINK_BLE)) { ack(op, seq, PV2_ACK_REJECTED, PV2_R_OTHER_LINK); continue; }
705 if (fc_is_core_op(op)) {
706 fc_cmd_t fc;
707 memset(&fc, 0, sizeof fc);
708 fc.op = op; fc.seq = seq;
709 fc.len = (uint8_t)(plen > sizeof fc.p ? sizeof fc.p : plen);
710 memcpy(fc.p, &c.b[2], fc.len);
711 if (fc_post(&fc) != 0) { ack(op, seq, PV2_ACK_BUSY, PV2_R_NONE); continue; }
712 s_pend = 1u; s_pend_op = op; s_pend_seq = seq; s_pend_t = now;
713 } else {
714 /* any other command stops a running motor test (§10.7) */
715 const fc_cmd_t stop = { FC_OP_MOTOR_STOP, 0u, 0u, { 0 } };
716 (void)fc_post(&stop);
717 local_cmd(op, seq, &c.b[2], plen, now);
718 }
719 }
720}
721
722/* answers of the LogTask to BLE log commands -> HI ring (dropped when no app
723 * is subscribed: the app asks again) */
724static void log_ble_out(void)
725{
726 for (;;) {
727 if (!s_have_logmsg) {
728 if (!log_task_ble_take(&s_logmsg)) return;
729 s_have_logmsg = 1u;
730 }
731 const log_msg_t *m = &s_logmsg;
732 if (m->type == PV2_FT_ACK && m->len >= 4u) {
733 ack(m->p[0], m->p[1], m->p[2], m->p[3]); /* through the ACK cache */
734 } else {
735 /* the HI ring takes log frames up to 60 % full (tlm_ble.c), so ACKs and
736 * events always find room; a frame that does not fit waits here */
737 const size_t n = pv2_frame(m->type, m->p, m->len, s_big, sizeof s_big);
738 if (tlm_ble_state() == TLM_BLE_STREAMING && n) {
739 if (tlm_ble_hi_free() < (TLM_BLE_RING_SIZE * 40u) / 100u + n) return; /* no room yet */
740 (void)tlm_ble_push_hi(s_big, (uint16_t)n);
741 }
742 }
743 s_have_logmsg = 0u;
744 }
745}
746
747/* ------------------------------------------------------------------ periodic */
748static int due(uint32_t now, uint32_t *t, uint32_t period_ms)
749{
750 if ((int32_t)(now - *t) < 0) return 0;
751 *t = now + period_ms;
752 return 1;
753}
754
755static void events(uint32_t now)
756{
757 fc_event_t e;
758 while (fc_take_event(&e)) send_event(e.level, e.code, e.arg, now);
759
760 const uint8_t failed = cal_take_failed();
761 if (failed) send_event(PV2_EV_ERROR, PV2_E_CALIB_FAILED, failed, now);
762
763 fsh_est_t s;
764 memset(&s, 0, sizeof s);
765 fsh_est_read(&s);
766 if (s.running && (s_sensors & PV2_S_MAG_FC) && s.mag_interf_pct > 30.0f &&
767 (!s_have_ev_mag || (now - s_t_ev_mag) > EV_RATE_MS)) {
768 send_event(PV2_EV_WARN, PV2_E_MAG_INTERFERENCE, (int32_t)(s.mag_interf_pct + 0.5f), now);
769 s_t_ev_mag = now; s_have_ev_mag = 1u;
770 }
771 if (armed_now() && s.vib_mg > VIB_WARN_MG && (!s_have_ev_vib || (now - s_t_ev_vib) > EV_RATE_MS)) {
772 send_event(PV2_EV_WARN, PV2_E_HIGH_VIBRATION, (int32_t)s.vib_mg, now);
773 s_t_ev_vib = now; s_have_ev_vib = 1u;
774 }
775}
776
777static void calibration(uint32_t now)
778{
779 cal_sample_t smp;
780 while (fsh_cal_pop(&smp)) cal_feed(&smp, now);
781 cal_tick(now);
782 /* lost app link: abort, the previous values are kept (§10) */
785 uint8_t why;
786 (void)cal_abort(&why);
787 }
789 if (cal_take_changed()) { send_calib(); s_t_calib = now + 100u; }
790 else if (cal_active() && due(now, &s_t_calib, 100u)) send_calib();
791}
792
793static void streams(uint32_t now, uint8_t armed)
794{
795 if (tlm_ble_state() != TLM_BLE_STREAMING && !s_usb_now) return;
796 if (due(now, &s_t_flight, 1000u / s_hz_flight)) send_flight();
797 if (due(now, &s_t_nav, 1000u / s_hz_nav)) send_nav();
798 if (due(now, &s_t_motors, 1000u / s_hz_motors)) send_motors();
799 if (due(now, &s_t_system, 1000u / s_hz_system)) send_system();
800 if (due(now, &s_t_checks, armed ? 2000u : 500u)) send_checks();
801 if (due(now, &s_t_caps, 5000u)) send_caps();
802 if (s_cfg.batt_monitor && due(now, &s_t_power, 1000u / s_hz_power)) send_power();
803}
804
805/* ------------------------------------------------------------------ entry points */
806const cal_set_t *app_task_boot(const float mag_hi_default[3])
807{
808 memset(&s_img, 0, sizeof s_img);
809 cal_set_defaults(&s_img.cal, mag_hi_default);
810 cal_set_defaults(&s_img.bak, mag_hi_default);
812 memcpy(s_img.name, "ACwing #1", 9);
813 cfg_image_t img;
815 if (cfg_load(&img) == 0) {
816 s_img = img;
817 float v[PARAM_COUNT];
818 cfg_params_get(&img, v);
820 s_img.name[15] = '\0';
821 }
823 s_cal = s_img.cal;
824 fsh_reset();
825 /* the ImuTask takes these before starting the estimator */
830 return &s_cal;
831}
832
833void app_init(const app_task_cfg_t *cfg)
834{
835 s_cfg = *cfg;
836 if (s_cfg.n_motors == 0u) s_cfg.n_motors = 4u;
839 ackc_reset();
841 s_cfg.batt_monitor ? cal_batt : NULL };
842 cal_init(&cc, &s_cal);
843 s_pend = 0u; s_list_next = -1;
844 memset(s_rep, 0, sizeof s_rep);
845 s_fl_count = 0u;
846 s_have_logmsg = 0u;
849}
850
851void app_step(uint32_t now)
852{
853 /* sensors found later (expansion board plugged in while disarmed) */
854 const uint16_t sn = sensors_now();
855 if (sn != s_sensors && !armed_now()) {
856 s_sensors = sn;
857 fc_set_sensors(sn);
858 cal_set_sensors(sn);
860 }
861
862 /* links: who is open, who has control of the configuration */
863 s_usb_now = usbl_open(now);
864 {
865 const uint8_t ble = (uint8_t)(tlm_ble_state() == TLM_BLE_STREAMING);
866 if (s_owner == LINK_BLE && !ble) s_owner = LINK_NONE;
869 }
870 /* a program opened the USB port: CAPS, INIT, CHECKS to it, as to a BLE subscriber */
871 if (s_usb_now && !s_usb_was) {
877 s_t_checks = now + 500u; s_t_caps = now + 5000u;
878 }
879 }
881
882 /* new subscriber: CAPS, INIT, CHECKS first, then the periodic streams (§2) */
884 ackc_reset();
885 s_list_next = -1;
887 send_caps();
888 send_init();
889 send_checks();
893 s_t_checks = now + 500u; s_t_caps = now + 5000u;
894 }
895
896 commands(now);
897 usb_commands(now);
898 events(now);
899 repeats(now);
900 calibration(now);
901
902 /* ground information for ARM (10 Hz) */
903 if (due(now, &s_t_ground, 100u)) {
905 uint8_t n;
906 build_checks(it, &n);
908 }
909
911 log_ble_out();
912 streams(now, armed_now());
913 list_pace();
914}
915
916/* ------------------------------------------------------------------ RTOS shell */
917#ifndef HOST_TEST
918#include "cmsis_os2.h"
919
920static void AppTask(void *arg)
921{
922 (void)arg;
923 for (;;) {
924 app_step(osKernelGetTickCount());
925 osDelay(5);
926 }
927}
928
930{
931 app_init(cfg);
932 static const osThreadAttr_t a = { .name = "app", .priority = osPriorityBelowNormal,
933 .stack_size = 3072 };
934 (void)osThreadNew(AppTask, NULL, &a);
935}
936#endif
void ackc_finish(uint8_t op, uint8_t seq, uint8_t result, uint8_t reason)
Completes the entry (or creates it if it was evicted meanwhile).
Definition ack_cache.c:44
uint8_t ackc_begin(uint8_t op, uint8_t seq, uint8_t *result, uint8_t *reason)
Looks up (op, seq).
Definition ack_cache.c:31
void ackc_reset(void)
Definition ack_cache.c:12
Last (op, seq) -> ACK pairs (HANDOVER_FW.md §5, "Sequence and ACK rules").
@ ACKC_PENDING
Definition ack_cache.h:19
@ ACKC_DONE
Definition ack_cache.h:19
static void send_checks(void)
Definition app_task.c:383
@ LINK_USB
Definition app_task.c:70
@ LINK_BLE
Definition app_task.c:70
@ LINK_NONE
Definition app_task.c:70
@ DEST_ALL
Definition app_task.c:70
static uint16_t s_sensors
Definition app_task.c:40
static uint32_t s_t_power
Definition app_task.c:49
static void send_nav(void)
Definition app_task.c:257
static uint32_t s_t_ev_mag
Definition app_task.c:61
static void send_caps(void)
Definition app_task.c:188
static cal_set_t s_cal
Definition app_task.c:39
static uint8_t s_pend_op
Definition app_task.c:44
static uint8_t s_frame[FRAME_CAP]
Definition app_task.c:41
static uint8_t s_dest
Definition app_task.c:72
static uint32_t s_pend_t
Definition app_task.c:45
static int s_list_next
Definition app_task.c:54
static uint8_t s_hz_power
Definition app_task.c:48
static void cal_apply(uint8_t type, const cal_set_t *set)
Definition app_task.c:439
static int16_t sat16(float v)
Definition app_task.c:255
static void list_start(void)
Definition app_task.c:408
static uint32_t s_t_ev_vib
Definition app_task.c:61
static uint8_t s_owner
Definition app_task.c:71
static uint8_t s_hz_motors
Definition app_task.c:48
#define FRAME_CAP
Definition app_task.c:27
static int save_image(void)
Definition app_task.c:427
static int save_image_keep_params(void)
Definition app_task.c:436
static uint8_t armed_now(void)
Definition app_task.c:469
static uint32_t s_t_flight
Definition app_task.c:51
static rep_t s_rep[6]
Definition app_task.c:58
#define REPEAT_MS
Definition app_task.c:30
static batt_state_t batt_now(void)
Definition app_task.c:179
static void send_motors(void)
Definition app_task.c:288
static void AppTask(void *arg)
Definition app_task.c:920
static uint8_t s_cal_link
Definition app_task.c:75
static void send_system(void)
Definition app_task.c:299
static void usb_commands(uint32_t now)
Definition app_task.c:651
static uint8_t s_batt_fail
Definition app_task.c:65
static uint32_t s_t_checks
Definition app_task.c:51
static uint8_t s_pend_seq
Definition app_task.c:44
static uint8_t s_hz_system
Definition app_task.c:48
static void tx(const uint8_t *f, size_t len)
Definition app_task.c:83
static void events(uint32_t now)
Definition app_task.c:755
static void cal_batt(float *vbat, float *used, float *scale_i)
Definition app_task.c:446
static uint8_t s_ready
Definition app_task.c:65
static uint16_t sensors_now(void)
Definition app_task.c:171
static void repeats(uint32_t now)
Definition app_task.c:160
#define EV_RATE_MS
Definition app_task.c:33
#define CAL_LINK_ABORT_MS
Definition app_task.c:31
static void commands(uint32_t now)
Definition app_task.c:674
static void ack_link(uint8_t link, uint8_t op, uint8_t seq, uint8_t res, uint8_t why)
Definition app_task.c:99
static float yaw_heading_deg(const float q[4])
Definition app_task.c:212
static void send_init(void)
Definition app_task.c:391
#define LIST_HEADROOM
Definition app_task.c:32
static uint32_t s_t_motors
Definition app_task.c:51
static void ack(uint8_t op, uint8_t seq, uint8_t res, uint8_t why)
Definition app_task.c:110
static uint32_t s_t_ground
Definition app_task.c:51
void app_init(const app_task_cfg_t *cfg)
protocol logic, also used by the host test
Definition app_task.c:833
static void send_param(int idx)
Definition app_task.c:399
static void send_calib(void)
Definition app_task.c:420
static uint8_t s_usb_now
Definition app_task.c:74
static uint8_t ack_key(uint8_t link, uint8_t op)
Definition app_task.c:97
static uint16_t u16c(float v)
Definition app_task.c:322
static const char * ev_text(uint16_t code)
Definition app_task.c:112
static void streams(uint32_t now, uint8_t armed)
Definition app_task.c:793
static void local_cmd(uint8_t op, uint8_t seq, const uint8_t *p, uint8_t plen, uint32_t now)
Definition app_task.c:477
void app_task_start(const app_task_cfg_t *cfg)
app_init + the RTOS task
Definition app_task.c:929
static uint8_t other_link(uint8_t link)
Definition app_task.c:649
const cal_set_t * app_task_boot(const float mag_hi_default[3])
Before the scheduler starts: loads the flash image (parameters, name, calibrations) and posts the sav...
Definition app_task.c:806
static int due(uint32_t now, uint32_t *t, uint32_t period_ms)
Definition app_task.c:748
static uint8_t s_big[4u+255u+2u]
Definition app_task.c:91
static uint16_t s_fl_count
Definition app_task.c:50
static int usb_allowed(uint8_t op)
Definition app_task.c:620
static void send_event(uint8_t level, uint16_t code, int32_t arg, uint32_t now)
Definition app_task.c:145
static void list_pace(void)
Definition app_task.c:410
void app_step(uint32_t now)
Definition app_task.c:851
static uint32_t s_t_nav
Definition app_task.c:51
static void battery_flight(void)
Definition app_task.c:454
static uint8_t s_have_ev_vib
Definition app_task.c:62
static uint32_t s_t_caps
Definition app_task.c:51
#define REPEAT_N
Definition app_task.c:29
static void log_ble_out(void)
Definition app_task.c:724
#define CORE_PENDING_MS
Definition app_task.c:28
static uint32_t s_t_system
Definition app_task.c:51
static int config_op(uint8_t op)
Definition app_task.c:635
static uint8_t s_link
Definition app_task.c:109
static uint8_t s_hz_flight
Definition app_task.c:48
static void send_flight(void)
Definition app_task.c:222
static uint8_t s_have_ev_mag
Definition app_task.c:62
#define VIB_WARN_MG
Definition app_task.c:34
#define RATE(dst, v, lo, hi)
static uint32_t bit(uint8_t type)
Definition app_task.c:93
static void build_checks(pv2_check_t *it, uint8_t *n)
Definition app_task.c:340
static uint8_t s_usb_was
Definition app_task.c:73
static uint8_t s_have_logmsg
Definition app_task.c:79
static cfg_image_t s_img
Definition app_task.c:38
static app_task_cfg_t s_cfg
Definition app_task.c:37
static void calibration(uint32_t now)
Definition app_task.c:777
static uint32_t s_t_calib
Definition app_task.c:51
static void send_power(void)
Definition app_task.c:324
static uint8_t s_pend
Definition app_task.c:44
static uint8_t s_hz_nav
Definition app_task.c:48
static log_msg_t s_logmsg
Definition app_task.c:78
AppTask: ACwing Pilot protocol v2 (HANDOVER_FW.md) on the drone side.
#define APP_FW_MINOR
Definition app_task.h:25
#define APP_FW_PATCH
Definition app_task.h:26
#define APP_FW_MAJOR
Definition app_task.h:24
void batt_task_last_flight(batt_flight_t *out)
last flight (any task, seqlock)
Definition batt_task.c:46
int batt_task_post_scales(float sv, float si)
AppTask -> BattTask: new calibration scales; 0 = queued.
Definition batt_task.c:40
BattTask: owns the battery ADC and the battery monitor (battery.c).
static batt_state_t s
Definition battery.c:21
void batt_read(batt_state_t *out)
seqlock snapshot for other tasks; keeps *out on a race
Definition battery.c:84
uint8_t cal_take_changed(void)
1 if the state/step changed since the last call (send a CALIB frame now)
Definition calib_mgr.c:141
uint8_t cal_abort(uint8_t *reason)
Definition calib_mgr.c:221
uint8_t pos
Definition calib_mgr.c:34
double b[6]
Definition calib_mgr.c:53
uint32_t n
Definition calib_mgr.c:35
uint8_t cal_capture(uint32_t now, uint8_t *reason)
Definition calib_mgr.c:528
uint8_t cal_type(void)
Definition calib_mgr.c:133
void cal_init(const cal_config_t *cfg, cal_set_t *active)
active: the set in effect (RAM), owned by the caller.
Definition calib_mgr.c:89
uint8_t cal_running(void)
active and not DONE: blocks arming / other starts
Definition calib_mgr.c:132
uint8_t cal_save_check(uint8_t *reason)
CAL_SAVE validation: returns the ACK; on OK the caller writes the result (cal_result()) to flash and ...
Definition calib_mgr.c:230
void cal_saved(void)
state SAVED (one frame), then IDLE
Definition calib_mgr.c:242
float used
Definition calib_mgr.c:50
size_t cal_frame(uint8_t *out, size_t cap)
CALIB frame for the current calibration; 0 if none.
Definition calib_mgr.c:592
uint8_t cal_start(uint8_t type, uint8_t option, uint8_t armed, uint32_t now, uint8_t *reason)
Definition calib_mgr.c:150
uint8_t cal_set_ref(uint8_t type, uint8_t index, float v, uint8_t *reason)
Definition calib_mgr.c:547
uint8_t cal_take_failed(void)
1 + type once when a calibration failed (EVENT 51), 0 otherwise
Definition calib_mgr.c:143
uint8_t cal_active(void)
a calibration exists (any state but IDLE)
Definition calib_mgr.c:131
const cal_set_t * cal_result(void)
Definition calib_mgr.c:134
float vbat
Definition calib_mgr.c:50
uint8_t cal_wants_samples(void)
Definition calib_mgr.c:136
void cal_copy_type(cal_set_t *d, const cal_set_t *s, uint8_t type)
copies the part of 'src' that belongs to 'type' into 'dst'
Definition calib_mgr.c:72
void cal_set_defaults(cal_set_t *s, const float hi[3])
Definition calib_mgr.c:60
void cal_set_sensors(uint16_t sensors)
expansion board detected later
Definition calib_mgr.c:87
void cal_tick(uint32_t now)
Definition calib_mgr.c:520
void cal_feed(const cal_sample_t *s, uint32_t now)
Definition calib_mgr.c:507
int cfg_save(cfg_image_t *img)
writes img (seq and crc filled here) to the other page; 0 = ok
Definition cfg_store.c:117
void cfg_params_put(cfg_image_t *img, const float v[PARAM_COUNT])
parameters <-> slots (unknown ids ignored, missing ids -> default)
Definition cfg_store.c:76
void cfg_params_get(const cfg_image_t *img, float v[PARAM_COUNT])
Definition cfg_store.c:85
int cfg_load(cfg_image_t *out)
latest valid image (version 1 converted); returns 0 if found, -1 if none (out left untouched)
Definition cfg_store.c:95
Persistent configuration: parameters, drone name, calibrations and one backup per calibration type (H...
uint8_t checks_build(const checks_in_t *in, pv2_check_t *it, uint8_t *ready, uint8_t *battery_fail)
fills items (PV2_CHECKS_MAX), returns the count; *ready = no FAIL; *battery_fail = the battery item i...
Definition checks.c:16
Pre-flight checks (CHECKS frame, HANDOVER_FW.md §9).
uint16_t ext_sensors(void)
PV2_S_TOF | PV2_S_FLOW currently detected.
Definition ext_sens.c:82
Expansion sensors on the Qwiic bus (ACwing Flow rev A): VL53L1X ToF and the optical-flow bridge.
uint8_t fc_is_core_op(uint8_t op)
ops executed by the core
Definition fc_core.c:100
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
int fc_take_result(fc_res_t *r)
1 if a result was taken
Definition fc_core.c:113
int fc_post(const fc_cmd_t *c)
0 = queued, -1 = full
Definition fc_core.c:112
void fc_set_sensors(uint16_t sensors)
sensors detected at run time (a late expansion board): atomic store
Definition fc_core.c:97
void fc_snapshot(fc_state_t *out)
Definition fc_core.c:121
int fc_take_event(fc_event_t *e)
Definition fc_core.c:114
Flight core: arming, modes, automatic actions, failsafes, motor test (HANDOVER_FW....
#define FC_FS_SHORT_MS
fsAppShort
Definition fc_core.h:29
#define FC_ARM_RC_FRESH_MS
throttle rule applies to RC this recent
Definition fc_core.h:33
#define FC_OP_MOTOR_STOP
stop a running motor test
Definition fc_core.h:53
void fsh_reset(void)
Definition fc_shared.c:15
volatile uint8_t g_fsh_cal_want
calibration samples (ImuTask -> AppTask)
Definition fc_shared.c:7
int fsh_apply_post(uint8_t type, const cal_set_t *set)
Definition fc_shared.c:47
int fsh_cal_pop(cal_sample_t *s)
Definition fc_shared.c:45
void fsh_est_read(fsh_est_t *out)
AppTask: keeps *out on a race.
Definition fc_shared.c:32
Data exchanged between the flight context (ImuTask) and the AppTask.
static uint16_t next(uint16_t s)
Definition log_store.c:50
int log_task_usb_app_take(appl_cmd_t *c)
1 if a command was taken
Definition log_task.c:68
uint8_t seq
Definition log_task.c:48
int log_task_ble_cmd(const log_cmd_t *c)
0 = queued, -1 = full (answer BUSY)
Definition log_task.c:66
uint8_t set
Definition log_task.c:39
int log_task_ble_take(log_msg_t *m)
1 if a frame was taken
Definition log_task.c:67
LogTask: owns the log flash, writes the flight log and serves the log download over USB.
uint8_t params_set(uint16_t id, float v, uint8_t *reason)
PARAM_SET rules: returns an ACK result (PV2_ACK_*) and sets *reason.
Definition params.c:68
uint16_t params_visible_count(uint16_t sensors)
Definition params.c:119
const param_def_t g_param_def[PARAM_COUNT]
Definition params.c:11
float param_get(int idx)
lock-free read, any thread
Definition params.c:56
uint8_t params_visible(int idx, uint16_t sensors)
listing: parameters whose sensor is missing are omitted
Definition params.c:112
void params_get_all(float *values)
PARAM_COUNT values.
Definition params.c:96
void params_mark_saved(void)
saved <- RAM
Definition params.c:101
void params_defaults(void)
RAM <- defaults (dirty until saved)
Definition params.c:45
int params_index(uint16_t id)
-1 if unknown
Definition params.c:50
void params_load(const float *values, uint16_t n)
flash image
Definition params.c:84
uint8_t params_dirty(int idx)
RAM != flash.
Definition params.c:106
int params_visible_pos(int idx, uint16_t sensors)
index in PARAM_LIST, -1 if hidden
Definition params.c:126
Drone parameters of protocol v2 (HANDOVER_FW.md §8, ids.ts PARAMS).
#define PARAM(i)
Definition params.h:43
@ P_BATT_WARN
Definition params.h:22
@ P_GF_MAX_DIST
Definition params.h:23
@ PARAM_COUNT
Definition params.h:29
@ P_BATT_CRIT
Definition params.h:22
@ P_GF_MAX_ALT
Definition params.h:23
@ P_BEGINNER
Definition params.h:20
size_t pv2_event(uint8_t level, uint16_t code, int32_t arg, const char *text, uint8_t *out, size_t cap)
Definition proto_v2.c:14
size_t pv2_ack(uint8_t op, uint8_t seq, uint8_t result, uint8_t reason, uint8_t *out, size_t cap)
Definition proto_v2.c:26
float pv2_rd_f32(const uint8_t *p)
Definition proto_v2.c:69
size_t pv2_param(uint16_t id, float value, uint8_t flags, uint16_t index, uint16_t count, uint8_t *out, size_t cap)
Definition proto_v2.c:32
size_t pv2_motors(const pv2_motor_t *m, uint8_t n, uint8_t *out, size_t cap)
Definition proto_v2.c:51
size_t pv2_frame(uint8_t type, const void *payload, uint8_t len, uint8_t *out, size_t cap)
Definition proto_v2.c:9
size_t pv2_checks(uint8_t ready, const pv2_check_t *items, uint8_t n, uint8_t *out, size_t cap)
Definition proto_v2.c:41
ACwing Pilot app protocol v2: identifiers and frame layouts.
#define PV2_S_MAG_FC
Definition proto_v2.h:123
@ PV2_ACK_UNSUPPORTED
Definition proto_v2.h:88
@ PV2_ACK_BUSY
Definition proto_v2.h:87
@ PV2_ACK_OK
Definition proto_v2.h:87
@ PV2_ACK_INVALID
Definition proto_v2.h:87
@ PV2_ACK_REJECTED
Definition proto_v2.h:87
static uint16_t pv2_rd_u16(const uint8_t *p)
little-endian readers for command payloads (unaligned-safe)
Definition proto_v2.h:329
#define PV2_FT_CAPS
Definition proto_v2.h:30
#define PV2_OP_SET_NAME
Definition proto_v2.h:75
@ PV2_R_NONE
Definition proto_v2.h:91
@ PV2_R_NOT_OVER_USB
flight command received over the USB link: ground link only
Definition proto_v2.h:97
@ PV2_R_UNKNOWN_PARAM
Definition proto_v2.h:95
@ PV2_R_ARMED
Definition proto_v2.h:93
@ PV2_R_CALIBRATING
Definition proto_v2.h:93
@ PV2_R_OTHER_LINK
configuration change from the link that does not have control
Definition proto_v2.h:98
#define PV2_OP_REQUEST
Definition proto_v2.h:74
#define PV2_FT_ACK
Definition proto_v2.h:39
#define PV2_FF_GEOFENCE_ON
Definition proto_v2.h:117
#define PV2_FF_LANDED
Definition proto_v2.h:115
#define PV2_OP_PARAM_LIST
Definition proto_v2.h:63
#define PV2_OP_PARAM_SAVE
Definition proto_v2.h:61
#define PV2_OP_MOTOR_REVERSE
Definition proto_v2.h:71
#define PV2_FT_POWER
Definition proto_v2.h:32
@ PV2_E_MODE_CHANGED
Definition proto_v2.h:141
@ PV2_E_MOTOR_HOT
Definition proto_v2.h:147
@ PV2_E_DISARMED
Definition proto_v2.h:140
@ PV2_E_CALIB_DONE
Definition proto_v2.h:148
@ PV2_E_TAKEOFF_STARTED
Definition proto_v2.h:140
@ PV2_E_PARAMS_SAVED
Definition proto_v2.h:149
@ PV2_E_FAILSAFE_RADIO
Definition proto_v2.h:143
@ PV2_E_RTH_STARTED
Definition proto_v2.h:141
@ PV2_E_RADIO_OVERRIDE
Definition proto_v2.h:148
@ 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_HOME_SET
Definition proto_v2.h:141
@ PV2_E_HIGH_VIBRATION
Definition proto_v2.h:147
@ PV2_E_MAG_INTERFERENCE
Definition proto_v2.h:147
@ 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_CALIB_FAILED
Definition proto_v2.h:148
@ PV2_E_GPS_LOST
Definition proto_v2.h:144
@ PV2_E_GEOFENCE_BREACH
Definition proto_v2.h:144
#define PV2_PROTO_VERSION
Definition proto_v2.h:22
#define PV2_S_TOF
Definition proto_v2.h:127
#define PV2_OP_CAL_START
Definition proto_v2.h:64
#define PV2_OP_MOTOR_REMAP
Definition proto_v2.h:72
#define PV2_FT_MOTORS
Definition proto_v2.h:34
#define PV2_FF_BEGINNER
Definition proto_v2.h:118
#define PV2_MOTORS_MAX
Definition proto_v2.h:316
#define PV2_S_BARO
Definition proto_v2.h:122
#define PV2_OP_PARAM_DEFAULTS
Definition proto_v2.h:62
#define PV2_FT_CHECKS
Definition proto_v2.h:40
#define PV2_OP_CAL_RESTORE
Definition proto_v2.h:68
#define PV2_OP_CAL_SAVE
Definition proto_v2.h:67
#define PV2_OP_PARAM_SET
Definition proto_v2.h:60
#define PV2_OP_CAL_SET_REF
Definition proto_v2.h:69
#define PV2_OP_CAL_ABORT
Definition proto_v2.h:66
#define PV2_OP_CAL_CAPTURE
Definition proto_v2.h:65
@ 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_CAL_COMPASS
Definition proto_v2.h:152
@ PV2_CAL_STICKS
Definition proto_v2.h:153
@ PV2_CAL_BATTERY
Definition proto_v2.h:153
@ PV2_CAL_ACCEL
Definition proto_v2.h:152
@ PV2_CAL_GYRO
Definition proto_v2.h:152
@ PV2_CAL_LEVEL
Definition proto_v2.h:152
#define PV2_OP_LOG_LIST
reserved in HANDOVER §16, USB link
Definition proto_v2.h:77
#define PV2_S_GNSS
Definition proto_v2.h:125
@ PV2_MODE_POS_HOLD
Definition proto_v2.h:102
@ PV2_MODE_ALT_HOLD
Definition proto_v2.h:102
#define PV2_CHECKS_MAX
Definition proto_v2.h:315
#define PV2_S_CURRENT
Definition proto_v2.h:129
#define PV2_OP_SET_TIME
unix_s u32, ms u16, tz_min i16: wall clock for the log
Definition proto_v2.h:76
#define PV2_FT_NAV
Definition proto_v2.h:33
#define PV2_FT_FLIGHT
Definition proto_v2.h:31
#define PV2_OP_STREAM_RATES
Definition proto_v2.h:73
#define PV2_FF_IN_AIR
Definition proto_v2.h:114
#define PV2_FT_INIT
Definition proto_v2.h:27
#define PV2_OP_LOG_READ
id u16, offset u32, len u32
Definition proto_v2.h:78
#define PV2_OP_LOG_ERASE
"WIPE" (firmware extension)
Definition proto_v2.h:79
#define PV2_OP_PARAM_GET
Definition proto_v2.h:59
#define PV2_FF_ARM_BLOCKED
Definition proto_v2.h:119
#define PV2_FT_SYSTEM
Definition proto_v2.h:35
uint16_t board_sensors
CAPS bits of the board itself (CURRENT, FLASH_LOG)
Definition app_task.h:36
char hw_rev
CAPS hwRev: 'N' Nucleo bench, 'A' ACwing FC rev A.
Definition app_task.h:29
uint32_t uid[3]
MCU unique id -> serial "ACW-xxxxxxxx"
Definition app_task.h:32
uint8_t(* log_pct)(void)
flash log used %, 0xFF = no log; may be NULL
Definition app_task.h:38
uint8_t motors_built
Definition app_task.h:31
size_t(* get_init)(uint8_t *dst, size_t cap)
INIT frame of the estimator (copy; 0 if not ready yet)
Definition app_task.h:34
uint8_t batt_monitor
a BattTask runs: POWER, CHECKS item 1, BATTERY calibration
Definition app_task.h:35
uint8_t n_motors
Definition app_task.h:30
uint8_t b[APPL_CMD_MAX]
Definition app_link.h:33
uint8_t len
Definition app_link.h:32
int16_t thr
Definition app_link.h:37
last flight summary (AppTask persists it for the BATTERY calibration)
Definition batt_task.h:35
float used_mah
charge used between ARM and DISARM
Definition batt_task.h:37
uint16_t count
flights finished since boot
Definition batt_task.h:36
float scale_i
current scale it was counted with
Definition batt_task.h:38
published state
Definition battery.h:51
uint8_t cells
0 = not known
Definition battery.h:53
float vbat
V, pack, filtered (τ 0.1 s)
Definition battery.h:56
uint32_t done_mask
bit (type-1): this calibration was done and saved
Definition calib_mgr.h:39
Version 2.
Definition cfg_store.h:34
cal_set_t bak
previous set, per type (CAL_RESTORE)
Definition cfg_store.h:42
float batt_used_scale_i
current scale it was counted with
Definition cfg_store.h:45
float batt_used_mah
charge used in the last flight
Definition cfg_store.h:44
char name[16]
Definition cfg_store.h:40
cal_set_t cal
saved calibrations
Definition cfg_store.h:41
uint32_t bak_mask
bit (type-1): bak holds a set for it
Definition cfg_store.h:43
float batt_warn
Definition checks.h:36
float batt_crit
params battWarn / battCrit
Definition checks.h:36
float gf_max_alt
param gfMaxAlt
Definition checks.h:24
uint8_t usb_host
a USB host is connected (item 12, FAIL)
Definition checks.h:37
uint8_t flow_ok
bridge alive, PMW3901 OK
Definition checks.h:28
uint8_t hold_mode
ALT_HOLD / POS_HOLD: centre stick = hold
Definition checks.h:23
uint8_t batt_monitor
battery measured (BattTask runs)
Definition checks.h:31
uint16_t sensors
CAPS bits
Definition checks.h:15
float gf_max_dist
param gfMaxDist
Definition checks.h:25
uint8_t rc_fresh
RC from the app in the last 0.5 s
Definition checks.h:21
int16_t rc_thr
Definition checks.h:22
uint32_t cal_done_mask
bit (type-1): calibration done and saved
Definition checks.h:20
uint8_t batt_present
Definition checks.h:33
uint8_t imu_ok
IMU initialised
Definition checks.h:16
uint8_t batt_stale
BattTask silent
Definition checks.h:32
uint8_t in_air
Definition checks.h:29
uint8_t batt_level
latched level from the flight core
Definition checks.h:34
uint8_t est_running
attitude estimate converged and running
Definition checks.h:17
float mag_interf_pct
Definition checks.h:19
uint8_t flow_q
0..100, 0xFF n/a
Definition checks.h:27
float att_unc_deg
1 sigma
Definition checks.h:18
float batt_cell_v
average cell, sagged and filtered
Definition checks.h:35
uint8_t alt_valid
vertical estimate running (baro / ToF)
Definition checks.h:26
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
uint8_t level
Definition fc_core.h:60
int32_t arg
Definition fc_core.h:60
uint16_t code
Definition fc_core.h:60
uint8_t result
Definition fc_core.h:59
uint8_t op
Definition fc_core.h:59
uint8_t reason
Definition fc_core.h:59
uint8_t seq
Definition fc_core.h:59
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
int32_t alt_rel_cm
above the take-off point
Definition fc_core.h:96
uint8_t batt_stale
BattTask silent: battery state unknown
Definition fc_core.h:95
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
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
float loop_hz
Definition fc_shared.h:24
uint8_t alt_valid
vertical / navigation
Definition fc_shared.h:26
float cpu_pct
Definition fc_shared.h:24
uint8_t flow_ok
bridge alive, PMW3901 OK
Definition fc_shared.h:30
uint16_t tof_mm
raw, 0xFFFF n/a
Definition fc_shared.h:28
uint8_t running
Definition fc_shared.h:18
uint8_t flow_q
0..100, 0xFF n/a
Definition fc_shared.h:29
uint16_t sensors
IMU / MAG_FC / BARO found by the IMU init.
Definition fc_shared.h:34
float mag_interf_pct
Definition fc_shared.h:21
float vib_mg
RMS of |a| - 1 g over ~1 s
Definition fc_shared.h:22
float imu_temp_c
Definition fc_shared.h:24
float vel_body[2]
m/s from flow x height (x fwd, y left); 0 when not usable
Definition fc_shared.h:32
float jitter_us
Definition fc_shared.h:24
uint8_t acc_gated
Definition fc_shared.h:18
uint8_t imu_ok
Definition fc_shared.h:18
float att_unc_deg
1 sigma, largest axis
Definition fc_shared.h:20
uint16_t acc_clip
samples near full scale since arm
Definition fc_shared.h:23
uint8_t flow_usable
above the PMW3901 minimum height (80 mm)
Definition fc_shared.h:31
uint8_t mag_gated
Definition fc_shared.h:18
float vz
Definition fc_shared.h:27
uint32_t baro_pa
0 n/a
Definition fc_shared.h:33
float q[4]
body -> world NWU
Definition fc_shared.h:19
a log command received over BLE (op, seq, arguments as written by the app)
Definition log_task.h:51
uint8_t p[17]
Definition log_task.h:51
uint8_t seq
Definition log_task.h:51
uint8_t op
Definition log_task.h:51
uint8_t len
Definition log_task.h:51
an answer frame for BLE: type and payload (LOG_ENTRY, LOG_DATA, ACK)
Definition log_task.h:53
uint8_t type
Definition log_task.h:53
uint8_t len
Definition log_task.h:53
uint8_t p[sizeof(pv2_log_data_hdr_t)+PV2_LOG_DATA_MAX]
Definition log_task.h:53
uint16_t id
Definition params.h:33
char serial[12]
zero padded
Definition proto_v2.h:176
char name[16]
zero padded
Definition proto_v2.h:175
uint8_t hw_rev
ASCII letter.
Definition proto_v2.h:169
uint8_t cells
Definition proto_v2.h:174
uint8_t frame
Definition proto_v2.h:173
uint8_t n_motors
Definition proto_v2.h:172
uint8_t proto
< 0x05 CAPS
Definition proto_v2.h:167
uint8_t model
Definition proto_v2.h:170
uint16_t sensors
Definition proto_v2.h:171
uint8_t fw[3]
Definition proto_v2.h:168
uint8_t mode
Definition proto_v2.h:180
uint16_t flags
Definition proto_v2.h:181
uint16_t flight_time_s
Definition proto_v2.h:183
uint16_t heading_cdeg
Definition proto_v2.h:187
int32_t alt_rel_cm
Definition proto_v2.h:184
int16_t vz_cms
Definition proto_v2.h:185
uint16_t ground_speed_cms
Definition proto_v2.h:186
uint8_t input
Definition proto_v2.h:182
uint8_t arm
< 0x06 FLIGHT
Definition proto_v2.h:180
uint8_t failsafe
Definition proto_v2.h:180
uint16_t home_bearing_cdeg
Definition proto_v2.h:189
uint16_t home_dist_m
0xFFFF n/a
Definition proto_v2.h:188
uint8_t autom
Definition proto_v2.h:180
uint16_t rth_eta_s
0xFFFF n/a
Definition proto_v2.h:190
int8_t temp_c
-128 n/a
Definition proto_v2.h:219
uint8_t out_pct
Definition proto_v2.h:220
uint16_t rpm
< 0x09 MOTORS, one entry (after n u8)
Definition proto_v2.h:218
uint16_t vbat_mv
< 0x07 POWER
Definition proto_v2.h:194
uint16_t remain_s
0xFFFF n/a
Definition proto_v2.h:200
uint16_t cell_mv
Definition proto_v2.h:198
uint8_t warn
Definition proto_v2.h:201
uint16_t used_mah
Definition proto_v2.h:196
uint8_t cells
Definition proto_v2.h:197
uint8_t pct
Definition proto_v2.h:199
int16_t current_ca
Definition proto_v2.h:195
uint8_t left
Definition app_task.c:57
uint8_t len
Definition app_task.c:57
uint32_t next
Definition app_task.c:57
uint8_t f[FRAME_CAP]
Definition app_task.c:57
uint16_t tlm_ble_hi_free(void)
bytes free in HI
Definition tlm_ble.c:110
int tlm_ble_take_subscribe(void)
1 once after a client enabled notifications (AppTask: CAPS, INIT, CHECKS)
Definition tlm_ble.c:135
int tlm_ble_push_hi(const uint8_t *frame, uint16_t len)
HI: AppTask
Definition tlm_ble.c:104
tlm_ble_state_t tlm_ble_state(void)
Definition tlm_ble.c:235
void tlm_ble_open_streams(void)
Definition tlm_ble.c:142
BLE telemetry, transport-independent part (phase 2 + protocol v2).
@ TLM_BLE_STREAMING
Definition tlm_ble.h:48
#define TLM_BLE_RING_SIZE
per queue, power of 2
Definition tlm_ble.h:42
static USBD_StatusTypeDef st(HAL_StatusTypeDef h)
Definition usbd_conf.c:86