Paparazzi UAS v7.1_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
sdlog_chibios.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2013-2015 Gautier Hattenberger, Alexandre Bustico
3 *
4 * This file is part of paparazzi.
5 *
6 * paparazzi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * paparazzi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with paparazzi; see the file COPYING. If not, write to
18 * the Free Software Foundation, 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22/*
23 * @file modules/loggers/sdlog_chibios.c
24 * @brief sdlog process with battery monitoring
25 *
26 */
27
28#include <ch.h>
29#include <hal.h>
35#include "mcu_periph/adc.h"
36#include "mcu.h"
37#include "led.h"
38
39#if HAL_USE_RTC
40#include <hal_rtc.h>
41#include <time.h>
42#include "modules/gps/gps.h"
43#endif
44
45#define GPS_LEAP_SECONDS 18
46
47// Delay before starting SD log
48#ifndef SDLOG_START_DELAY
49#if INS_EKF2_LOG_INPUTS
50/* the EKF2 only starts once the log is open to log all its inputs, don't wait long */
51#define SDLOG_START_DELAY 2
52#else
53#define SDLOG_START_DELAY 30
54#endif
55#endif
56#if INS_EKF2_LOG_INPUTS && SDLOG_START_DELAY > 5
57PRINT_CONFIG_MSG("INS_EKF2_LOG_INPUTS: the EKF2 only starts after the SD log is open (SDLOG_START_DELAY), set it low")
58#endif
59
60// Auto-flush period (in seconds)
61#ifndef SDLOG_AUTO_FLUSH_PERIOD
62#define SDLOG_AUTO_FLUSH_PERIOD 10
63#endif
64
65// Contiguous storage memory (in Mo)
66#ifndef SDLOG_CONTIGUOUS_STORAGE_MEM
67#define SDLOG_CONTIGUOUS_STORAGE_MEM 50
68#endif
69
70// Flush file on power loss
71#ifndef SDLOG_ENABLE_LOWBAT_FLUSH
72#define SDLOG_ENABLE_LOWBAT_FLUSH TRUE
73#endif
74
75
76#if (!defined USE_ADC_WATCHDOG) || (USE_ADC_WATCHDOG == 0)
77#error sdlog_chibios need USE_ADC_WATCHDOG in order to properly close files when power is unplugged
78#endif
79
80static const char PPRZ_LOG_NAME[] = "pprzlog_";
81static const char PPRZ_LOG_DIR[] = "PPRZ";
82
85#ifndef SDLOG_CONF
86#define SDLOG_CONF TRUE
87#endif
88#if SDLOG_CONF
89#include "generated/aircraft_conf.h"
90#endif
93// Error of the start step that failed, the storage status can be changed later by other calls
95static const char *sdlog_start_step = "";
96static volatile uint8_t sdlog_start_fatfs = 0;
97// Every switch into error is counted with its cause by the logger thread (it checks every second),
98// so the GCS report (every LOGGER_STATUS) misses none and gives the cause of that moment
99static volatile uint32_t sdlog_error_events = 0;
101
102/*
103 * Start log thread
104 */
105static IN_DMA_SECTION(THD_WORKING_AREA(wa_thd_startlog, 4096));
106static __attribute__((noreturn)) void thd_startlog(void *arg);
107
108bool sdOk = false;
109
111
113
114#if FLIGHTRECORDER_SDLOG
115static const char FLIGHTRECORDER_LOG_NAME[] = "fr_";
116static const char FR_LOG_DIR[] = "FLIGHT_RECORDER";
117FileDes flightRecorderLogFile = -1;
118#endif
119
122static enum {
127
131static char NO_FILE_NAME[] = "none";
132
133#if PREFLIGHT_CHECKS
134/* Preflight checks */
136static struct preflight_check_t sdlog_pfc;
137
138static void sdlog_preflight(struct preflight_result_t *result) {
140 const char *cause = (chibios_sdlog_status == SDLOG_STOPPED) ? "not started yet" :
142#ifdef SDLOG_PREFLIGHT_ERROR
143 preflight_error(result, "SD log not running: %s", cause);
144#else
145 preflight_warning(result, "SD log not running: %s", cause);
146#endif
147 } else {
148 preflight_success(result, "SDLogger running");
149 }
150}
151#endif // PREFLIGHT_CHECKS
152
153#if PERIODIC_TELEMETRY
160static void sdlog_report_status(struct transport_tx *trans, struct link_device *dev)
161{
162 static uint32_t reported_errors = 0;
163 static bool start_reported = false;
164 char msg[120];
165 const uint32_t errors = sdlog_error_events;
166 if (errors != reported_errors) {
167 // went into error (at the start or later) since the last report
168 reported_errors = errors;
169 const SdioError start_error = sdlog_start_error;
170 if (start_error != SDLOG_OK && sdlog_start_fatfs != 0) {
171 // the start failed: the failing step and its FatFS result tell why the card could not be used
172 chsnprintf(msg, sizeof(msg), "SD log error: %s (%s, FatFS %d)", sdlog_error_string(start_error),
174 } else if (start_error != SDLOG_OK) {
175 chsnprintf(msg, sizeof(msg), "SD log error: %s (%s)", sdlog_error_string(start_error), sdlog_start_step);
176 } else {
177 chsnprintf(msg, sizeof(msg), "SD log error: %s", sdlog_error_string(sdlog_error_cause));
178 }
179 } else if (!start_reported && chibios_sdlog_status != SDLOG_STOPPED) {
180 // started: only a configuration that could not be written is reported, a normal start is not
181 start_reported = true;
182 if (sdlog_conf_error == SDLOG_OK) {
183 return;
184 }
185 chsnprintf(msg, sizeof(msg), "SD log: configuration not written: %s", sdlog_error_string(sdlog_conf_error));
186 } else {
187 return;
188 }
189 pprz_msg_send_INFO_MSG(trans, dev, AC_ID, strlen(msg), msg);
190}
191
192static void send_sdlog_status(struct transport_tx *trans, struct link_device *dev)
193{
194 sdlog_report_status(trans, dev);
198 uint8_t size = strlen(chibios_sdlog_filenames);
199 char *filenames = chibios_sdlog_filenames;
200 if (size == 0) {
201 // when no file opened
202 filenames = NO_FILE_NAME;
203 size = strlen(filenames);
204 }
205 pprz_msg_send_LOGGER_STATUS(trans, dev, AC_ID, &status, &errno, &used, size, filenames);
206}
207#endif
208
209
210// Functions for the generic device API
211static int sdlog_check_free_space(struct chibios_sdlog *p __attribute__((unused)), long *fd, uint16_t len)
212{
213 SdLogBuffer *sdb;
214 SdioError status = sdLogAllocSDB(&sdb, len);
215 if (status != SDLOG_OK) {
216 return 0;
217 } else {
218 *fd = (long) sdb;
219 return 1;
220 }
221}
222
223static void sdlog_transmit(struct chibios_sdlog *p __attribute__((unused)), long fd, uint8_t byte)
224{
225 SdLogBuffer *sdb = (SdLogBuffer *) fd;
226 uint8_t *data = (uint8_t *) sdLogGetBufferFromSDB(sdb);
227 *data = byte;
228 sdLogSeekBufferFromSDB(sdb, 1);
229}
230
231static void sdlog_transmit_buffer(struct chibios_sdlog *p __attribute__((unused)), long fd, uint8_t *data, uint16_t len)
232{
233 SdLogBuffer *sdb = (SdLogBuffer *) fd;
234 memcpy(sdLogGetBufferFromSDB(sdb), data, len);
235 sdLogSeekBufferFromSDB(sdb, len);
236}
237
238static void sdlog_send(struct chibios_sdlog *p, long fd)
239{
240 SdLogBuffer *sdb = (SdLogBuffer *) fd;
241 sdLogWriteSDB(*(p->file), sdb);
242}
243
244static int null_function(struct chibios_sdlog *p __attribute__((unused))) { return 0; }
245static uint8_t null_byte_function(struct chibios_sdlog *p __attribute__((unused))) { return 0; }
246
247void chibios_sdlog_init(struct chibios_sdlog *sdlog, FileDes *file)
248{
249 // Store file descriptor
250 sdlog->file = file;
251 // Configure generic device
252 sdlog->device.periph = (void *)(sdlog);
253 sdlog->device.check_free_space = (check_free_space_t) sdlog_check_free_space;
254 sdlog->device.put_byte = (put_byte_t) sdlog_transmit;
255 sdlog->device.put_buffer = (put_buffer_t) sdlog_transmit_buffer;
256 sdlog->device.send_message = (send_message_t) sdlog_send;
257 sdlog->device.char_available = (char_available_t) null_function; // write only
258 sdlog->device.get_byte = (get_byte_t) null_byte_function; // write only
259
260}
261
263{
265#if PERIODIC_TELEMETRY
267#endif
268
269#if PREFLIGHT_CHECKS
270 preflight_check_register(&sdlog_pfc, sdlog_preflight);
271#endif
272
273 // Start polling on USB
275
276 // Start log thread
277 chThdCreateStatic(wa_thd_startlog, sizeof(wa_thd_startlog),
278 NORMALPRIO + 2, thd_startlog, NULL);
279}
280
281
282void sdlog_chibios_finish(const bool flush)
283{
284 if (pprzLogFile != -1) {
285 // if a FF_FS_REENTRANT is true, we can umount fs without closing
286 // file, fatfs lock will assure that umount is done after a write,
287 // and umount will close all open files cleanly. Thats the fatest
288 // way to umount cleanly filesystem.
289 //
290 // if FF_FS_REENTRANT is false,
291 // we have to flush and close files before unmounting filesystem
292#if FF_FS_REENTRANT == 0
293 sdLogCloseAllLogs(flush);
294#else
295 (void) flush;
296#endif
297
298 sdLogFinish();
299 pprzLogFile = -1;
300#if FLIGHTRECORDER_SDLOG
301 flightRecorderLogFile = -1;
302#endif
303 }
305}
306
307#if defined(SDLOG_BAT_ADC) && defined(SDLOG_BAT_CHAN)
308/*
309 * Bat survey thread
310 */
311#if SDLOG_ENABLE_LOWBAT_FLUSH
312static THD_WORKING_AREA(wa_thd_bat_survey, 1024);
313static __attribute__((noreturn)) void thd_bat_survey(void *arg);
314static void powerOutageIsr(void);
315event_source_t powerOutageSource;
316event_listener_t powerOutageListener;
317
318#define DefaultAdcOfVoltage(voltage) ((uint32_t) (voltage/(DefaultVoltageOfAdc(1))))
319static const uint16_t V_ALERT = DefaultAdcOfVoltage(5.5f);
320
321
322/*
323 powerOutageIsr is called within a lock zone from an isr, so no lock/unlock is needed
324*/
325static void powerOutageIsr(void)
326{
327 chEvtBroadcastI(&powerOutageSource);
328}
329
330static void thd_bat_survey(void *arg)
331{
332 (void)arg;
333 chRegSetThreadName("battery survey");
334 chEvtRegister(&powerOutageSource, &powerOutageListener, 1);
335 chThdSleepMilliseconds(2000);
336
337 register_adc_watchdog(&SDLOG_BAT_ADC, SDLOG_BAT_CHAN, V_ALERT, &powerOutageIsr);
338
339 chEvtWaitOne(EVENT_MASK(1));
340 // Only try to energy save is it is really a problem and not powered through USB
341 if (palReadPad(SDLOG_USB_VBUS_PORT, SDLOG_USB_VBUS_PIN) == PAL_LOW) {
342 // disable all required periph to save energy and maximize chance to flush files
343 // to mass storage and avoid infamous dirty bit on filesystem
345 }
346
347 // in case of powerloss, we should go fast and avoid to flush ram buffer
349
350 // Only put to deep sleep in case there is no power on the USB
351 if (palReadPad(SDLOG_USB_VBUS_PORT, SDLOG_USB_VBUS_PIN) == PAL_LOW) {
353 }
354 chThdExit(0);
355 while (true); // never goes here, only to avoid compiler warning: 'noreturn' function does return
356}
357#endif // SDLOG_ENABLE_LOWBAT_FLUSH
358#endif
359
361static void sdlog_start_failed(const char *step, SdioError err)
362{
363 if (sdOk) {
366 sdlog_start_error = err;
367 sdlog_error_cause = err;
368 }
369 sdOk = false;
370}
371
372static void thd_startlog(void *arg)
373{
374 (void) arg;
375 chRegSetThreadName("start log");
376 char tmpFilename[32];
377
378 // Wait before starting the log if needed
379 chThdSleepSeconds(SDLOG_START_DELAY);
380 // Check if we are already in USB Storage mode
381 if (usbStorageIsItRunning()) {
382 chThdSleepSeconds(20000); // stuck here for hours FIXME stop the thread ?
383 }
384
385 // Init sdlog struct
387
388 // Check for init errors
389 sdOk = true;
390
391 SdioError err = sdLogInit(NULL);
392 if (err != SDLOG_OK) {
393 sdlog_start_failed("init", err);
394 } else {
396 err = sdLogOpenLog(&pprzLogFile, PPRZ_LOG_DIR,
398 SDLOG_CONTIGUOUS_STORAGE_MEM, LOG_PREALLOCATION_DISABLED, tmpFilename, sizeof(tmpFilename));
399 if (err != SDLOG_OK) {
400 sdlog_start_failed("open log", err);
401 }
403#if FLIGHTRECORDER_SDLOG
404 removeEmptyLogs(FR_LOG_DIR, FLIGHTRECORDER_LOG_NAME, 50);
405 err = sdLogOpenLog(&flightRecorderLogFile, FR_LOG_DIR, FLIGHTRECORDER_LOG_NAME,
407 SDLOG_CONTIGUOUS_STORAGE_MEM, LOG_PREALLOCATION_DISABLED, tmpFilename, sizeof(tmpFilename));
408 if (err != SDLOG_OK) {
409 sdlog_start_failed("open flight recorder", err);
410 }
412#endif
413#if SDLOG_CONF
414 // once per configuration, a failure does not stop the logging
415 sdlog_conf_error = sdLogWriteFile(PPRZ_LOG_DIR, AIRCRAFT_CONF_NAME, aircraft_conf_gz, sizeof(aircraft_conf_gz));
416#endif
417 }
418
419 if (sdOk) {
420#if defined(SDLOG_BAT_ADC) && defined(SDLOG_BAT_CHAN) && SDLOG_ENABLE_LOWBAT_FLUSH
421 // Create Battery Survey Thread with event
422 chEvtObjectInit(&powerOutageSource);
423 chThdCreateStatic(wa_thd_bat_survey, sizeof(wa_thd_bat_survey),
424 NORMALPRIO + 2, thd_bat_survey, NULL);
425#endif
426
428 } else {
431 }
432
433 bool in_error = !sdOk;
434 while (true) {
435#ifdef SDLOG_LED
436 LED_TOGGLE(SDLOG_LED);
437#endif
438 // Blink faster if init has errors
439 chThdSleepMilliseconds(sdOk == true ? 1000 : 200);
440 const SdioError storage = sdLogGetStorageStatus();
441 if (storage != SDLOG_OK) {
442 if (!in_error) {
443 // a new error: keep its cause for the report
444 sdlog_error_cause = storage;
446 }
447 in_error = true;
449 sdOk = false;
450 } else {
451 // running again: a later error is not the start error anymore
452 in_error = false;
455 sdOk = true;
456 }
457
458#if HAL_USE_RTC && USE_GPS
459 static uint32_t timestamp = 0;
460 // FIXME this could be done somewhere else, like in sys_time
461 // we sync gps time to rtc every 5 seconds
462 if (chVTGetSystemTime() - timestamp > TIME_S2I(5)) {
463 timestamp = chVTGetSystemTime();
464 if (gps.tow != 0 && (sys_time.nb_sec - gps.last_msg_time) < 5) {
465 // Unix timestamp of the GPS epoch 1980-01-06 00:00:00 UTC
466 const uint32_t unixToGpsEpoch = 315964800;
467 struct tm time_tm;
468 time_t univTime = ((gps.week * 7 * 24 * 3600) + (gps.tow / 1000)) + unixToGpsEpoch - GPS_LEAP_SECONDS;
469 gmtime_r(&univTime, &time_tm);
470 // Chibios date struct
471 RTCDateTime date;
472 rtcConvertStructTmToDateTime(&time_tm, 0, &date);
473 rtcSetTime(&RTCD1, &date);
474 }
475 }
476#endif
477
478 }
479}
480
481
483 uint8_t ac_id = pprzlink_get_DL_INFO_MSG_UP_ac_id(buf);
484 if(ac_id != AC_ID && ac_id != 0xFF) {
485 return;
486 }
487 uint8_t fd = pprzlink_get_DL_INFO_MSG_UP_fd(buf);
489 uint8_t len = pprzlink_get_INFO_MSG_UP_msg_length(buf);
490 uint8_t* msg = (uint8_t*) pprzlink_get_DL_INFO_MSG_UP_msg(buf);
493 }
494}
495
arch independent ADC (Analog to Digital Converter) API
#define SDLOG_BAT_ADC
Definition board.h:533
#define SDLOG_USB_VBUS_PIN
Definition board.h:538
#define SDLOG_USB_VBUS_PORT
Definition board.h:537
#define SDLOG_BAT_CHAN
Definition board.h:534
static uint8_t status
#define LED_TOGGLE(i)
Definition led_hw.h:53
static THD_WORKING_AREA(wa_thd_spi1, SPI_THREAD_STACK_SIZE)
static struct uart_periph * dev
struct GpsState gps
global GPS state
Definition gps.c:74
Device independent GPS code (interface).
void chsnprintf(char *buffer, size_t size, const char *fmt,...)
Definition printf.c:377
void mcu_reboot(enum reboot_state_t reboot_state)
Reboot the MCU.
Definition mcu_arch.c:208
void mcu_energy_save(void)
Save energy for performing operations on shutdown Used for example to shutdown SD-card logging.
Definition mcu_arch.c:232
@ MCU_REBOOT_POWEROFF
Poweroff the device.
Definition mcu.h:45
uint8_t msg[10]
Buffer used for general comunication over SPI (out buffer).
static float p[2][2]
PRINT_CONFIG_MSG("USE_INS_NAV_INIT defaulting to TRUE")
arch independent LED (Light Emitting Diodes) API
static FILE * pprzLogFile
uint8_t step
Definition max7456.c:140
Arch independent mcu ( Micro Controller Unit ) utilities.
void preflight_error(struct preflight_result_t *result, const char *fmt,...)
Register a preflight error used inside the preflight checking functions.
void preflight_success(struct preflight_result_t *result, const char *fmt,...)
Register a preflight success used inside the preflight checking functions.
void preflight_warning(struct preflight_result_t *result, const char *fmt,...)
Register a preflight warning used inside the preflight checking functions.
void preflight_check_register(struct preflight_check_t *check, preflight_check_f func)
Register a preflight check and add it to the linked list.
Mini printf-like functionality.
#define IN_DMA_SECTION(var)
Definition ram_arch.h:87
SdioError sdLogFinish(void)
unmount filesystem
Definition sdLog.c:307
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
Definition sdLog.c:837
SdioError sdLogInit(uint32_t *freeSpaceInKo)
initialise sdLog
Definition sdLog.c:245
uint8_t sdLogGetFatfsResult(void)
Definition sdLog.c:227
SdioError sdLogWriteFile(const char *directoryName, const char *fileName, const uint8_t *data, const size_t len)
write a whole file, if it does not exist yet
Definition sdLog.c:882
struct _SdLogBuffer SdLogBuffer
Definition sdLog.h:127
int8_t FileDes
Definition sdLog.h:128
#define LOG_APPEND_TAG_AT_CLOSE_DISABLED
Definition sdLog.h:105
SdioError
Definition sdLog.h:111
@ SDLOG_OK
Definition sdLog.h:112
#define LOG_PREALLOCATION_DISABLED
Definition sdLog.h:103
static void sdlog_send(struct chibios_sdlog *p, long fd)
static int null_function(struct chibios_sdlog *p)
static enum @167037365353014241033154007315025330016177123070 chibios_sdlog_status
sdlog status
static volatile uint8_t sdlog_start_fatfs
static volatile SdioError sdlog_conf_error
Result of writing the configuration, reported to the GCS.
static void send_sdlog_status(struct transport_tx *trans, struct link_device *dev)
static volatile SdioError sdlog_start_error
#define GPS_LEAP_SECONDS
static int sdlog_check_free_space(struct chibios_sdlog *p, long *fd, uint16_t len)
void logger_log_msg_up(uint8_t *buf)
static volatile uint32_t sdlog_error_events
static void thd_startlog(void *arg)
#define SDLOG_AUTO_FLUSH_PERIOD
static void sdlog_report_status(struct transport_tx *trans, struct link_device *dev)
Tell the GCS in words why the logger does not run (no SD card, card full, ...): at the start and ever...
static const char PPRZ_LOG_DIR[]
static volatile SdioError sdlog_error_cause
void sdlog_chibios_finish(const bool flush)
static void sdlog_transmit(struct chibios_sdlog *p, long fd, uint8_t byte)
static void sdlog_start_failed(const char *step, SdioError err)
Keep why the start failed (the first failing step only).
#define SDLOG_START_DELAY
@ SDLOG_STOPPED
@ SDLOG_ERROR
@ SDLOG_RUNNING
static char NO_FILE_NAME[]
static const char PPRZ_LOG_NAME[]
#define SDLOG_CONTIGUOUS_STORAGE_MEM
void chibios_sdlog_init(struct chibios_sdlog *sdlog, FileDes *file)
init chibios_sdlog structure
bool sdOk
static char chibios_sdlog_filenames[68]
sdlog filenames
static uint8_t null_byte_function(struct chibios_sdlog *p)
static void sdlog_transmit_buffer(struct chibios_sdlog *p, long fd, uint8_t *data, uint16_t len)
void sdlog_chibios_init(void)
static const char * sdlog_start_step
#define DEST_INFO_MSG_PPRZLOG
FileDes * file
#define DEST_INFO_MSG_ALL
struct link_device device
Generic device interface.
static const char * sdlog_error_string(SdioError error)
Human readable cause of a logger error, for the messages to the GCS.
chibios_sdlog structure
SdioError sdLogWriteByte(const FileDes fd, const uint8_t value)
SdioError sdLogGetStorageStatus(void)
SdioError sdLogWriteRaw(const FileDes fd, const uint8_t *buffer, const size_t len)
size_t sdLogGetNbBytesWrittenToStorage(void)
int fd
Definition serial.c:26
volatile uint32_t nb_sec
full seconds since startup
Definition sys_time.h:72
int16_t register_periodic_telemetry(struct periodic_telemetry *_pt, uint16_t _id, telemetry_cb _cb)
Register a telemetry callback function.
Definition telemetry.c:51
Periodic telemetry system header (includes downlink utility and generated code).
#define DefaultPeriodic
Set default periodic telemetry.
Definition telemetry.h:66
Dynamic memory allocation based on TLSF library.
void usbStorageStartPolling(void)
Definition usbStorage.c:76
bool usbStorageIsItRunning(void)
Definition usbStorage.c:163
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.