From 8a0b8b8252aa98bd089ada983c4517141a559e77 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 16 Oct 2017 23:14:46 +0200 Subject: [PATCH] Implemented UART/I2C switch for GPS Implemented LOWBATT1 and LOWBATT2 Switch off camera at JPEG validation process Moved JPEG validation section Added GPS communication error message Changed Watchdog behavior (1sec delay inserted) Tidy up --- tracker/software/drivers/ov5640.c | 79 +-------- tracker/software/drivers/ov5640.h | 2 +- tracker/software/drivers/si4464.c | 4 - tracker/software/drivers/si4464.h | 1 - tracker/software/drivers/ublox.c | 230 ++++++++++++------------- tracker/software/drivers/ublox.h | 4 + tracker/software/main.c | 19 ++ tracker/software/mcuconf.h | 4 + tracker/software/protocols/aprs/aprs.c | 58 ++++--- tracker/software/radio.c | 2 +- tracker/software/threads/image.c | 98 +++++++++-- tracker/software/threads/tracking.c | 122 ++++++------- tracker/software/threads/tracking.h | 8 +- tracker/software/threads/watchdog.c | 15 +- 14 files changed, 347 insertions(+), 299 deletions(-) diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 47694a6..24fa22e 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -9,7 +9,6 @@ #include "pi2c.h" #include "board.h" #include "debug.h" -#include "ssdv.h" #include "padc.h" #include @@ -713,7 +712,7 @@ void set48MHz(void) /** * Reduce AHB clock back to the old speed which has been saved by set48MHz() */ -void set6MHz(void) +void setSlowFreq(void) { uint32_t new = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | oldSpeed; RCC->CFGR = new; @@ -724,59 +723,6 @@ void set6MHz(void) while(FLASH->ACR != new); } -/** - * Analyzes the image for JPEG errors. Returns true if the image is error free. - */ -static bool analyze_image(uint8_t *image, uint32_t image_len) -{ - #if !OV5640_USE_DMA_DBM - if(image_len >= 65535) - { - TRACE_ERROR("CAM > Camera has %d bytes allocated but DMA DBM not activated", image_len); - TRACE_ERROR("CAM > DMA can only use 65535 bytes only"); - image_len = 65535; - } - #endif - - ssdv_t ssdv; - uint8_t pkt[SSDV_PKT_SIZE]; - uint8_t *b; - uint32_t bi = 0; - uint16_t i = 0; - uint8_t c = SSDV_OK; - - ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, "", 0, 7); - ssdv_enc_set_buffer(&ssdv, pkt); - - while(true) // FIXME: I get caught in these loops occasionally and never return - { - while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME) - { - b = &image[bi]; - uint8_t r = bi < image_len-128 ? 128 : image_len - bi; - bi += r; - if(r <= 0) - { - TRACE_ERROR("CAM > Error in image (Premature end of file %d)", i); - return false; - } - ssdv_enc_feed(&ssdv, b, r); - } - - if(c == SSDV_EOI) // End of image - return true; - - if(c != SSDV_OK) // Error in JPEG image - { - TRACE_ERROR("CAM > Error in image (ssdv_enc_get_packet failed: %d %d)", c, i); - return false; - } - - i++; - chThdSleepMilliseconds(5); - } -} - /** * Captures an image from the camera. * @buffer Buffer in which the image can be sampled @@ -787,16 +733,14 @@ static bool analyze_image(uint8_t *image, uint32_t image_len) * that could lead to different resolutions on different method calls. * The method returns the size of the image. */ -uint32_t OV5640_Snapshot2RAM(uint8_t* buffer, uint32_t size, resolution_t res, bool enableJpegValidation) +uint32_t OV5640_Snapshot2RAM(uint8_t* buffer, uint32_t size, resolution_t res) { - uint8_t cntr = 10; + uint8_t cntr = 5; bool status; - bool jpegValid; uint32_t size_sampled; // Set resoultion - if(res == RES_MAX) - { + if(res == RES_MAX) { OV5640_SetResolution(RES_UXGA); // FIXME: We actually have to choose the resolution which fits in the memory } else { OV5640_SetResolution(res); @@ -818,18 +762,7 @@ uint32_t OV5640_Snapshot2RAM(uint8_t* buffer, uint32_t size, resolution_t res, b size_sampled--; TRACE_INFO("CAM > Image size: %d bytes", size_sampled); - - // Validate JPEG image - if(enableJpegValidation) - { - TRACE_INFO("CAM > Validate integrity of JPEG"); - jpegValid = analyze_image(buffer, size); - TRACE_INFO("CAM > JPEG image %s", jpegValid ? "valid" : "invalid"); - } else { - jpegValid = true; - } - - } while((!jpegValid || !status) && cntr--); + } while(!status && cntr--); return size_sampled; } @@ -1003,7 +936,7 @@ CH_IRQ_HANDLER(Vector5C) { vsync = true; } else { // Reduce AHB clock to 6 MHz - set6MHz(); + setSlowFreq(); /* VSYNC leading with vsync true. * This means end of capture for the frame. diff --git a/tracker/software/drivers/ov5640.h b/tracker/software/drivers/ov5640.h index 4d24f28..b2eef26 100644 --- a/tracker/software/drivers/ov5640.h +++ b/tracker/software/drivers/ov5640.h @@ -13,7 +13,7 @@ #define OV5640_USE_DMA_DBM TRUE -uint32_t OV5640_Snapshot2RAM(uint8_t* buffer, uint32_t size, resolution_t resolution, bool enableJpegValidation); +uint32_t OV5640_Snapshot2RAM(uint8_t* buffer, uint32_t size, resolution_t resolution); bool OV5640_Capture(uint8_t* buffer, uint32_t size); void OV5640_InitGPIO(void); void OV5640_TransmitConfig(void); diff --git a/tracker/software/drivers/si4464.c b/tracker/software/drivers/si4464.c index bd4676f..32b54cf 100644 --- a/tracker/software/drivers/si4464.c +++ b/tracker/software/drivers/si4464.c @@ -359,7 +359,3 @@ int8_t Si4464_getTemperature(void) { return (899*adc)/4096 - 293; } -bool isRadioInitialized(void) { - return initialized; -} - diff --git a/tracker/software/drivers/si4464.h b/tracker/software/drivers/si4464.h index 2644bb1..d8a25a2 100644 --- a/tracker/software/drivers/si4464.h +++ b/tracker/software/drivers/si4464.h @@ -40,7 +40,6 @@ void Si4464_writeFIFO(uint8_t *msg, uint8_t size); uint8_t Si4464_freeFIFO(void); uint8_t Si4464_getState(void); int8_t Si4464_getTemperature(void); -bool isRadioInitialized(void); #endif diff --git a/tracker/software/drivers/ublox.c b/tracker/software/drivers/ublox.c index e50f89e..46d074c 100644 --- a/tracker/software/drivers/ublox.c +++ b/tracker/software/drivers/ublox.c @@ -10,6 +10,7 @@ #include "debug.h" #include "config.h" +#if defined(UBLOX_USE_UART) // Serial driver configuration for GPS const SerialConfig gps_config = { @@ -18,39 +19,48 @@ const SerialConfig gps_config = 0, // CR2 register 0 // CR3 register }; +#endif -/* - * gps_transmit_string - * - * transmits a command to the GPS - */ +/** + * Transmits a string of bytes to the GPS + */ void gps_transmit_string(uint8_t *cmd, uint8_t length) { + #if defined(UBLOX_USE_I2C) I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length); + #elif defined(UBLOX_USE_UART) + sdWrite(&SD5, cmd, length); + #endif } +/** + * Receives a single byte from the GPS. Returns 0x00 is there is no byte available + */ uint8_t gps_receive_byte(void) { - uint8_t val; - I2C_read8(UBLOX_MAX_ADDRESS, 0xFF, &val); + uint8_t val = 0x00; + + #if defined(UBLOX_USE_I2C) + uint16_t len; + I2C_read16(UBLOX_MAX_ADDRESS, 0xFD, &len); + if(len) { + I2C_read8(UBLOX_MAX_ADDRESS, 0xFF, &val); + #elif defined(UBLOX_USE_UART) + val = sdGetTimeout(&SD5, TIME_IMMEDIATE); + if(val == 0xFF) val = 0x00; + #endif + return val; } -uint16_t gps_bytes_avail(void) -{ - uint16_t val; - I2C_read16(UBLOX_MAX_ADDRESS, 0xFD, &val); - return val; -} - -/* - * gps_receive_ack - * - * waits for transmission of an ACK/NAK message from the GPS. - * - * returns 1 if ACK was received, 0 if NAK was received - * - */ +/** + * gps_receive_ack + * + * waits for transmission of an ACK/NAK message from the GPS. + * + * returns 1 if ACK was received, 0 if NAK was received + * + */ uint8_t gps_receive_ack(uint8_t class_id, uint8_t msg_id, uint16_t timeout) { int match_count = 0; int msg_ack = 0; @@ -67,9 +77,8 @@ uint8_t gps_receive_ack(uint8_t class_id, uint8_t msg_id, uint16_t timeout) { while(sTimeout >= chVTGetSystemTimeX()) { // Receive one byte - if(gps_bytes_avail()) { - rx_byte = gps_receive_byte(); - } else { + rx_byte = gps_receive_byte(); + if(!rx_byte) { chThdSleepMilliseconds(10); continue; } @@ -96,15 +105,15 @@ uint8_t gps_receive_ack(uint8_t class_id, uint8_t msg_id, uint16_t timeout) { return false; } -/* - * gps_receive_payload - * - * retrieves the payload of a packet with a given class and message-id with the retrieved length. - * the caller has to ensure suitable buffer length! - * - * returns the length of the payload - * - */ +/** + * gps_receive_payload + * + * retrieves the payload of a packet with a given class and message-id with the retrieved length. + * the caller has to ensure suitable buffer length! + * + * returns the length of the payload + * + */ uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, unsigned char *payload, uint16_t timeout) { uint8_t rx_byte; enum {UBX_A, UBX_B, CLASSID, MSGID, LEN_A, LEN_B, PAYLOAD} state = UBX_A; @@ -115,9 +124,8 @@ uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, unsigned char *pa while(sTimeout >= chVTGetSystemTimeX()) { // Receive one byte - if(gps_bytes_avail()) { - rx_byte = gps_receive_byte(); - } else { + rx_byte = gps_receive_byte(); + if(!rx_byte) { chThdSleepMilliseconds(10); continue; } @@ -162,20 +170,20 @@ uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, unsigned char *pa return 0; } -/* - * gps_get_fix - * - * retrieves a GPS fix from the module. if validity flag is not set, date/time and position/altitude are - * assumed not to be reliable! - * - * This method divides MAX7/8 and MAX6 modules since the protocol changed at MAX7 series. MAX6 requires - * NAV-POSLLH NAV-TIMEUTC and NAV-SOL to get all information about the GPS. With implementation of the - * NAV-PVT message at the MAX7 series, all information can be aquired by only one message. Although - * MAX7 is backward compatible, MAX7/8 will use NAV-PVT rather than the old protocol. - * - * argument is call by reference to avoid large stack allocations - * - */ +/** + * gps_get_fix + * + * retrieves a GPS fix from the module. if validity flag is not set, date/time and position/altitude are + * assumed not to be reliable! + * + * This method divides MAX7/8 and MAX6 modules since the protocol changed at MAX7 series. MAX6 requires + * NAV-POSLLH NAV-TIMEUTC and NAV-SOL to get all information about the GPS. With implementation of the + * NAV-PVT message at the MAX7 series, all information can be aquired by only one message. Although + * MAX7 is backward compatible, MAX7/8 will use NAV-PVT rather than the old protocol. + * + * argument is call by reference to avoid large stack allocations + * + */ bool gps_get_fix(gpsFix_t *fix) { static uint8_t response[92]; @@ -184,7 +192,7 @@ bool gps_get_fix(gpsFix_t *fix) { gps_transmit_string(pvt, sizeof(pvt)); if(!gps_receive_payload(0x01, 0x07, response, 5000)) { // Receive request - TRACE_INFO("GPS > PVT Polling FAILED"); + TRACE_ERROR("GPS > PVT Polling FAILED"); return false; } @@ -224,16 +232,16 @@ bool gps_get_fix(gpsFix_t *fix) { return true; } -/* - * gps_disable_nmea_output - * - * disables all NMEA messages to be output from the GPS. - * even though the parser can cope with NMEA messages and ignores them, it - * may save power to disable them completely. - * - * returns if ACKed by GPS - * - */ +/** + * gps_disable_nmea_output + * + * disables all NMEA messages to be output from the GPS. + * even though the parser can cope with NMEA messages and ignores them, it + * may save power to disable them completely. + * + * returns if ACKed by GPS + * + */ uint8_t gps_disable_nmea_output(void) { uint8_t nonmea[] = { 0xB5, 0x62, 0x06, 0x00, 20, 0x00, // UBX-CFG-PRT @@ -251,17 +259,17 @@ uint8_t gps_disable_nmea_output(void) { return gps_receive_ack(0x06, 0x00, 1000); } -/* - * gps_set_airborne_model - * - * tells the GPS to use the airborne positioning model. Should be used to - * get stable lock up to 50km altitude - * - * working uBlox MAX-M8Q - * - * returns if ACKed by GPS - * - */ +/** + * gps_set_airborne_model + * + * tells the GPS to use the airborne positioning model. Should be used to + * get stable lock up to 50km altitude + * + * working uBlox MAX-M8Q + * + * returns if ACKed by GPS + * + */ uint8_t gps_set_airborne_model(void) { uint8_t model6[] = { 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, // UBX-CFG-NAV5 @@ -290,14 +298,14 @@ uint8_t gps_set_airborne_model(void) { return gps_receive_ack(0x06, 0x24, 1000); } -/* - * gps_set_power_save - * - * enables cyclic tracking on the uBlox M8Q - * - * returns if ACKed by GPS - * - */ +/** + * gps_set_power_save + * + * enables cyclic tracking on the uBlox M8Q + * + * returns if ACKed by GPS + * + */ uint8_t gps_set_power_save(void) { uint8_t powersave[] = { 0xB5, 0x62, 0x06, 0x3B, 44, 0, // UBX-CFG-PM2 @@ -320,11 +328,11 @@ uint8_t gps_set_power_save(void) { return gps_receive_ack(0x06, 0x3B, 1000); } -/* - * gps_power_save - * - * enables or disables the power save mode (which was configured before) - */ +/** + * gps_power_save + * + * enables or disables the power save mode (which was configured before) + */ uint8_t gps_power_save(int on) { uint8_t recvmgmt[] = { 0xB5, 0x62, 0x06, 0x11, 2, 0, // UBX-CFG-RXM @@ -346,12 +354,12 @@ bool GPS_Init(void) { TRACE_INFO("GPS > Init pins"); palSetLineMode(LINE_GPS_RESET, PAL_MODE_OUTPUT_PUSHPULL); // GPS reset palSetLineMode(LINE_GPS_EN, PAL_MODE_OUTPUT_PUSHPULL); // GPS off - palSetLineMode(LINE_GPS_RXD, PAL_MODE_ALTERNATE(8)); // UART RXD - palSetLineMode(LINE_GPS_TXD, PAL_MODE_ALTERNATE(8)); // UART TXD + palSetLineMode(LINE_GPS_RXD, PAL_MODE_ALTERNATE(11)); // UART RXD + palSetLineMode(LINE_GPS_TXD, PAL_MODE_ALTERNATE(11)); // UART TXD // Init UART - //TRACE_INFO("GPS > Init GPS UART"); - //sdStart(&SD6, &gps_config); + TRACE_INFO("GPS > Init GPS UART"); + sdStart(&SD5, &gps_config); // Switch MOSFET TRACE_INFO("GPS > Switch on"); @@ -359,39 +367,31 @@ bool GPS_Init(void) { palSetLine(LINE_GPS_EN); // Switch on GPS // Wait for GPS startup - chThdSleepMilliseconds(3000); - - uint8_t status = 1; + chThdSleepMilliseconds(1000); // Configure GPS - TRACE_INFO("GPS > Initialize GPS"); - if(gps_disable_nmea_output()) { - TRACE_INFO("GPS > Disable NMEA output OK"); + TRACE_INFO("GPS > Transmit config to GPS"); + + uint8_t cntr = 3; + bool status; + while((status = gps_disable_nmea_output()) == false && cntr--); + if(status) { + TRACE_INFO("GPS > ... Disable NMEA output OK"); } else { - TRACE_ERROR("GPS > Disable NMEA output FAILED"); - status = 0; + TRACE_ERROR("GPS > Communication Eror"); + return false; } - if(gps_set_airborne_model()) { - TRACE_INFO("GPS > Set airborne model OK"); + cntr = 5; + while((status = gps_set_airborne_model()) == false && cntr--); + if(status) { + TRACE_INFO("GPS > ... Set airborne model OK"); } else { - TRACE_ERROR("GPS > Set airborne model FAILED"); - status = 0; - } - if(gps_set_power_save()) { - TRACE_INFO("GPS > Configure power save OK"); - } else { - TRACE_ERROR("GPS > Configure power save FAILED"); - status = 0; - } - if(gps_power_save(0)) { - TRACE_INFO("GPS > Disable power save OK"); - } else { - TRACE_ERROR("GPS > Disable power save FAILED"); - status = 0; + TRACE_ERROR("GPS > Communication Eror"); + return false; } - return status; + return true; } void GPS_Deinit(void) diff --git a/tracker/software/drivers/ublox.h b/tracker/software/drivers/ublox.h index 6622acb..d01f079 100644 --- a/tracker/software/drivers/ublox.h +++ b/tracker/software/drivers/ublox.h @@ -11,6 +11,10 @@ #define UBLOX_MAX_ADDRESS 0x42 +// You can either use I2C or UART +#define UBLOX_USE_UART +//#define UBLOX_USE_I2C + #define isGPSLocked(pos) ((pos)->type == 3 && (pos)->num_svs >= 5) typedef struct { diff --git a/tracker/software/main.c b/tracker/software/main.c index 1949ef4..85d45e6 100644 --- a/tracker/software/main.c +++ b/tracker/software/main.c @@ -34,6 +34,25 @@ int main(void) { chThdSleepMilliseconds(100); #endif + + + /*// Clear Wakeup flag + PWR->CR |= PWR_CR_CWUF; + + // Select STANDBY mode + PWR->CR |= PWR_CR_PDDS; + + // Set SLEEPDEEP bit of Cortex System Control Register + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + + // This option is used to ensure that store operations are completed + #if defined ( __CC_ARM ) + __force_stores(); + #endif + // Request Wait For Interrupt + __WFI(); + while(1);*/ + // Init debugging (Serial debug port, LEDs) DEBUG_INIT(); TRACE_INFO("MAIN > Startup"); diff --git a/tracker/software/mcuconf.h b/tracker/software/mcuconf.h index 23f6d7e..ec0e75b 100644 --- a/tracker/software/mcuconf.h +++ b/tracker/software/mcuconf.h @@ -195,6 +195,7 @@ #define STM32_SERIAL_USE_USART1 FALSE #define STM32_SERIAL_USE_USART2 FALSE #define STM32_SERIAL_USE_USART3 TRUE +#define STM32_SERIAL_USE_UART5 TRUE #define STM32_SERIAL_USE_USART6 FALSE #define STM32_SERIAL_USART1_PRIORITY 12 #define STM32_SERIAL_USART2_PRIORITY 12 @@ -243,6 +244,7 @@ #define STM32_UART_USE_USART1 FALSE #define STM32_UART_USE_USART2 FALSE #define STM32_UART_USE_USART3 FALSE +#define STM32_UART_USE_UART5 TRUE #define STM32_UART_USE_USART6 FALSE #define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5) #define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7) @@ -250,6 +252,8 @@ #define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) #define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 1) #define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3) +#define STM32_UART_UART5_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0) +#define STM32_UART_UART5_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) #define STM32_UART_USART6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) #define STM32_UART_USART6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7) #define STM32_UART_USART1_IRQ_PRIORITY 12 diff --git a/tracker/software/protocols/aprs/aprs.c b/tracker/software/protocols/aprs/aprs.c index b06c227..6fc1158 100644 --- a/tracker/software/protocols/aprs/aprs.c +++ b/tracker/software/protocols/aprs/aprs.c @@ -24,7 +24,7 @@ #define METER_TO_FEET(m) (((m)*26876) / 8192) -static uint16_t loss_of_gps_counter = 0; +static uint16_t loss_of_gps_counter; static uint16_t msg_id; /** @@ -99,34 +99,44 @@ void aprs_encode_position(ax25_t* packet, const aprs_conf_t *config, trackPoint_ chsnprintf(temp, sizeof(temp), "%d", trackPoint->gps_sats); ax25_send_string(packet, temp); - if(trackPoint->gps_lock == GPS_LOCKED) // GPS is locked - { - // TTFF (Time to first fix) - ax25_send_string(packet, " TTFF "); - chsnprintf(temp, sizeof(temp), "%d", trackPoint->gps_ttff); - ax25_send_string(packet, temp); - ax25_send_string(packet, "sec"); - } + switch(trackPoint->gps_lock) { + case GPS_LOCKED: // GPS is locked + // TTFF (Time to first fix) + ax25_send_string(packet, " TTFF "); + chsnprintf(temp, sizeof(temp), "%d", trackPoint->gps_ttff); + ax25_send_string(packet, temp); + ax25_send_string(packet, "sec"); + loss_of_gps_counter = 0; + break; - if(trackPoint->gps_lock == GPS_LOSS) { // No GPS lock + case GPS_LOSS: // No GPS lock + ax25_send_string(packet, " GPS LOSS "); + chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); + ax25_send_string(packet, temp); + break; - ax25_send_string(packet, " GPS LOSS "); - chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); - ax25_send_string(packet, temp); + case GPS_LOWBATT1: // GPS switched off prematurely + ax25_send_string(packet, " GPS LOWBATT1 "); + chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); + ax25_send_string(packet, temp); + break; - } else if(trackPoint->gps_lock == GPS_LOWBATT) { // GPS switched off prematurely + case GPS_LOWBATT2: // GPS switched off prematurely + ax25_send_string(packet, " GPS LOWBATT2 "); + chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); + ax25_send_string(packet, temp); + break; - ax25_send_string(packet, " GPS LOWBATT "); - chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); - ax25_send_string(packet, temp); + case GPS_ERROR: // GPS error + ax25_send_string(packet, " GPS ERROR "); + chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter); + ax25_send_string(packet, temp); + break; - } else if(trackPoint->gps_lock == GPS_LOG) { // GPS position from log (because the tracker has been just switched on) - - ax25_send_string(packet, " GPS FROM LOG"); - loss_of_gps_counter = 0; - - } else { - loss_of_gps_counter = 0; + case GPS_LOG: // GPS position from log (because the tracker has been just switched on) + ax25_send_string(packet, " GPS FROM LOG"); + loss_of_gps_counter = 0; + break; } ax25_send_byte(packet, '|'); diff --git a/tracker/software/radio.c b/tracker/software/radio.c index a2cc682..e9ff38b 100644 --- a/tracker/software/radio.c +++ b/tracker/software/radio.c @@ -175,7 +175,6 @@ THD_FUNCTION(si_fifo_feeder_thd2, arg) c += more; chThdSleepMilliseconds(15); } - // Shutdown radio (and wait for Si4464 to finish transmission) shutdownRadio(); @@ -325,6 +324,7 @@ void shutdownRadio(void) while(Si4464_getState() == SI4464_STATE_TX) chThdSleepMilliseconds(10); + TRACE_INFO("RAD > Shutdown radio"); Si4464_shutdown(); active_mod = MOD_NOT_SET; } diff --git a/tracker/software/threads/image.c b/tracker/software/threads/image.c index f114f0d..4ff5953 100644 --- a/tracker/software/threads/image.c +++ b/tracker/software/threads/image.c @@ -273,8 +273,6 @@ const uint8_t noCameraFound[4071] = { 0xBD, 0xC0, 0x20, 0x00, 0x01, 0xFF, 0xD9 }; -#include - uint8_t gimage_id; // Global image ID (for all image threads) mutex_t camera_mtx; bool camera_mtx_init = false; @@ -404,6 +402,9 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, // Initialize new packet buffer aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod); + + if(!conf->packet_spacing) + chThdSleepMilliseconds(8000); // Leave a little break because it will overflow some devices } break; @@ -439,6 +440,59 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, } } +/** + * Analyzes the image for JPEG errors. Returns true if the image is error free. + */ +static bool analyze_image(uint8_t *image, uint32_t image_len) +{ + #if !OV5640_USE_DMA_DBM + if(image_len >= 65535) + { + TRACE_ERROR("CAM > Camera has %d bytes allocated but DMA DBM not activated", image_len); + TRACE_ERROR("CAM > DMA can only use 65535 bytes only"); + image_len = 65535; + } + #endif + + ssdv_t ssdv; + uint8_t pkt[SSDV_PKT_SIZE]; + uint8_t *b; + uint32_t bi = 0; + uint16_t i = 0; + uint8_t c = SSDV_OK; + + ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, "", 0, 7); + ssdv_enc_set_buffer(&ssdv, pkt); + + while(true) // FIXME: I get caught in these loops occasionally and never return + { + while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME) + { + b = &image[bi]; + uint8_t r = bi < image_len-128 ? 128 : image_len - bi; + bi += r; + if(r <= 0) + { + TRACE_ERROR("CAM > Error in image (Premature end of file %d)", i); + return false; + } + ssdv_enc_feed(&ssdv, b, r); + } + + if(c == SSDV_EOI) // End of image + return true; + + if(c != SSDV_OK) // Error in JPEG image + { + TRACE_ERROR("CAM > Error in image (ssdv_enc_get_packet failed: %d %d)", c, i); + return false; + } + + i++; + chThdSleepMilliseconds(5); + } +} + static bool camInitialized = false; bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation) @@ -463,24 +517,38 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation) // Lock Radio (The radio uses the same DMA for SPI as the camera) lockRadio(); // Lock radio - // Init camera - if(!camInitialized) { - OV5640_init(); - camInitialized = true; - } + uint8_t cntr = 5; + bool jpegValid; + do { + // Init camera + if(!camInitialized) { + OV5640_init(); + camInitialized = true; + } - // Sample data from pseudo DCMI through DMA into RAM - conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res, enableJpegValidation); + // Sample data from pseudo DCMI through DMA into RAM + conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res); + + // Switch off camera + if(!keep_cam_switched_on) { + OV5640_deinit(); + camInitialized = false; + } + + // Validate JPEG image + if(enableJpegValidation) + { + TRACE_INFO("CAM > Validate integrity of JPEG"); + jpegValid = analyze_image(conf->ram_buffer, conf->ram_size); + TRACE_INFO("CAM > JPEG image %s", jpegValid ? "valid" : "invalid"); + } else { + jpegValid = true; + } + } while(!jpegValid && cntr--); // Unlock radio unlockRadio(); - // Switch off camera - if(!keep_cam_switched_on) { - OV5640_deinit(); - camInitialized = false; - } - } else { // Camera not found camInitialized = false; diff --git a/tracker/software/threads/tracking.c b/tracker/software/threads/tracking.c index 2a844e3..60090b0 100644 --- a/tracker/software/threads/tracking.c +++ b/tracker/software/threads/tracking.c @@ -224,62 +224,76 @@ THD_FUNCTION(trackingThread, arg) { // Switch on GPS is enough power is available and GPS is needed by any position thread uint16_t batt = getBatteryVoltageMV(); - if(batt >= gps_on_vbat && tracking_useGPS) - { + if(!tracking_useGPS) { // No position thread running + tp->gps_lock = GPS_OFF; + } else if(batt < gps_on_vbat) { + tp->gps_lock = GPS_LOWBATT1; + } else { + // Switch on GPS - GPS_Init(); + bool status = GPS_Init(); - // Search for lock as long enough power is available - do { - batt = getBatteryVoltageMV(); - gps_get_fix(&gpsFix); - } while(!isGPSLocked(&gpsFix) && batt >= gps_off_vbat && chVTGetSystemTimeX() <= time + track_cycle_time - S2ST(3)); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-3sec in order to keep synchronization) + if(status) { + + // Search for lock as long enough power is available + do { + batt = getBatteryVoltageMV(); + gps_get_fix(&gpsFix); + } while(!isGPSLocked(&gpsFix) && batt >= gps_off_vbat && chVTGetSystemTimeX() <= time + track_cycle_time - S2ST(3)); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-3sec in order to keep synchronization) + + if(batt < gps_off_vbat) { // GPS was switched on but prematurely switched off because the battery is low on power, switch off GPS + + GPS_Deinit(); + TRACE_WARN("TRAC > GPS sampling finished GPS LOW BATT"); + tp->gps_lock = GPS_LOWBATT2; + + } else if(!isGPSLocked(&gpsFix)) { // GPS was switched on but it failed to get a lock, keep GPS switched on + + TRACE_WARN("TRAC > GPS sampling finished GPS LOSS"); + tp->gps_lock = GPS_LOSS; + + } else { // GPS locked successfully, switch off GPS (unless cycle is less than 60 seconds) + + // Switch off GPS (if cycle time is more than 60 seconds) + if(track_cycle_time >= S2ST(60)) { + TRACE_INFO("TRAC > Switch off GPS"); + GPS_Deinit(); + } else { + TRACE_INFO("TRAC > Keep GPS switched of because cycle < 60sec"); + } + + // Debug + TRACE_INFO("TRAC > GPS sampling finished GPS LOCK"); + + // Calibrate RTC + setTime(gpsFix.time); + + // Take time from GPS + tp->time.year = gpsFix.time.year; + tp->time.month = gpsFix.time.month; + tp->time.day = gpsFix.time.day; + tp->time.hour = gpsFix.time.hour; + tp->time.minute = gpsFix.time.minute; + tp->time.second = gpsFix.time.second; + + // Set new GPS fix + tp->gps_lat = gpsFix.lat; + tp->gps_lon = gpsFix.lon; + tp->gps_alt = gpsFix.alt; + + tp->gps_lock = GPS_LOCKED; + tp->gps_sats = gpsFix.num_svs; + } + + } else { // GPS communication error - if(batt < gps_off_vbat) // Switch off GPS at low batt GPS_Deinit(); + tp->gps_lock = GPS_ERROR; + + } } - if(isGPSLocked(&gpsFix)) { // GPS locked - - // Switch off GPS (if cycle time is more than 60 seconds) - if(track_cycle_time >= S2ST(60)) { - TRACE_INFO("TRAC > Switch off GPS"); - GPS_Deinit(); - } else { - TRACE_INFO("TRAC > Keep GPS switched of because cycle < 60sec"); - } - - // Debug - TRACE_INFO("TRAC > GPS sampling finished GPS LOCK"); - - // Calibrate RTC - setTime(gpsFix.time); - - // Take time from GPS - tp->time.year = gpsFix.time.year; - tp->time.month = gpsFix.time.month; - tp->time.day = gpsFix.time.day; - tp->time.hour = gpsFix.time.hour; - tp->time.minute = gpsFix.time.minute; - tp->time.second = gpsFix.time.second; - - // Set new GPS fix - tp->gps_lat = gpsFix.lat; - tp->gps_lon = gpsFix.lon; - tp->gps_alt = gpsFix.alt; - - tp->gps_lock = GPS_LOCKED; - tp->gps_sats = gpsFix.num_svs; - - } else { // GPS lost (keep GPS switched on) - - // Debug - if(batt < gps_off_vbat) { - TRACE_WARN("TRAC > GPS sampling finished GPS LOW BATT"); - } else { - TRACE_WARN("TRAC > GPS sampling finished GPS LOSS"); - } - + if(tp->gps_lock != GPS_LOCKED) { // We have no valid GPS fix // Take time from internal RTC getTime(&rtc); tp->time.year = rtc.year; @@ -293,14 +307,6 @@ THD_FUNCTION(trackingThread, arg) { tp->gps_lat = ltp->gps_lat; tp->gps_lon = ltp->gps_lon; tp->gps_alt = ltp->gps_alt; - - // Mark GPS loss (or low batt, GPS switch off) - if(tracking_useGPS) - tp->gps_lock = batt < gps_off_vbat || batt < gps_on_vbat ? GPS_LOWBATT : GPS_LOSS; - else - tp->gps_lock = GPS_OFF; - tp->gps_sats = 0; - } tp->id = id; // Serial ID diff --git a/tracker/software/threads/tracking.h b/tracker/software/threads/tracking.h index 0331d7a..db48677 100644 --- a/tracker/software/threads/tracking.h +++ b/tracker/software/threads/tracking.h @@ -6,11 +6,13 @@ #include "ptime.h" typedef enum { - GPS_LOCKED, // GPS is locked and could aquire a fix - GPS_LOSS, // GPS was switched on all time but it couln't aquire a fix - GPS_LOWBATT, // GPS was switched on but had to be switched off prematurely while the battery is almost empty (or is too cold) + GPS_LOCKED, // The GPS is locked and could aquire a fix + GPS_LOSS, // The GPS was switched on all time but it couln't aquire a fix + GPS_LOWBATT1, // The GPS wasnt switched on because the battery has not enough energy + GPS_LOWBATT2, // The GPS was switched on but has been switched off prematurely while the battery has not enough energy (or is too cold) GPS_LOG, // The tracker has been just switched on and the position has been taken from the log GPS_OFF, // There is no active position thread so the GPS was never switched on (in oder to save power) + GPS_ERROR // The GPS has a communication error } gpsLock_t; typedef struct { diff --git a/tracker/software/threads/watchdog.c b/tracker/software/threads/watchdog.c index 490110d..a86a43b 100644 --- a/tracker/software/threads/watchdog.c +++ b/tracker/software/threads/watchdog.c @@ -16,12 +16,20 @@ void register_thread_at_wdg(module_conf_t *thread_config) registered_threads[threads_cnt++] = thread_config; } +static void flash_led(void) { + palClearLine(LINE_IO_LED2); + chThdSleepMilliseconds(50); + palSetLine(LINE_IO_LED2); +} + THD_FUNCTION(wdgThread, arg) { (void)arg; uint8_t counter = 0; while(true) { + chThdSleepMilliseconds(1000); + bool healthy = true; for(uint8_t i=0; iwdg_timeout < chVTGetSystemTimeX()) @@ -37,11 +45,8 @@ THD_FUNCTION(wdgThread, arg) { // Switch LEDs if(counter++ % (4*healthy) == 0) { - palClearLine(LINE_IO_LED2); - chThdSleepMilliseconds(50); - palSetLine(LINE_IO_LED2); + flash_led(); } - chThdSleepMilliseconds(1000); } } @@ -52,6 +57,8 @@ void init_watchdog(void) wdgStart(&WDGD1, &wdgcfg); wdgReset(&WDGD1); + flash_led(); + TRACE_INFO("WDG > Startup Watchdog thread"); thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(256), "WDG", NORMALPRIO, wdgThread, NULL); if(!th) {