kopia lustrzana https://github.com/DL7AD/pecanpico9
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 upmaster
rodzic
f6d69c9afe
commit
8a0b8b8252
|
@ -9,7 +9,6 @@
|
|||
#include "pi2c.h"
|
||||
#include "board.h"
|
||||
#include "debug.h"
|
||||
#include "ssdv.h"
|
||||
#include "padc.h"
|
||||
#include <string.h>
|
||||
|
||||
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -359,7 +359,3 @@ int8_t Si4464_getTemperature(void) {
|
|||
return (899*adc)/4096 - 293;
|
||||
}
|
||||
|
||||
bool isRadioInitialized(void) {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,32 +19,41 @@ 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;
|
||||
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.
|
||||
|
@ -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 {
|
||||
if(!rx_byte) {
|
||||
chThdSleepMilliseconds(10);
|
||||
continue;
|
||||
}
|
||||
|
@ -96,7 +105,7 @@ 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.
|
||||
|
@ -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 {
|
||||
if(!rx_byte) {
|
||||
chThdSleepMilliseconds(10);
|
||||
continue;
|
||||
}
|
||||
|
@ -162,7 +170,7 @@ 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
|
||||
|
@ -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,7 +232,7 @@ bool gps_get_fix(gpsFix_t *fix) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* gps_disable_nmea_output
|
||||
*
|
||||
* disables all NMEA messages to be output from the GPS.
|
||||
|
@ -251,7 +259,7 @@ 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
|
||||
|
@ -290,7 +298,7 @@ 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
|
||||
|
@ -320,7 +328,7 @@ 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)
|
||||
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
if(trackPoint->gps_lock == GPS_LOSS) { // No GPS lock
|
||||
loss_of_gps_counter = 0;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
} else if(trackPoint->gps_lock == GPS_LOWBATT) { // GPS switched off prematurely
|
||||
|
||||
ax25_send_string(packet, " GPS LOWBATT ");
|
||||
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_LOG) { // GPS position from log (because the tracker has been just switched on)
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
} else {
|
||||
loss_of_gps_counter = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ax25_send_byte(packet, '|');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -273,8 +273,6 @@ const uint8_t noCameraFound[4071] = {
|
|||
0xBD, 0xC0, 0x20, 0x00, 0x01, 0xFF, 0xD9
|
||||
};
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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,6 +517,9 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
|
|||
// Lock Radio (The radio uses the same DMA for SPI as the camera)
|
||||
lockRadio(); // Lock radio
|
||||
|
||||
uint8_t cntr = 5;
|
||||
bool jpegValid;
|
||||
do {
|
||||
// Init camera
|
||||
if(!camInitialized) {
|
||||
OV5640_init();
|
||||
|
@ -470,10 +527,7 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
|
|||
}
|
||||
|
||||
// Sample data from pseudo DCMI through DMA into RAM
|
||||
conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res, enableJpegValidation);
|
||||
|
||||
// Unlock radio
|
||||
unlockRadio();
|
||||
conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res);
|
||||
|
||||
// Switch off camera
|
||||
if(!keep_cam_switched_on) {
|
||||
|
@ -481,6 +535,20 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
|
|||
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();
|
||||
|
||||
} else { // Camera not found
|
||||
|
||||
camInitialized = false;
|
||||
|
|
|
@ -224,10 +224,16 @@ 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();
|
||||
|
||||
if(status) {
|
||||
|
||||
// Search for lock as long enough power is available
|
||||
do {
|
||||
|
@ -235,11 +241,18 @@ THD_FUNCTION(trackingThread, arg) {
|
|||
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) // Switch off GPS at low batt
|
||||
GPS_Deinit();
|
||||
}
|
||||
if(batt < gps_off_vbat) { // GPS was switched on but prematurely switched off because the battery is low on power, switch off GPS
|
||||
|
||||
if(isGPSLocked(&gpsFix)) { // GPS locked
|
||||
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)) {
|
||||
|
@ -270,16 +283,17 @@ THD_FUNCTION(trackingThread, arg) {
|
|||
|
||||
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");
|
||||
}
|
||||
|
||||
} else { // GPS communication error
|
||||
|
||||
GPS_Deinit();
|
||||
tp->gps_lock = GPS_ERROR;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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; i<threads_cnt; i++) {
|
||||
if(registered_threads[i]->wdg_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) {
|
||||
|
|
Ładowanie…
Reference in New Issue