2020-08-24 08:57:28 +00:00
|
|
|
/*************************************************************************
|
|
|
|
*
|
|
|
|
* SatCam - Camera Module for PSAT-2
|
|
|
|
* Copyright (c) 2015-2017 Ales Povalac <alpov@alpov.net>
|
|
|
|
* Dept. of Radio Electronics, Brno University of Technology
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the MIT license
|
|
|
|
*
|
|
|
|
*************************************************************************/
|
|
|
|
|
|
|
|
#include "cube.h"
|
|
|
|
#include "eeprom.h"
|
|
|
|
#include "ov2640.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "comm.h"
|
|
|
|
#include "m25p16.h"
|
|
|
|
#include "sstv.h"
|
|
|
|
#include "eeprom.h"
|
|
|
|
|
|
|
|
static uint8_t jpeg[IMG_BUFFER_SIZE];
|
|
|
|
static struct {
|
|
|
|
uint32_t length;
|
|
|
|
char overlay[2][TEXT_LEN];
|
|
|
|
} img;
|
|
|
|
|
|
|
|
IMPORT_BIN("Inc/sstv_monoscope.jpg", uint8_t, img_monoscope);
|
|
|
|
|
|
|
|
uint8_t *images[] = {
|
|
|
|
img_monoscope,
|
|
|
|
};
|
|
|
|
|
|
|
|
bool service_enable = false;
|
|
|
|
|
|
|
|
/* global configuration variables, linked from eeprom.c */
|
|
|
|
CONFIG_SYSTEM config;
|
|
|
|
const CONFIG_SYSTEM config_default = {
|
|
|
|
.command = 0,
|
|
|
|
.save_page = 0,
|
|
|
|
.save_count = 0,
|
|
|
|
.save_delay = 0,
|
|
|
|
.save_light_lo = 0,
|
|
|
|
.save_light_hi = 0,
|
|
|
|
.spl_light_delay = 0,
|
|
|
|
.spl_volt_delay = 0,
|
|
|
|
.spl_temp_delay = 0,
|
|
|
|
.psk_append = 0,
|
|
|
|
.cam_agc = true,
|
|
|
|
.cam_aec = true,
|
|
|
|
.cam_agc_ceiling = 16,
|
|
|
|
.cam_agc_manual = 0,
|
|
|
|
.cam_aec_manual = 0,
|
|
|
|
.cam_awb = AWB_SUNNY,
|
|
|
|
.cam_rotate = false,
|
|
|
|
.psk_speed = PSK_SPEED2,
|
|
|
|
.psk_freq = PSK_FREQ,
|
|
|
|
.cw_wpm = CW_WPM,
|
|
|
|
.cw_freq = CW_FREQ,
|
|
|
|
.callsign = "SatCam",
|
|
|
|
.sys_i2c_watchdog = 0,
|
|
|
|
.sys_autoreboot = 43200,
|
|
|
|
.cam_delay = 1000,
|
|
|
|
.cam_qs = 5,
|
|
|
|
.sstv_ampl = (int16_t)(0.9 * 32767),
|
|
|
|
.psk_ampl = (int16_t)(0.4 * 32767),
|
|
|
|
.cw_ampl = (int16_t)(0.9 * 32767),
|
|
|
|
.debug_enable = 1,
|
|
|
|
.light_cal = 512,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static bool camera_snapshot(void)
|
|
|
|
{
|
|
|
|
CONFIG_CAMERA cam = {
|
|
|
|
.delay = config.cam_delay,
|
|
|
|
.qs = config.cam_qs,
|
|
|
|
.agc = config.cam_agc ? true : false,
|
|
|
|
.aec = config.cam_aec ? true : false,
|
|
|
|
.agc_ceiling = config.cam_agc_ceiling,
|
|
|
|
.agc_manual = config.cam_agc_manual,
|
|
|
|
.aec_manual = config.cam_aec_manual,
|
|
|
|
.awb = config.cam_awb,
|
|
|
|
.rotate = config.cam_rotate ? true : false,
|
|
|
|
};
|
|
|
|
|
|
|
|
set_led_red(true);
|
|
|
|
if (!ov2640_hilevel_init(&cam)) {
|
|
|
|
set_led_red(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
img.length = ov2640_snapshot(jpeg, sizeof(jpeg)); // requires enabled turbo
|
|
|
|
uint16_t agc = ov2640_get_current_agc();
|
|
|
|
uint16_t aec = ov2640_get_current_aec();
|
|
|
|
set_led_red(false);
|
|
|
|
ov2640_enable(false);
|
|
|
|
|
|
|
|
uint32_t img_counter = syslog_get_counter(LOG_CAM_SNAPSHOT) % 10000;
|
|
|
|
uint16_t voltage = adc_read_voltage();
|
|
|
|
uint16_t light = adc_read_light();
|
|
|
|
snprintf(img.overlay[OVERLAY_HEADER], sizeof(img.overlay[OVERLAY_HEADER]), "%s +%05u %d\037C #%04u %02ulx%u %umV", config.callsign,
|
|
|
|
(unsigned int)(HAL_GetTick() / 60000), adc_read_temperature(), (unsigned int)img_counter, light % 100, light / 100, voltage
|
|
|
|
);
|
|
|
|
|
2020-09-02 07:42:42 +00:00
|
|
|
snprintf(img.overlay[OVERLAY_IMG], sizeof(img.overlay[OVERLAY_IMG]), "#%04u %uB g%u.%u e%u", (unsigned int)img_counter, (int)img.length, agc/10, agc%10, aec);
|
2020-08-24 08:57:28 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void send_roger(void)
|
|
|
|
{
|
|
|
|
if (config.debug_enable) {
|
|
|
|
audio_start();
|
|
|
|
audio_morse(CW_WPM, CW_FREQ, "R");
|
|
|
|
audio_stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_sstv(uint8_t param, uint8_t mode)
|
|
|
|
{
|
|
|
|
if (param == 0) { // snapshot camera (live)
|
|
|
|
/* start SSTV here */
|
|
|
|
enable_turbo(true); // peak 18% CPU
|
|
|
|
bool ok = camera_snapshot();
|
|
|
|
if (ok) ok = jpeg_test(jpeg, img.length);
|
|
|
|
if (ok) {
|
|
|
|
sstv_set_overlay(OVERLAY_HEADER, img.overlay[OVERLAY_HEADER]);
|
|
|
|
sstv_set_overlay(OVERLAY_IMG, img.overlay[OVERLAY_IMG]);
|
|
|
|
sstv_set_overlay(OVERLAY_LARGE, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_FROM, config.callsign);
|
|
|
|
sstv_play_jpeg(jpeg, mode);
|
|
|
|
}
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
else if (param >= 1 && param <= 16) { // Flash images
|
|
|
|
uint8_t sector = param - 1;
|
|
|
|
|
|
|
|
/* load image from FLASH: sector */
|
|
|
|
flash_read(ADDR_JPEGIMAGE(sector), jpeg, 0xFFFF);
|
|
|
|
flash_read(ADDR_FLASHINFO(sector), (uint8_t*)(&img), sizeof(img));
|
|
|
|
|
|
|
|
/* send image: mode, overlay */
|
|
|
|
if (img.length != 0 && img.length != 0xFFFFFFFF) {
|
|
|
|
// change last parameter (voltage) to flash memory number
|
|
|
|
sprintf(strrchr(img.overlay[OVERLAY_HEADER], ' '), " F#%u", sector);
|
|
|
|
sstv_set_overlay(OVERLAY_HEADER, img.overlay[OVERLAY_HEADER]);
|
|
|
|
sstv_set_overlay(OVERLAY_IMG, img.overlay[OVERLAY_IMG]);
|
|
|
|
sstv_set_overlay(OVERLAY_LARGE, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_FROM, config.callsign);
|
|
|
|
enable_turbo(true); // peak 18% CPU
|
|
|
|
sstv_play_jpeg(jpeg, mode);
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (param == 17) { // Flash thumbnails
|
|
|
|
/* send thumbnails: mode, no overlay */
|
|
|
|
snprintf(img.overlay[OVERLAY_HEADER], sizeof(img.overlay[OVERLAY_HEADER]), "%s +%05u %d\037C thmbs %umV", config.callsign,
|
|
|
|
(unsigned int)(HAL_GetTick() / 60000), adc_read_temperature(), adc_read_voltage()
|
|
|
|
);
|
|
|
|
sstv_set_overlay(OVERLAY_HEADER, img.overlay[OVERLAY_HEADER]);
|
|
|
|
sstv_set_overlay(OVERLAY_IMG, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_LARGE, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_FROM, NULL);
|
|
|
|
enable_turbo(true); // peak 31% CPU
|
|
|
|
sstv_play_thumbnail(mode);
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
else if (param >= 18 && param <= (18+(sizeof(images)/sizeof(images[0])))) { // ROM images
|
|
|
|
uint8_t sector = param - 18;
|
|
|
|
|
|
|
|
/* send image: mode, overlay */
|
|
|
|
snprintf(img.overlay[OVERLAY_HEADER], sizeof(img.overlay[OVERLAY_HEADER]), "%s +%05u %d\037C %umV ROM #%u", config.callsign,
|
|
|
|
(unsigned int)(HAL_GetTick() / 60000), adc_read_temperature(), adc_read_voltage(), sector
|
|
|
|
);
|
|
|
|
sstv_set_overlay(OVERLAY_HEADER, img.overlay[OVERLAY_HEADER]);
|
|
|
|
sstv_set_overlay(OVERLAY_IMG, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_LARGE, NULL);
|
|
|
|
sstv_set_overlay(OVERLAY_FROM, config.callsign);
|
|
|
|
|
|
|
|
enable_turbo(true); // peak 18% CPU
|
|
|
|
sstv_play_jpeg(images[sector], mode);
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config.psk_append) {
|
|
|
|
char buffer[SYSLOG_MAX_LENGTH];
|
|
|
|
syslog_read_telemetry(buffer);
|
|
|
|
|
|
|
|
/* start PSK here */
|
|
|
|
enable_turbo(true); // peak 18% CPU
|
|
|
|
audio_start();
|
|
|
|
audio_psk(config.psk_append, config.psk_freq, buffer); // 76% CPU without turbo, 4% CPU with turbo
|
|
|
|
audio_stop();
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_psk(uint8_t param)
|
|
|
|
{
|
|
|
|
char buffer[SYSLOG_MAX_LENGTH];
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case 0: // hello
|
|
|
|
snprintf(buffer, sizeof(buffer), "Greetings from %s, up %umin", config.callsign, (unsigned int)(HAL_GetTick() / 60000));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: // config
|
|
|
|
syslog_read_config(buffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // nvinfo
|
|
|
|
syslog_read_nvinfo(buffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // telemetry
|
|
|
|
syslog_read_telemetry(buffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4 ... 6: // samples all
|
|
|
|
syslog_read_samples(buffer, param-4, MAX_SAMPLES);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7 ... 9: // samples 32
|
|
|
|
syslog_read_samples(buffer, param-7, 32);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start PSK here */
|
|
|
|
enable_turbo(true); // peak 18% CPU
|
|
|
|
audio_start();
|
|
|
|
audio_psk(config.psk_speed, config.psk_freq, buffer); // 76% CPU without turbo, 4% CPU with turbo
|
|
|
|
audio_stop();
|
|
|
|
enable_turbo(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_cw(uint8_t param)
|
|
|
|
{
|
|
|
|
char buffer[CW_MAX_LENGTH];
|
|
|
|
|
|
|
|
if (param == 0) { // hello
|
|
|
|
snprintf(buffer, sizeof(buffer), "73 DE %s %s %s K", config.callsign, config.callsign, config.callsign);
|
|
|
|
}
|
|
|
|
else return;
|
|
|
|
|
|
|
|
/* start CW here */
|
|
|
|
audio_start();
|
|
|
|
audio_morse(config.cw_wpm, config.cw_freq, buffer);
|
|
|
|
audio_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_service(uint8_t param)
|
|
|
|
{
|
|
|
|
if (!service_enable) return;
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case 0: // eeprom clearlog
|
|
|
|
eeprom_erase_full(false);
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: // reboot by nvic
|
|
|
|
NVIC_SystemReset(); // trigger NVIC system reset
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // eeprom load
|
|
|
|
config_load_eeprom();
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // eeprom save
|
|
|
|
config_save_eeprom();
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: // eeprom default
|
|
|
|
config_load_default();
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 200: // reboot by watchdog
|
|
|
|
while (1) {} // wait for watchdog reset, system halted
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 201: // reboot by fault
|
|
|
|
((void (*)())(0x0700000))(); // trigger HardFault with invalid jump
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 210: // eeprom fullerase
|
|
|
|
eeprom_erase_full(true);
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 220: // flash erase all
|
|
|
|
flash_erase_bulk();
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 221 ... 236: // flash erase sector
|
|
|
|
flash_erase_sector(ADDR_JPEGIMAGE(param - 221));
|
|
|
|
flash_erase_sector(ADDR_THUMBNAIL(param - 221));
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // service disable
|
|
|
|
service_enable = false;
|
|
|
|
send_roger();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cmd_handler(uint16_t command)
|
|
|
|
{
|
|
|
|
uint8_t cmd = (command) & 0xFF;
|
|
|
|
uint8_t param = (command >> 8) & 0xFF;
|
|
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case 1: cmd_sstv(param, 36); break;
|
|
|
|
case 2: cmd_sstv(param, 72); break;
|
|
|
|
case 3: cmd_sstv(param, 73); break;
|
|
|
|
case 4: cmd_sstv(param, 115); break;
|
|
|
|
case 5: cmd_psk(param); break;
|
|
|
|
case 6: cmd_cw(param); break;
|
|
|
|
case 254: cmd_service(param); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void save_task(void)
|
|
|
|
{
|
|
|
|
static uint32_t last;
|
|
|
|
|
|
|
|
if (config.save_count == 0) return;
|
|
|
|
|
|
|
|
if (HAL_GetTick() >= last + (config.save_delay*1000)) {
|
|
|
|
last = HAL_GetTick();
|
|
|
|
|
|
|
|
/* check light */
|
|
|
|
uint16_t light = adc_read_light();
|
|
|
|
if (config.save_light_lo && light < config.save_light_lo) return;
|
|
|
|
if (config.save_light_hi && light > config.save_light_hi) return;
|
|
|
|
|
|
|
|
/* do CAM snapshot here */
|
|
|
|
uint8_t *thumbnail;
|
|
|
|
enable_turbo(true);
|
|
|
|
bool ok = camera_snapshot();
|
|
|
|
if (ok) ok = jpeg_thumbnail(jpeg, &thumbnail); // 4060ms without turbo, 205ms with turbo
|
|
|
|
if (ok) {
|
|
|
|
if (flash_erase_sector(ADDR_JPEGIMAGE(config.save_page))) {
|
|
|
|
flash_program(ADDR_JPEGIMAGE(config.save_page), jpeg, img.length);
|
|
|
|
}
|
|
|
|
if (flash_erase_sector(ADDR_THUMBNAIL(config.save_page))) {
|
|
|
|
flash_program(ADDR_THUMBNAIL(config.save_page), thumbnail, 80*60*3);
|
|
|
|
flash_program(ADDR_FLASHINFO(config.save_page), (uint8_t*)(&img), sizeof(img));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enable_turbo(false);
|
|
|
|
|
|
|
|
if (++config.save_page >= 16) config.save_page = 0;
|
|
|
|
config.save_count--;
|
|
|
|
last = HAL_GetTick(); // update delay with elapsed time
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void main_satcam()
|
|
|
|
{
|
|
|
|
#if ENABLE_SWD_DEBUG
|
|
|
|
/* enable debug access */
|
|
|
|
HAL_DBGMCU_EnableDBGSleepMode();
|
|
|
|
__HAL_DBGMCU_FREEZE_IWDG();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* initialize peripherals */
|
|
|
|
set_led_red(true);
|
|
|
|
|
|
|
|
eeprom_init();
|
|
|
|
flash_init();
|
|
|
|
comm_init(); // last
|
|
|
|
|
|
|
|
config_load_eeprom();
|
|
|
|
|
|
|
|
syslog_event(LOG_BOOT);
|
|
|
|
send_roger();
|
|
|
|
set_led_red(false);
|
|
|
|
|
|
|
|
/* main loop */
|
|
|
|
while (1) {
|
|
|
|
/* tasks */
|
|
|
|
comm_cmd_task();
|
|
|
|
save_task();
|
|
|
|
sampling_task();
|
|
|
|
|
|
|
|
/* watchdog reset */
|
|
|
|
HAL_IWDG_Refresh(&hiwdg);
|
|
|
|
}
|
|
|
|
}
|