diff --git a/.gitignore b/.gitignore index 1d6e7da3c1..736e180b72 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,13 @@ paparazzi.sublime-workspace # JetBrains (PyCharm, etc) IDE project files .idea +# QtCreator IDE +*.creator +*.creator.user +*.config +*.files +*.includes + # Vagrant VM files /.vagrant @@ -126,6 +133,7 @@ paparazzi.sublime-workspace /sw/logalizer/openlog2tlm /sw/logalizer/ahrs2fg /sw/logalizer/disp3d +/sw/logalizer/sdlogger_download # /sw/simulator/ /sw/simulator/gaia diff --git a/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml b/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml new file mode 100644 index 0000000000..0efc58da8c --- /dev/null +++ b/conf/airframes/TUDelft/airframes/walkera_genius_v2.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +
+ + + + + +
+ + +
+ + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ +
+ + + + + +
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/conf/airframes/TUDelft/conf.xml b/conf/airframes/TUDelft/conf.xml index 3f8491b59c..969e40342a 100644 --- a/conf/airframes/TUDelft/conf.xml +++ b/conf/airframes/TUDelft/conf.xml @@ -87,6 +87,17 @@ settings_modules="modules/geo_mag.xml modules/air_data.xml modules/video_thread.xml modules/video_rtp_stream.xml" gui_color="red" /> + + + + + Direct SPI SD Logger that saves pprzlog messages to SD Card. + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + SDLOGGER_DIRECT_SPI ?= spi2 + SDLOGGER_DIRECT_SPI_LOWER=$(shell echo $(SDLOGGER_DIRECT_SPI) | tr A-Z a-z) + SDLOGGER_DIRECT_SPI_UPPER=$(shell echo $(SDLOGGER_DIRECT_SPI) | tr a-z A-Z) + + SDLOGGER_DIRECT_SPI_SLAVE ?= spi_slave2 + SDLOGGER_DIRECT_SPI_SLAVE_LOWER=$(shell echo $(SDLOGGER_DIRECT_SPI_SLAVE) | tr A-Z a-z) + SDLOGGER_DIRECT_SPI_SLAVE_UPPER=$(shell echo $(SDLOGGER_DIRECT_SPI_SLAVE) | tr a-z A-Z) + + LOGGER_CONTROL_SWITCH ?= RADIO_AUX2 + + LOGGER_LED ?= none + ifneq ($(LOGGER_LED),none) + ap.CFLAGS += -DLOGGER_LED=$(LOGGER_LED) + endif + + ap.srcs += subsystems/datalink/downlink.c subsystems/datalink/pprzlog_transport.c + + + + + + + + + + + + + + +
+ diff --git a/conf/telemetry/rotorcraft_with_logger.xml b/conf/telemetry/rotorcraft_with_logger.xml new file mode 100644 index 0000000000..ae6ffd1307 --- /dev/null +++ b/conf/telemetry/rotorcraft_with_logger.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/airborne/modules/loggers/sdlogger_spi_direct.c b/sw/airborne/modules/loggers/sdlogger_spi_direct.c new file mode 100644 index 0000000000..c47f0db9a8 --- /dev/null +++ b/sw/airborne/modules/loggers/sdlogger_spi_direct.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) Bart Slinger + * + * This file is part of paparazzi + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ +/** + * @file "modules/loggers/sdlogger_spi_direct.c" + * @author Bart Slinger + * SPI SD Logger that saves pprzlog messages to SD Card. + * + * Developed using Test Driven Development. + * Test code available at: + * https://github.com/bartslinger/paparazzi-unittest + */ + +#define PERIODIC_C_LOGGER + +#include "modules/loggers/sdlogger_spi_direct.h" + +#ifdef LOGGER_LED +#define LOGGER_LED_ON LED_ON(LOGGER_LED); +#define LOGGER_LED_OFF LED_OFF(LOGGER_LED); +#else +#define LOGGER_LED_ON {} +#define LOGGER_LED_OFF {} +#endif + +#ifndef TELEMETRY_MODE_Main_empty +#warning You need to define a main telemetry mode named "empty" without any \ + messages in your config file in /conf/telemetry/. \ + \ + Add to your main telemetry process. +#endif + +#ifndef TELEMETRY_PROCESS_Logger +#error "You need to use a telemetry xml file with Logger process!" +#endif + +#ifndef DOWNLINK_DEVICE +#warning This module can only be used with uart downlink for now. +#endif + +struct sdlogger_spi_periph sdlogger_spi; + +/* Private function declarations */ +void sdlogger_spi_direct_block_to_uart(void); + +/** + * @brief sdlogger_spi_direct_init + * Initialize the logger and SD Card. + */ +void sdlogger_spi_direct_init(void) +{ + /* Initialize the SD Card */ + sdcard_spi_init(&sdcard1, &(SDLOGGER_SPI_LINK_DEVICE), + SDLOGGER_SPI_LINK_SLAVE_NUMBER); + + /* Set values in the struct to their defaults */ + sdlogger_spi.status = SDLogger_Initializing; + sdlogger_spi.next_available_address = 0; + sdlogger_spi.last_completed = 0; + sdlogger_spi.sdcard_buf_idx = 1; + + /* Fill internal buffer with zeros */ + for (uint8_t i = 0; i < sizeof(sdlogger_spi.buffer); i++) { + sdlogger_spi.buffer[i] = 0; + } + sdlogger_spi.idx = 0; + sdlogger_spi.log_len = 0; + sdlogger_spi.command = 0; + sdlogger_spi.download_id = 0; + sdlogger_spi.download_address = 0; + sdlogger_spi.download_length = 0; + + /* Set function pointers in link_device to the logger functions */ + sdlogger_spi.device.check_free_space = (check_free_space_t)sdlogger_spi_direct_check_free_space; + sdlogger_spi.device.put_byte = (put_byte_t)sdlogger_spi_direct_put_byte; + sdlogger_spi.device.send_message = (send_message_t)sdlogger_spi_direct_send_message; + sdlogger_spi.device.char_available = (char_available_t)sdlogger_spi_direct_char_available; + sdlogger_spi.device.get_byte = (get_byte_t)sdlogger_spi_direct_get_byte; + sdlogger_spi.device.periph = &sdlogger_spi; + + /* Init pprzlog_tp */ + pprzlog_transport_init(); +} + +/** + * @brief sdlogger_spi_direct_periodic + * Periodic function called at module frequency + */ +void sdlogger_spi_direct_periodic(void) +{ + sdcard_spi_periodic(&sdcard1); + + switch (sdlogger_spi.status) { + case SDLogger_Initializing: + if (sdcard1.status == SDCard_Idle) { + sdcard_spi_read_block(&sdcard1, 0x00002000, &sdlogger_spi_direct_index_received); + sdlogger_spi.status = SDLogger_RetreivingIndex; + } + break; + + case SDLogger_Ready: + if (radio_control.values[SDLOGGER_CONTROL_SWITCH] > 0 && + sdcard1.status == SDCard_Idle) { + LOGGER_LED_ON; + sdcard_spi_multiwrite_start(&sdcard1, sdlogger_spi.next_available_address); + sdlogger_spi.status = SDLogger_Logging; + } + break; + + case SDLogger_Logging: + /* This line is NOT unit-tested because it is an inline function */ + #if PERIODIC_TELEMETRY + periodic_telemetry_send_Logger(DefaultPeriodic, + &pprzlog_tp.trans_tx, + &sdlogger_spi.device); + #endif + /* Check if SD Card buffer is full and SD Card is ready for new data */ + if (sdlogger_spi.sdcard_buf_idx > 512 && + sdcard1.status == SDCard_MultiWriteIdle) { + sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written); + } + /* Check if switch is flipped to stop logging */ + if (radio_control.values[SDLOGGER_CONTROL_SWITCH] < 0) { + sdlogger_spi.status = SDLogger_LoggingFinalBlock; + } + break; + + case SDLogger_LoggingFinalBlock: + if (sdcard1.status == SDCard_MultiWriteIdle) { + if (sdlogger_spi.sdcard_buf_idx > 512) { + sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written); + } + else if (sdlogger_spi.sdcard_buf_idx > 1) { + /* Fill with trailing zero's */ + for (uint16_t i = sdlogger_spi.sdcard_buf_idx; i < (SD_BLOCK_SIZE+1); i++) { + sdcard1.output_buf[i] = 0x00; + } + sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written); + } + else if (sdlogger_spi.sdcard_buf_idx == 1) { + sdcard_spi_multiwrite_stop(&sdcard1); + sdlogger_spi.status = SDLogger_StoppedLogging; + } + } + break; + + case SDLogger_StoppedLogging: + if (sdcard1.status == SDCard_Idle) { + sdcard_spi_read_block(&sdcard1, 0x00002000, &sdlogger_spi_direct_index_received); + sdlogger_spi.status = SDLogger_GettingIndexForUpdate; + } + break; + + case SDLogger_UpdatingIndex: + if (sdcard1.status == SDCard_Idle) { + LOGGER_LED_OFF; + sdlogger_spi.status = SDLogger_Ready; + } + break; + + case SDLogger_Downloading: + if (sdcard1.status == SDCard_Idle) { + /* Put bytes to the buffer until all is written or buffer is full */ + for (uint16_t i = sdlogger_spi.sdcard_buf_idx; i < SD_BLOCK_SIZE; i++) { + if(uart_check_free_space(&(DOWNLINK_DEVICE), 1)) { + uart_put_byte(&(DOWNLINK_DEVICE), sdcard1.input_buf[i]); + } + else { + /* No free space left, abort for-loop */ + break; + } + sdlogger_spi.sdcard_buf_idx++; + } + /* Request next block if entire buffer was written to uart */ + if (sdlogger_spi.sdcard_buf_idx >= SD_BLOCK_SIZE) { + if (sdlogger_spi.download_length > 0) { + sdcard_spi_read_block(&sdcard1, sdlogger_spi.download_address, NULL); + sdlogger_spi.download_address++; + sdlogger_spi.download_length--; + } + else { + LOGGER_LED_OFF; + sdlogger_spi.status = SDLogger_Ready; + } + sdlogger_spi.sdcard_buf_idx = 0; + } + } + break; + + default: + break; + } +} + +void sdlogger_spi_direct_start(void) {} +void sdlogger_spi_direct_stop(void) {} + +/** + * @brief sdlogger_spi_direct_index_received + * Callback from SD Card when block at index location is received. + */ +void sdlogger_spi_direct_index_received(void) +{ + + switch (sdlogger_spi.status) { + case SDLogger_RetreivingIndex: + sdlogger_spi.next_available_address = 0x00004000; + sdlogger_spi.last_completed = 0; + /* Save data for later use + sdlogger_spi.next_available_address = (sdcard1.input_buf[0] << 24) | + (sdcard1.input_buf[1] << 16) | + (sdcard1.input_buf[2] << 8) | + (sdcard1.input_buf[3]); + sdlogger_spi.last_completed = sdcard1.input_buf[4]; + */ + + /* Ready to start logging */ + sdlogger_spi.status = SDLogger_Ready; + break; + + case SDLogger_GettingIndexForUpdate: + /* Copy input buffer to output buffer */ + for (uint16_t i = 0; i < SD_BLOCK_SIZE; i++) { + sdcard1.output_buf[i+6] = sdcard1.input_buf[i]; + } + + /* Increment last completed log */ + sdcard1.output_buf[4+6] = ++sdlogger_spi.last_completed; + /* Write log info at dedicated location */ + { + uint16_t log_idx_start = 5 + 6 + (sdlogger_spi.last_completed - 1) * 12; + + /* Set start address and length at location that belongs to the log nr */ + sdcard1.output_buf[log_idx_start+0] = sdlogger_spi.next_available_address >> 24; + sdcard1.output_buf[log_idx_start+1] = sdlogger_spi.next_available_address >> 16; + sdcard1.output_buf[log_idx_start+2] = sdlogger_spi.next_available_address >> 8; + sdcard1.output_buf[log_idx_start+3] = sdlogger_spi.next_available_address >> 0; + sdcard1.output_buf[log_idx_start+4] = sdlogger_spi.log_len >> 24; + sdcard1.output_buf[log_idx_start+5] = sdlogger_spi.log_len >> 16; + sdcard1.output_buf[log_idx_start+6] = sdlogger_spi.log_len >> 8; + sdcard1.output_buf[log_idx_start+7] = sdlogger_spi.log_len >> 0; + } + + /* Increment and update the next available address */ + sdlogger_spi.next_available_address += sdlogger_spi.log_len; + sdcard1.output_buf[0+6] = sdlogger_spi.next_available_address >> 24; + sdcard1.output_buf[1+6] = sdlogger_spi.next_available_address >> 16; + sdcard1.output_buf[2+6] = sdlogger_spi.next_available_address >> 8; + sdcard1.output_buf[3+6] = sdlogger_spi.next_available_address >> 0; + + sdcard_spi_write_block(&sdcard1, 0x00002000); + /* Reset log length */ + sdlogger_spi.log_len = 0; + sdlogger_spi.status = SDLogger_UpdatingIndex; + break; + + case SDLogger_GettingIndexForDownload: + { + uint16_t info_idx = 5 + (sdlogger_spi.download_id - 1) * 12; + sdlogger_spi.download_address = (sdcard1.input_buf[info_idx+0] << 24) | + (sdcard1.input_buf[info_idx+1] << 16) | + (sdcard1.input_buf[info_idx+2] << 8) | + (sdcard1.input_buf[info_idx+3] << 0); + sdlogger_spi.download_length = (sdcard1.input_buf[info_idx+4] << 24) | + (sdcard1.input_buf[info_idx+5] << 16) | + (sdcard1.input_buf[info_idx+6] << 8) | + (sdcard1.input_buf[info_idx+7] << 0); + if (sdlogger_spi.download_length > 0) { + /* Request the first block */ + sdcard_spi_read_block(&sdcard1, sdlogger_spi.download_address, NULL); + /* After each read block, incr address, decr length */ + sdlogger_spi.download_address++; + sdlogger_spi.download_length--; + sdlogger_spi.status = SDLogger_Downloading; + } + else { + LOGGER_LED_OFF; + sdlogger_spi.status = SDLogger_Ready; + } + sdlogger_spi.sdcard_buf_idx = 0; + } + break; + + default: + break; + } + +} + +/** + * @brief sdlogger_spi_direct_multiwrite_written + * Called when a multiwrite is complete. Data stored in the logger buffer is + * then moved to the SD Card buffer, which is now available again. + */ +void sdlogger_spi_direct_multiwrite_written(void) +{ + /* Increment log length */ + sdlogger_spi.log_len++; + + /* Copy data from logger buffer to SD Card buffer */ + for (uint8_t i = 0; i < sdlogger_spi.idx; i++) { + sdcard1.output_buf[i+1] = sdlogger_spi.buffer[i]; + } + /* Set sdcard buffer index to new value */ + sdlogger_spi.sdcard_buf_idx = sdlogger_spi.idx + 1; + /* And reset the logger buffer index */ + sdlogger_spi.idx = 0; +} + +void sdlogger_spi_direct_command(void) +{ + if (sdcard1.status == SDCard_Idle && sdlogger_spi.command > 0 && + sdlogger_spi.command < 43) { + LOGGER_LED_ON; + sdcard_spi_read_block(&sdcard1, 0x00002000, + &sdlogger_spi_direct_index_received); + sdlogger_spi.download_id = sdlogger_spi.command; + sdlogger_spi.status = SDLogger_GettingIndexForDownload; + } + else if (sdcard1.status == SDCard_Idle && sdlogger_spi.command == 255) { + telemetry_mode_Main = TELEMETRY_MODE_Main_empty; + LOGGER_LED_ON; + sdcard_spi_read_block(&sdcard1, 0x00002000, NULL); + sdlogger_spi.download_length = 0; + sdlogger_spi.sdcard_buf_idx = 0; + sdlogger_spi.status = SDLogger_Downloading; + } + /* Always reset command value back to zero */ + sdlogger_spi.command = 0; +} + +bool_t sdlogger_spi_direct_check_free_space(struct sdlogger_spi_periph *p, uint8_t len) +{ + if (p->status == SDLogger_Logging) { + /* Calculating free space in both buffers */ + if ( (513 - p->sdcard_buf_idx) + (SDLOGGER_BUFFER_SIZE - p->idx) >= len) { + return TRUE; + } + } + return FALSE; +} + +void sdlogger_spi_direct_put_byte(struct sdlogger_spi_periph *p, uint8_t data) +{ + /* SD Buffer full, write in logger buffer */ + if (p->sdcard_buf_idx > 512) { + if (p->idx < SDLOGGER_BUFFER_SIZE) { + p->buffer[p->idx++] = data; + } + /* else: data lost */ + } + /* Writing directly to SD Card buffer */ + else { + sdcard1.output_buf[p->sdcard_buf_idx++] = data; + + /* Flush buffer */ + if (p->sdcard_buf_idx > 512 && sdcard1.status == SDCard_MultiWriteIdle) { + sdcard_spi_multiwrite_next(&sdcard1, &sdlogger_spi_direct_multiwrite_written); + } + } +} + +void sdlogger_spi_direct_send_message(void *p) +{ + (void) p; +} + +int sdlogger_spi_direct_char_available(void *p){ + (void) p; + return 0; +} + +uint8_t sdlogger_spi_direct_get_byte(void *p) +{ + (void) p; + return 0; +} + + + diff --git a/sw/airborne/modules/loggers/sdlogger_spi_direct.h b/sw/airborne/modules/loggers/sdlogger_spi_direct.h new file mode 100644 index 0000000000..47bd7f62a9 --- /dev/null +++ b/sw/airborne/modules/loggers/sdlogger_spi_direct.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) Bart Slinger + * + * This file is part of paparazzi + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ +/** + * @file "modules/loggers/sdlogger_spi_direct.h" + * @author Bart Slinger + * SPI SD Logger that saves pprzlog messages to SD Card. + */ + +#ifndef SDLOGGER_SPI_H +#define SDLOGGER_SPI_H + +#define SDLOGGER_BUFFER_SIZE 128 + +#include "std.h" +#include "mcu_periph/link_device.h" +#include "subsystems/radio_control.h" +#include "subsystems/datalink/pprzlog_transport.h" +#include "subsystems/datalink/telemetry.h" +#include "peripherals/sdcard_spi.h" +#include "mcu_periph/uart.h" +#include "led.h" + +#include "generated/periodic_telemetry.h" + +enum SDLoggerStatus { + SDLogger_UnInit, + SDLogger_Error, + SDLogger_Initializing, + SDLogger_RetreivingIndex, + SDLogger_Ready, + SDLogger_Logging, + SDLogger_LoggingFinalBlock, + SDLogger_StoppedLogging, + SDLogger_GettingIndexForUpdate, + SDLogger_UpdatingIndex, + SDLogger_GettingIndexForDownload, + SDLogger_Downloading +}; + +struct sdlogger_spi_periph{ + enum SDLoggerStatus status; + uint32_t next_available_address; + uint8_t last_completed; + uint16_t sdcard_buf_idx; + uint8_t buffer[SDLOGGER_BUFFER_SIZE]; + uint8_t idx; + uint32_t log_len; + uint8_t command; + uint8_t download_id; + uint32_t download_address; + uint32_t download_length; + struct link_device device; +}; + +extern struct sdlogger_spi_periph sdlogger_spi; + +extern void sdlogger_spi_direct_init(void); +extern void sdlogger_spi_direct_periodic(void); +extern void sdlogger_spi_direct_start(void); +extern void sdlogger_spi_direct_stop(void); + +extern void sdlogger_spi_direct_index_received(void); +extern void sdlogger_spi_direct_multiwrite_written(void); +extern void sdlogger_spi_direct_command(void); + +extern bool_t sdlogger_spi_direct_check_free_space(struct sdlogger_spi_periph *p, uint8_t len); +extern void sdlogger_spi_direct_put_byte(struct sdlogger_spi_periph *p, uint8_t data); +extern void sdlogger_spi_direct_send_message(void *p); +extern int sdlogger_spi_direct_char_available(void *p); +extern uint8_t sdlogger_spi_direct_get_byte(void *p); + +#endif + diff --git a/sw/airborne/peripherals/sdcard_spi.c b/sw/airborne/peripherals/sdcard_spi.c index 90603b1993..88f7268d84 100644 --- a/sw/airborne/peripherals/sdcard_spi.c +++ b/sw/airborne/peripherals/sdcard_spi.c @@ -35,6 +35,11 @@ * The following resource was used as implementation reference: http://elm-chan.org/docs/mmc/mmc_e.html * The initialization procedure is implemented according to the following diagram. Only the branches for SD ver.2 are currently included. * \image html airborne/sdinit.png + * + * Developed using Test Driven Development. + * Test code available at: + * https://github.com/bartslinger/paparazzi-unittest + * * @todo CRC checksums are not implemented. Fake values of 0xFF are used and they are ignored by the card. */ @@ -380,8 +385,8 @@ void sdcard_spi_spicallback(struct spi_transaction *t) /* Data block received in buffer, process data */ case SDCard_ReadingDataBlock: sdcard1.status = SDCard_Idle; - if (sdcard1.read_callback != NULL) { - sdcard1.read_callback(); + if (sdcard1.external_callback != NULL) { + sdcard1.external_callback(); } break; @@ -408,6 +413,9 @@ void sdcard_spi_spicallback(struct spi_transaction *t) case SDCard_MultiWriteWriting: if ((sdcard1.input_buf[SD_BLOCK_SIZE + 3] & 0x0F) == 0x05 /* Data accepted */) { sdcard1.status = SDCard_MultiWriteBusy; + if(sdcard1.external_callback != NULL) { + sdcard1.external_callback(); + } } else { sdcard1.status = SDCard_Error; } @@ -576,7 +584,7 @@ void sdcard_spi_read_block(struct SDCard *sdcard, uint32_t addr, SDCardCallback } /* Set function to be called after the read action has finished. */ - sdcard->read_callback = callback; + sdcard->external_callback = callback; /* Send command 17 (read block) to the SDCard */ sdcard_spi_send_cmd(sdcard, 17, addr); @@ -611,7 +619,7 @@ void sdcard_spi_multiwrite_start(struct SDCard *sdcard, uint32_t addr) * Use only after sdcard_spi_multiwrite_start(). * @param sdcard Pointer to the SDCard. */ -void sdcard_spi_multiwrite_next(struct SDCard *sdcard) +void sdcard_spi_multiwrite_next(struct SDCard *sdcard, SDCardCallback callback) { /* Can only write next block if card is in multiwrite mode and not currently busy */ if (sdcard->status != SDCard_MultiWriteIdle) { @@ -627,6 +635,7 @@ void sdcard_spi_multiwrite_next(struct SDCard *sdcard) /* Set the callback */ sdcard->spi_t.after_cb = &sdcard_spi_spicallback; + sdcard->external_callback = callback; /* Submit the spi transaction */ spi_submit(sdcard->spi_p, &sdcard->spi_t); diff --git a/sw/airborne/peripherals/sdcard_spi.h b/sw/airborne/peripherals/sdcard_spi.h index 8b9ae5ceb6..c8a976083e 100644 --- a/sw/airborne/peripherals/sdcard_spi.h +++ b/sw/airborne/peripherals/sdcard_spi.h @@ -96,7 +96,7 @@ struct SDCard { uint8_t response_counter; /**< Response counter used at various locations */ uint32_t timeout_counter; /**< Timeout counter used for initialization checks with ACMD41 */ enum SDCardType card_type; /**< Type of SDCard */ - SDCardCallback read_callback; /**< Callback to call when read operation finishes */ + SDCardCallback external_callback; /**< Callback to call when external operation finishes */ }; extern struct SDCard sdcard1; @@ -107,7 +107,7 @@ extern void sdcard_spi_periodic(struct SDCard *sdcard); extern void sdcard_spi_write_block(struct SDCard *sdcard, uint32_t addr); extern void sdcard_spi_read_block(struct SDCard *sdcard, uint32_t addr, SDCardCallback callback); extern void sdcard_spi_multiwrite_start(struct SDCard *sdcard, uint32_t addr); -extern void sdcard_spi_multiwrite_next(struct SDCard *sdcard); +extern void sdcard_spi_multiwrite_next(struct SDCard *sdcard, SDCardCallback callback); extern void sdcard_spi_multiwrite_stop(struct SDCard *sdcard); #endif // SDCARD_H_ diff --git a/sw/logalizer/Makefile b/sw/logalizer/Makefile index 94dd243465..57cd3f890e 100644 --- a/sw/logalizer/Makefile +++ b/sw/logalizer/Makefile @@ -30,7 +30,7 @@ LINKPKG = $(PKG) -linkpkg -dllpath-pkg pprz XPKG = -package pprz.xlib XLINKPKG = $(XPKG) -linkpkg -dllpath-pkg pprz.xlib -all: play plotter logplotter sd2log plotprofile openlog2tlm +all: play plotter logplotter sd2log plotprofile openlog2tlm sdlogger_download play : log_file.cmo play_core.cmo play.cmo $(LIBPPRZCMA) @echo OL $@ @@ -52,6 +52,9 @@ sd2log : sd2log.cmo $(LIBPPRZCMA) @echo OL $@ $(Q)$(OCAMLC) $(INCLUDES) -o $@ $(LINKPKG) $^ +sdlogger_download: sdlogger_download.c + @echo CC $@ + $(Q)$(CC) $(CFLAGS) -std=gnu99 -Wno-unused-result -o $@ $^ # Target for bytecode executable (if ocamlopt is not available) # plot : log_file.cmo gtk_export.cmo export.cmo plot.cmo @@ -135,7 +138,7 @@ ctrlstick: ctrlstick.c clean: - $(Q)rm -f *.opt *.out *~ core *.o *.bak .depend *.cm* play ahrs2fg logplotter plotter gtk_export.ml openlog2tlm disp3d plotprofile tmclient ffjoystick ctrlstick sd2log + $(Q)rm -f *.opt *.out *~ core *.o *.bak .depend *.cm* play ahrs2fg logplotter plotter gtk_export.ml openlog2tlm disp3d plotprofile tmclient ffjoystick ctrlstick sd2log sdlogger_download .PHONY: all clean diff --git a/sw/logalizer/sdlogger_download.c b/sw/logalizer/sdlogger_download.c new file mode 100644 index 0000000000..1442f2511b --- /dev/null +++ b/sw/logalizer/sdlogger_download.c @@ -0,0 +1,655 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PPRZ_STX 0x99 + +/* File pointer for temp.tlm */ +FILE *fp; + +/* File pointer for serial link */ +int fd = 0; + +/* Paths */ +char sd2log[256]; +char pycommand[256]; + +/* Setting associated with sdlogger_spi.command */ +unsigned char setting = 0; + +/* Global variable to check need for input */ +static volatile bool need_input = true; + +/* Global states enum and definition */ +enum global_states { + Idle, + WaitingForIndexRequestConfirmation, + ReadingIndexBlock, + GotIndex, + Downloading +}; +enum global_states global_state = Idle; + +/* Counter for reading bytes in the index block */ +int index_cnt = 0; + +/* PPRZ message parser states */ +enum normal_parser_states { + SearchingPPRZ_STX, + ParsingLength, + ParsingSenderId, + ParsingMsgId, + ParsingMsgPayload, + CheckingCRCA, + CheckingCRCB +}; + +struct normal_parser_t { + enum normal_parser_states state; + unsigned char length; + int counter; + unsigned char sender_id; + unsigned char msg_id; + unsigned char payload[100]; + unsigned char crc_a; + unsigned char crc_b; +}; + +struct normal_parser_t parser; + +/* Struct and definition of log info (read from index block) */ +struct log_info_t { + uint32_t address; + uint32_t length; + unsigned int reserved; +}; + +struct log_index_t { + uint32_t next_available_address; + uint8_t last_completed_log; + struct log_info_t logs[42]; +} __attribute__((packed)); + +/* Log index initialization */ +bool index_available = false; +struct log_index_t log_index; + +/* Identifier of the log that is currently being downloaded */ +uint8_t current_download = 0; + + +/* Function definitions */ +void process_command(char *command); + +void new_logfile(void) +{ + fp = fopen("temp.tlm", "w+"); +} + +void close_logfile(void) +{ + fclose(fp); +} + + +/* For ctrl+c */ +static volatile int keep_running = 1; +static volatile int searching = 1; +void intHandler(int dummy) { + printf("\n"); + keep_running = 0; +} + +unsigned int get_baud(unsigned int baud_rate) +{ + unsigned int BAUD; + switch (baud_rate) + { + case 921600: + BAUD = B921600; + break; + case 460800: + BAUD = B460800; + break; + case 230400: + BAUD = B230400; + break; + case 115200: + BAUD = B115200; + break; + default: + printf("Baud rate not recognized, using default B57600\n"); + case 57600: + BAUD = B57600; + break; + case 38400: + BAUD = B38400; + break; + case 19200: + BAUD = B19200; + break; + case 9600: + BAUD = B9600; + break; + case 4800: + BAUD = B4800; + break; + case 2400: + BAUD = B2400; + break; + case 1800: + BAUD = B1800; + break; + case 1200: + BAUD = B1200; + break; + case 600: + BAUD = B600; + break; + case 300: + BAUD = B300; + break; + case 200: + BAUD = B200; + break; + case 150: + BAUD = B150; + break; + case 134: + BAUD = B134; + break; + case 110: + BAUD = B110; + break; + case 75: + BAUD = B75; + break; + case 50: + BAUD = B50; + break; + } //end of switch baud_rate + return BAUD; +} + +int serial_init(char *port_name, unsigned int baud) +{ + struct termios orig_termios, cur_termios; + + int br = get_baud(baud); + + fd = open(port_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd == -1) { + printf("opening modem serial device : fd < 0\n"); + return -1; + } + + if (tcgetattr(fd, &orig_termios)) { + printf("getting modem serial device attr\n"); + return -2; + } + cur_termios = orig_termios; + + /* input modes - turn off input processing */ + cur_termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR + | ICRNL | IXON | IXANY | IXOFF | IMAXBEL); + /* pas IGNCR sinon il vire les 0x0D */ + cur_termios.c_iflag |= BRKINT; + + /* output_flags - turn off output processing */ + cur_termios.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR | ONLRET); + + /* control modes */ + cur_termios.c_cflag &= ~(CSIZE | CSTOPB | CREAD | PARENB | PARODD | HUPCL | CLOCAL); + cur_termios.c_cflag |= CREAD | CS8 | CLOCAL; + cur_termios.c_cflag &= ~(CRTSCTS); + + /* local modes */ + cur_termios.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO | FLUSHO | PENDIN); + cur_termios.c_lflag |= NOFLSH; + + if (cfsetspeed(&cur_termios, br)) { + printf("setting modem serial device speed\n"); + return -3; + } + + if (tcsetattr(fd, TCSADRAIN, &cur_termios)) { + printf("setting modem serial device attr\n"); + return -4; + } + + return 0; +} + +void open_port(const char* device) { + fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + fprintf(stderr, "open_port: unable to open device %s - ", device); + perror(NULL); + exit(EXIT_FAILURE); + } + // setup connection options + struct termios options; + + // get the current options + tcgetattr(fd, &options); + + // set local mode, enable receiver, set comm. options: + // 8 data bits, no parity, 57600 Baud AND 2 STOP BITS @#! + options.c_cflag = CLOCAL | CREAD | CS8 | B57600;// | CSTOPB; + + // write options back to port + tcsetattr(fd, TCSANOW, &options); +} + +void close_port(void) +{ + close(fd); +} + +void write_command(float value) +{ + unsigned char msg[12]; + unsigned char crc_a = 0; + unsigned char crc_b = 0; + unsigned char length = 12; + unsigned char *pc; + pc = (unsigned char*)&value; + + msg[0] = PPRZ_STX; + msg[1] = length; + crc_a += length; crc_b += crc_a; + + msg[2] = 0; // Sender ID + crc_a += 0; crc_b += crc_a; + + msg[3] = 4; // MSG_ID = SETTING + crc_a += 4; crc_b += crc_a; + + msg[4] = setting; // setting index + crc_a += setting; crc_b += crc_a; + + msg[5] = 114; // AC_ID + crc_a += 114; crc_b += crc_a; + + msg[6] = pc[0]; // value + crc_a += msg[6]; crc_b += crc_a; + msg[7] = pc[1]; // value + crc_a += msg[7]; crc_b += crc_a; + msg[8] = pc[2]; // value + crc_a += msg[8]; crc_b += crc_a; + msg[9] = pc[3]; // value + crc_a += msg[9]; crc_b += crc_a; + msg[10] = crc_a; + msg[11] = crc_b; + + write(fd, msg, length); +} + +/* + * PPRZ-message: ABCxxxxxxxDE + A PPRZ_STX (0x99) + B LENGTH (A->E) + C PPRZ_DATA + 0 SENDER_ID + 1 MSG_ID + 2 MSG_PAYLOAD + . DATA (messages.xml) + D PPRZ_CHECKSUM_A (sum[B->C]) + E PPRZ_CHECKSUM_B (sum[ck_a]) +*/ +void parse_single_byte(unsigned char byte) +{ + switch (parser.state) { + printf("GOT BYTE %u\n", byte); + case SearchingPPRZ_STX: + if (byte == PPRZ_STX) { + //printf("Got PPRZ_STX\n"); + parser.crc_a = 0; + parser.crc_b = 0; + parser.counter = 1; + parser.state = ParsingLength; + } + break; + + case ParsingLength: + parser.length = byte; + parser.crc_a += byte; + parser.crc_b += parser.crc_a; + parser.counter++; + parser.state = ParsingSenderId; + break; + + case ParsingSenderId: + parser.sender_id = byte; + parser.crc_a += byte; + parser.crc_b += parser.crc_a; + parser.counter++; + parser.state = ParsingMsgId; + break; + + case ParsingMsgId: + parser.msg_id = byte; + parser.crc_a += byte; + parser.crc_b += parser.crc_a; + parser.counter++; + parser.state = ParsingMsgPayload; + break; + + case ParsingMsgPayload: + parser.payload[parser.counter-4] = byte; + parser.crc_a += byte; + parser.crc_b += parser.crc_a; + parser.counter++; + if (parser.counter == parser.length - 2) { + parser.state = CheckingCRCA; + } + break; + + case CheckingCRCA: + //printf("CRCA: %d vs %d\n", byte, parser.crc_a); + if (byte == parser.crc_a) { + parser.state = CheckingCRCB; + } + else { + parser.state = SearchingPPRZ_STX; + } + break; + + case CheckingCRCB: + //printf("CRCB: %d vs %d\n", byte, parser.crc_b); + if (byte == parser.crc_b && parser.msg_id == 31) { + /*printf("MSG ID: %d \t" + "SENDER_ID: %d\t" + "LEN: %d\t" + "SETTING: %d\n", + parser.msg_id, + parser.sender_id, + parser.length, + parser.payload[0]);*/ + //printf("Request confirmed\n"); + + /* Check what to do next if the command was received */ + if (global_state == WaitingForIndexRequestConfirmation + && parser.payload[0] == 60) { + global_state = ReadingIndexBlock; + index_cnt = 0; + } + if (global_state == GotIndex + && parser.payload[0] == 60) { + global_state = Downloading; + new_logfile(); + index_cnt = 0; + } + } + parser.state = SearchingPPRZ_STX; + break; + + default: + /* Should never get here */ + break; + } + +} + +void parse_index_byte(unsigned char byte) +{ + unsigned char *pc; + pc = (unsigned char*)&log_index; + pc[index_cnt] = byte; + index_cnt++; + if (index_cnt >= 512) { + /* Plot results: */ + printf("\n\nNumber of Logs: %u\n", log_index.last_completed_log); + printf("Log number \t Address \t Length [bytes]\n"); + for (uint8_t i = 0; i < log_index.last_completed_log; i++) { + printf("%u \t\t 0x%08x \t %u \n", + i+1, + be32toh(log_index.logs[i].address), + be32toh(log_index.logs[i].length)*512); + } + searching = false; + global_state = GotIndex; + } +} + +void parse_download_byte(unsigned char byte) +{ + static long long dcnt = 0; + dcnt++; + //printf("Got \t (%d) \t 0x%02x\n", dcnt, byte); + fputc(byte, fp); + + /* Show progress */ + if (dcnt % 512 == 0) { + printf("."); + fflush(stdout); + } + + /* Show progress every 10% */ + if ( dcnt % (be32toh(log_index.logs[current_download-1].length)*512/10) == 0 ){ + int percent = (dcnt+1)*100/(be32toh(log_index.logs[current_download-1].length)*512); + printf("%i%%", percent); + fflush(stdout); + } + + if (dcnt >= be32toh(log_index.logs[current_download-1].length)*512){ + /* Download finished */ + printf("\nDownloaded log %u\n", current_download); + /* Close temp file */ + close_logfile(); + /* Process data into log format */ + system(sd2log); + /* Remove file */ + remove("temp.tlm"); + /* Reset and get ready for next command */ + need_input = true; + dcnt = 0; + } +} + +void parse_bytes(const unsigned char *buff, int len) +{ + /* For each received byte call the appropriate function */ + for (int i = 0; i < len; i++) { + switch (global_state) { + + /* Parse as pprz message */ + case Idle: + case GotIndex: + case WaitingForIndexRequestConfirmation: + parse_single_byte(buff[i]); + break; + + /* Read as index-block format */ + case ReadingIndexBlock: + parse_index_byte(buff[i]); + break; + + /* Download raw log data */ + case Downloading: + parse_download_byte(buff[i]); + break; + + default: + break; + } + } + return; +} + +bool in_download_range(int download_id) +{ + if (download_id <= log_index.last_completed_log && + download_id > 0) { + return true; + } else { + return false; + } +} + +void process_command(char *command) +{ + char *token; + char *search = " "; + strtok(command, "\n"); + + token = strtok(command, search); + + if (strcmp(token, "download") == 0) { + token = strtok(NULL, search); + int download_id = atoi(token); + if (in_download_range(download_id)) { + printf("Downloading %i", download_id); + write_command(download_id); + current_download = download_id; + global_state = GotIndex; + } + else { + printf("Log ID %i outside available range\n", download_id); + need_input = true; + } + } + else if (strcmp(token, "exit") == 0) { + keep_running = false; + } + else { + printf("Possible commands:\n" + " download \n" + " help\n" + " exit\n"); + need_input = true; + } +} + +/* Main function */ +int main ( int argc, char** argv) +{ + + /* Default settings for serial connection */ + char *port = "/dev/ttyUSB0"; + int baud = 57600; + + /* Serial read buffer */ + int bytes; + unsigned char buff[32]; + + /* Aircraft ID */ + int ac_id = 114; + + /* Parse arguments */ + int c; + while ((c = getopt (argc, argv, "a:p:b:h")) != -1) { + switch (c) + { + case 'a': + ac_id = atoi(optarg); + break; + case 'p': + port = optarg; + break; + case 'b': + baud = atoi(optarg); + break; + case 'h': + default: + printf("usage: sdlogger_download [options]\n" + " options:\n" + " -a \tAircraft ID\n" + " -p \tPort (default: /dev/ttyUSB0).\n" + " -b \tBaudrate (default: 57600).\n" + " -h \tHelp, shows this message.\n"); + exit(0); + } + } + + /* Obtain sd2log directory */ + char *pprz_home; + pprz_home = getenv( "PAPARAZZI_HOME" ); + strcat(sd2log, pprz_home); + strcat(sd2log, "/sw/logalizer/sd2log temp.tlm"); + + /* Get the setting ID with a python script */ + /* TODO: would be nicer to have a C xml parser */ + FILE *in = NULL; + strcat(pycommand, pprz_home); + strcat(pycommand, "/sw/logalizer/sdlogger_get_setting_id.py %u sdlogger_spi.command"); + char new_command[256]; + sprintf(new_command, pycommand, ac_id, "ab"); + strcpy(pycommand, new_command); + + char returnvalue[128]; + in = popen(pycommand, "r"); + fgets(returnvalue, 128, in); + setting = atoi(returnvalue); + pclose(in); + if (setting == 0) { + printf("Aborting: %s\n", returnvalue); + exit(0); + } + + /* Enable Ctrl+C */ + signal(SIGINT, intHandler); + + /* Open serial port */ + printf("Opening port %s with baudrate B%i\n", port, baud); + if(serial_init(port, baud) < 0){ + return -1; + } + + /* Keep polling for logger */ + printf("Searching for logger.."); + time_t counter = time(0); + time_t timeout = 1; // every second + while (keep_running && searching) { + if (time(0) - counter >= timeout) { + printf("."); + fflush(stdout); + write_command(255); + global_state = WaitingForIndexRequestConfirmation; + counter = time(0); + } + else { + bytes = read(fd, (unsigned char*) buff, 32); + parse_bytes(buff, bytes); + usleep(5000); + } + } + + char command[128]; + strcpy(command, "help"); + /* Show available commands */ + printf("\n"); + process_command(command); + + + while (keep_running) { + if (need_input) { + need_input = false; + /* Test user input */ + printf("Command> "); + fgets (command, 128, stdin); + process_command(command); + } + else { + bytes = read(fd, (unsigned char*) buff, 32); + parse_bytes(buff, bytes); + } + } + + printf("Closing app\n"); + + /* Close serial port */ + close_port(); + + return 0; +} + diff --git a/sw/logalizer/sdlogger_get_setting_id.py b/sw/logalizer/sdlogger_get_setting_id.py new file mode 100755 index 0000000000..e85883726a --- /dev/null +++ b/sw/logalizer/sdlogger_get_setting_id.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import sys +from os import path, getenv + +# if PAPARAZZI_SRC not set, then assume the tree containing this +# file is a reasonable substitute +PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../'))) +sys.path.append(PPRZ_SRC + "/sw/lib/python") +from settings_xml_parse import PaparazziACSettings + +if __name__ == '__main__': + ac_id = int(sys.argv[1]) + command_name = sys.argv[2] + settings = PaparazziACSettings(ac_id) + print settings.name_lookup[command_name].index