Fix blackbox compilation when included but not enabled.
[betaflight.git] / src / main / blackbox / blackbox_io.c
blob5c03dc34bcf0a0a5e400e6f5d1134c4689374261
1 #include <stdlib.h>
2 #include <stdarg.h>
3 #include <string.h>
5 #include "blackbox_io.h"
7 #include "platform.h"
8 #include "version.h"
10 #include "common/maths.h"
11 #include "common/axis.h"
12 #include "common/color.h"
14 #include "drivers/gpio.h"
15 #include "drivers/sensor.h"
16 #include "drivers/system.h"
17 #include "drivers/serial.h"
18 #include "drivers/compass.h"
19 #include "drivers/timer.h"
20 #include "drivers/pwm_rx.h"
21 #include "drivers/accgyro.h"
22 #include "drivers/light_led.h"
23 #include "drivers/sound_beeper.h"
25 #include "sensors/sensors.h"
26 #include "sensors/boardalignment.h"
27 #include "sensors/acceleration.h"
28 #include "sensors/barometer.h"
29 #include "sensors/gyro.h"
30 #include "sensors/battery.h"
32 #include "io/beeper.h"
33 #include "io/display.h"
34 #include "io/escservo.h"
35 #include "rx/rx.h"
36 #include "io/rc_controls.h"
38 #include "io/gimbal.h"
39 #include "io/gps.h"
40 #include "io/ledstrip.h"
41 #include "io/serial.h"
42 #include "io/serial_cli.h"
43 #include "io/serial_msp.h"
44 #include "io/statusindicator.h"
45 #include "rx/msp.h"
46 #include "telemetry/telemetry.h"
47 #include "common/printf.h"
49 #include "flight/mixer.h"
50 #include "flight/altitudehold.h"
51 #include "flight/failsafe.h"
52 #include "flight/imu.h"
53 #include "flight/navigation.h"
55 #include "config/runtime_config.h"
56 #include "config/config.h"
57 #include "config/config_profile.h"
58 #include "config/config_master.h"
60 #include "io/flashfs.h"
62 #ifdef BLACKBOX
64 #define BLACKBOX_BAUDRATE 115200
65 #define BLACKBOX_INITIAL_PORT_MODE MODE_TX
67 // How many bytes should we transmit per loop iteration?
68 uint8_t blackboxWriteChunkSize = 16;
70 static serialPort_t *blackboxPort;
71 static portMode_t previousPortMode;
72 static uint32_t previousBaudRate;
74 void blackboxWrite(uint8_t value)
76 switch (masterConfig.blackbox_device) {
77 #ifdef USE_FLASHFS
78 case BLACKBOX_DEVICE_FLASH:
79 flashfsWriteByte(value); // Write byte asynchronously
80 break;
81 #endif
82 case BLACKBOX_DEVICE_SERIAL:
83 default:
84 serialWrite(blackboxPort, value);
85 break;
89 static void _putc(void *p, char c)
91 (void)p;
92 blackboxWrite(c);
95 //printf() to the blackbox serial port with no blocking shenanigans (so it's caller's responsibility to not write too fast!)
96 void blackboxPrintf(char *fmt, ...)
98 va_list va;
99 va_start(va, fmt);
100 tfp_format(NULL, _putc, fmt, va);
101 va_end(va);
104 // Print the null-terminated string 's' to the serial port and return the number of bytes written
105 int blackboxPrint(const char *s)
107 int length;
108 const uint8_t *pos;
110 switch (masterConfig.blackbox_device) {
112 #ifdef USE_FLASHFS
113 case BLACKBOX_DEVICE_FLASH:
114 length = strlen(s);
115 flashfsWrite((const uint8_t*) s, length, false); // Write asynchronously
116 break;
117 #endif
119 case BLACKBOX_DEVICE_SERIAL:
120 default:
121 pos = (uint8_t*) s;
122 while (*pos) {
123 serialWrite(blackboxPort, *pos);
124 pos++;
127 length = pos - (uint8_t*) s;
128 break;
131 return length;
135 * Write an unsigned integer to the blackbox serial port using variable byte encoding.
137 void blackboxWriteUnsignedVB(uint32_t value)
139 //While this isn't the final byte (we can only write 7 bits at a time)
140 while (value > 127) {
141 blackboxWrite((uint8_t) (value | 0x80)); // Set the high bit to mean "more bytes follow"
142 value >>= 7;
144 blackboxWrite(value);
148 * Write a signed integer to the blackbox serial port using ZigZig and variable byte encoding.
150 void blackboxWriteSignedVB(int32_t value)
152 //ZigZag encode to make the value always positive
153 blackboxWriteUnsignedVB((uint32_t)((value << 1) ^ (value >> 31)));
156 void blackboxWriteS16(int16_t value)
158 blackboxWrite(value & 0xFF);
159 blackboxWrite((value >> 8) & 0xFF);
163 * Write a 2 bit tag followed by 3 signed fields of 2, 4, 6 or 32 bits
165 void blackboxWriteTag2_3S32(int32_t *values) {
166 static const int NUM_FIELDS = 3;
168 //Need to be enums rather than const ints if we want to switch on them (due to being C)
169 enum {
170 BITS_2 = 0,
171 BITS_4 = 1,
172 BITS_6 = 2,
173 BITS_32 = 3
176 enum {
177 BYTES_1 = 0,
178 BYTES_2 = 1,
179 BYTES_3 = 2,
180 BYTES_4 = 3
183 int x;
184 int selector = BITS_2, selector2;
187 * Find out how many bits the largest value requires to encode, and use it to choose one of the packing schemes
188 * below:
190 * Selector possibilities
192 * 2 bits per field ss11 2233,
193 * 4 bits per field ss00 1111 2222 3333
194 * 6 bits per field ss11 1111 0022 2222 0033 3333
195 * 32 bits per field sstt tttt followed by fields of various byte counts
197 for (x = 0; x < NUM_FIELDS; x++) {
198 //Require more than 6 bits?
199 if (values[x] >= 32 || values[x] < -32) {
200 selector = BITS_32;
201 break;
204 //Require more than 4 bits?
205 if (values[x] >= 8 || values[x] < -8) {
206 if (selector < BITS_6) {
207 selector = BITS_6;
209 } else if (values[x] >= 2 || values[x] < -2) { //Require more than 2 bits?
210 if (selector < BITS_4) {
211 selector = BITS_4;
216 switch (selector) {
217 case BITS_2:
218 blackboxWrite((selector << 6) | ((values[0] & 0x03) << 4) | ((values[1] & 0x03) << 2) | (values[2] & 0x03));
219 break;
220 case BITS_4:
221 blackboxWrite((selector << 6) | (values[0] & 0x0F));
222 blackboxWrite((values[1] << 4) | (values[2] & 0x0F));
223 break;
224 case BITS_6:
225 blackboxWrite((selector << 6) | (values[0] & 0x3F));
226 blackboxWrite((uint8_t)values[1]);
227 blackboxWrite((uint8_t)values[2]);
228 break;
229 case BITS_32:
231 * Do another round to compute a selector for each field, assuming that they are at least 8 bits each
233 * Selector2 field possibilities
234 * 0 - 8 bits
235 * 1 - 16 bits
236 * 2 - 24 bits
237 * 3 - 32 bits
239 selector2 = 0;
241 //Encode in reverse order so the first field is in the low bits:
242 for (x = NUM_FIELDS - 1; x >= 0; x--) {
243 selector2 <<= 2;
245 if (values[x] < 128 && values[x] >= -128) {
246 selector2 |= BYTES_1;
247 } else if (values[x] < 32768 && values[x] >= -32768) {
248 selector2 |= BYTES_2;
249 } else if (values[x] < 8388608 && values[x] >= -8388608) {
250 selector2 |= BYTES_3;
251 } else {
252 selector2 |= BYTES_4;
256 //Write the selectors
257 blackboxWrite((selector << 6) | selector2);
259 //And now the values according to the selectors we picked for them
260 for (x = 0; x < NUM_FIELDS; x++, selector2 >>= 2) {
261 switch (selector2 & 0x03) {
262 case BYTES_1:
263 blackboxWrite(values[x]);
264 break;
265 case BYTES_2:
266 blackboxWrite(values[x]);
267 blackboxWrite(values[x] >> 8);
268 break;
269 case BYTES_3:
270 blackboxWrite(values[x]);
271 blackboxWrite(values[x] >> 8);
272 blackboxWrite(values[x] >> 16);
273 break;
274 case BYTES_4:
275 blackboxWrite(values[x]);
276 blackboxWrite(values[x] >> 8);
277 blackboxWrite(values[x] >> 16);
278 blackboxWrite(values[x] >> 24);
279 break;
282 break;
287 * Write an 8-bit selector followed by four signed fields of size 0, 4, 8 or 16 bits.
289 void blackboxWriteTag8_4S16(int32_t *values) {
291 //Need to be enums rather than const ints if we want to switch on them (due to being C)
292 enum {
293 FIELD_ZERO = 0,
294 FIELD_4BIT = 1,
295 FIELD_8BIT = 2,
296 FIELD_16BIT = 3
299 uint8_t selector, buffer;
300 int nibbleIndex;
301 int x;
303 selector = 0;
304 //Encode in reverse order so the first field is in the low bits:
305 for (x = 3; x >= 0; x--) {
306 selector <<= 2;
308 if (values[x] == 0) {
309 selector |= FIELD_ZERO;
310 } else if (values[x] < 8 && values[x] >= -8) {
311 selector |= FIELD_4BIT;
312 } else if (values[x] < 128 && values[x] >= -128) {
313 selector |= FIELD_8BIT;
314 } else {
315 selector |= FIELD_16BIT;
319 blackboxWrite(selector);
321 nibbleIndex = 0;
322 buffer = 0;
323 for (x = 0; x < 4; x++, selector >>= 2) {
324 switch (selector & 0x03) {
325 case FIELD_ZERO:
326 //No-op
327 break;
328 case FIELD_4BIT:
329 if (nibbleIndex == 0) {
330 //We fill high-bits first
331 buffer = values[x] << 4;
332 nibbleIndex = 1;
333 } else {
334 blackboxWrite(buffer | (values[x] & 0x0F));
335 nibbleIndex = 0;
337 break;
338 case FIELD_8BIT:
339 if (nibbleIndex == 0) {
340 blackboxWrite(values[x]);
341 } else {
342 //Write the high bits of the value first (mask to avoid sign extension)
343 blackboxWrite(buffer | ((values[x] >> 4) & 0x0F));
344 //Now put the leftover low bits into the top of the next buffer entry
345 buffer = values[x] << 4;
347 break;
348 case FIELD_16BIT:
349 if (nibbleIndex == 0) {
350 //Write high byte first
351 blackboxWrite(values[x] >> 8);
352 blackboxWrite(values[x]);
353 } else {
354 //First write the highest 4 bits
355 blackboxWrite(buffer | ((values[x] >> 12) & 0x0F));
356 // Then the middle 8
357 blackboxWrite(values[x] >> 4);
358 //Only the smallest 4 bits are still left to write
359 buffer = values[x] << 4;
361 break;
364 //Anything left over to write?
365 if (nibbleIndex == 1) {
366 blackboxWrite(buffer);
371 * Write `valueCount` fields from `values` to the Blackbox using signed variable byte encoding. A 1-byte header is
372 * written first which specifies which fields are non-zero (so this encoding is compact when most fields are zero).
374 * valueCount must be 8 or less.
376 void blackboxWriteTag8_8SVB(int32_t *values, int valueCount)
378 uint8_t header;
379 int i;
381 if (valueCount > 0) {
382 //If we're only writing one field then we can skip the header
383 if (valueCount == 1) {
384 blackboxWriteSignedVB(values[0]);
385 } else {
386 //First write a one-byte header that marks which fields are non-zero
387 header = 0;
389 // First field should be in low bits of header
390 for (i = valueCount - 1; i >= 0; i--) {
391 header <<= 1;
393 if (values[i] != 0) {
394 header |= 0x01;
398 blackboxWrite(header);
400 for (i = 0; i < valueCount; i++) {
401 if (values[i] != 0) {
402 blackboxWriteSignedVB(values[i]);
410 * If there is data waiting to be written to the blackbox device, attempt to write (a portion of) that now.
412 * Returns true if all data has been flushed to the device.
414 bool blackboxDeviceFlush(void)
416 switch (masterConfig.blackbox_device) {
417 case BLACKBOX_DEVICE_SERIAL:
418 //Nothing to speed up flushing on serial, as serial is continuously being drained out of its buffer
419 return isSerialTransmitBufferEmpty(blackboxPort);
421 #ifdef USE_FLASHFS
422 case BLACKBOX_DEVICE_FLASH:
423 return flashfsFlushAsync();
424 #endif
426 default:
427 return false;
432 * Attempt to open the logging device. Returns true if successful.
434 bool blackboxDeviceOpen(void)
437 * We want to write at about 7200 bytes per second to give the OpenLog a good chance to save to disk. If
438 * about looptime microseconds elapse between our writes, this is the budget of how many bytes we should
439 * transmit with each write.
441 * 9 / 1250 = 7200 / 1000000
443 blackboxWriteChunkSize = MAX((masterConfig.looptime * 9) / 1250, 4);
445 switch (masterConfig.blackbox_device) {
446 case BLACKBOX_DEVICE_SERIAL:
447 blackboxPort = findOpenSerialPort(FUNCTION_BLACKBOX);
448 if (blackboxPort) {
449 previousPortMode = blackboxPort->mode;
450 previousBaudRate = blackboxPort->baudRate;
452 serialSetBaudRate(blackboxPort, BLACKBOX_BAUDRATE);
453 serialSetMode(blackboxPort, BLACKBOX_INITIAL_PORT_MODE);
454 beginSerialPortFunction(blackboxPort, FUNCTION_BLACKBOX);
455 } else {
456 blackboxPort = openSerialPort(FUNCTION_BLACKBOX, NULL, BLACKBOX_BAUDRATE, BLACKBOX_INITIAL_PORT_MODE, SERIAL_NOT_INVERTED);
458 if (blackboxPort) {
459 previousPortMode = blackboxPort->mode;
460 previousBaudRate = blackboxPort->baudRate;
464 return blackboxPort != NULL;
465 break;
466 #ifdef USE_FLASHFS
467 case BLACKBOX_DEVICE_FLASH:
468 if (flashfsGetSize() == 0 || isBlackboxDeviceFull()) {
469 return false;
472 return true;
473 break;
474 #endif
475 default:
476 return false;
481 * Close the Blackbox logging device immediately without attempting to flush any remaining data.
483 void blackboxDeviceClose(void)
485 switch (masterConfig.blackbox_device) {
486 case BLACKBOX_DEVICE_SERIAL:
487 serialSetMode(blackboxPort, previousPortMode);
488 serialSetBaudRate(blackboxPort, previousBaudRate);
490 endSerialPortFunction(blackboxPort, FUNCTION_BLACKBOX);
493 * Normally this would be handled by mw.c, but since we take an unknown amount
494 * of time to shut down asynchronously, we're the only ones that know when to call it.
496 if (isSerialPortFunctionShared(FUNCTION_BLACKBOX, FUNCTION_MSP)) {
497 mspAllocateSerialPorts(&masterConfig.serialConfig);
499 break;
503 bool isBlackboxDeviceFull(void)
505 switch (masterConfig.blackbox_device) {
506 case BLACKBOX_DEVICE_SERIAL:
507 return false;
509 #ifdef USE_FLASHFS
510 case BLACKBOX_DEVICE_FLASH:
511 return flashfsIsEOF();
512 #endif
514 default:
515 return false;
519 #endif