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) {