Major update, work with AXP192 power control chip thus the new T-Beam V1.0

pull/10/head
Pawel Jalocha 2020-02-24 21:54:21 +00:00
rodzic 60bee324fd
commit 86a24a2913
41 zmienionych plików z 5928 dodań i 934 usunięć

71
FollowMe.cfg 100644
Wyświetl plik

@ -0,0 +1,71 @@
#define DEFAULT_AcftType 1 // [0..15] default aircraft-type: glider
#define DEFAULT_GeoidSepar 40 // [m]
#define DEFAULT_CONbaud 115200
#define DEFAULT_PPSdelay 100
#define DEFAULT_FreqPlan 0
// #define WITH_HELTEC // HELTEC module: PCB LED on GPI025
// #define WITH_TTGO // TTGO module: PCB LED on GPIO2, GPIO25 free to use as DAC2 output
// #define WITH_TBEAM // T-Beam module
// #define WITH_TBEAM_V10 // T-Beam module
// #define WITH_JACEK // JACEK ESP32 OGN-Tracker
// #define WITH_M5_JACEK // JACEK M5 ESP32 OGN-Tracker
#define WITH_FollowMe // by Avionix
// #define WITH_ILI9341 // 320x240 M5stack
// #define WITH_ST7789 // IPS 240x240 ST7789
// #define WITH_TFT_LCD // TFT LCD
// #define WITH_OLED // OLED display on the I2C: some TTGO modules are without OLED display
// #define WITH_OLED2 // 2nd OLED display, I2C address next higher
#define WITH_U8G2_OLED // I2C OLED through the U8g2 library
#define WITH_U8G2_SH1106
#define WITH_RFM95 // RF chip selection: both HELTEC and TTGO use sx1276 which is same as RFM95
// #define WITH_SLEEP // with software sleep mode controlled by the long-press on the button
// #define WITH_AXP // with AXP192 power controller (T-BEAM V1.0)
#define WITH_BQ // with BQ24295 power controller (FollowMe)
// #define WITH_LED_RX
// #define WITH_LED_TX
#define WITH_GPS_ENABLE // use GPS_ENABLE control line to turn the GPS ON/OFF
#define WITH_GPS_PPS // use the PPS signal from GPS for precise time-sync.
#define WITH_GPS_CONFIG // attempt to configure higher GPS baud rate and airborne mode
// #define WITH_GPS_UBX // GPS understands UBX
#define WITH_GPS_MTK // GPS understands MTK
// #define WITH_GPS_SRF
// #define WITH_MAVLINK
// #define WITH_GPS_UBX_PASS // to pass directly UBX packets to/from GPS
// #define WITH_GPS_NMEA_PASS // to pass directly NMEA to/from GPS
// #define WITH_BMP180 // BMP180 pressure sensor
// #define WITH_BMP280 // BMP280 pressure sensor
#define WITH_BME280 // BMP280 with humidity (but still works with BMP280)
// #define WITH_MS5607 // MS5607 pressure sensor
#define WITH_PFLAA // PFLAU and PFLAA for compatibility with XCsoar and LK8000
// #define WITH_POGNT
#define WITH_LOOKOUT
#define WITH_CONFIG // interpret the console input: $POGNS to change parameters
// #define WITH_BEEPER // with digital buzzer
// #define WITH_SOUND // with analog sound produced by DAC on pin 25
// #define WITH_KNOB
// #define WITH_VARIO
#define WITH_SD // use the SD card in SPI mode and FAT file system
#define WITH_SPIFFS // use SPIFFS file system in Flash
#define WITH_LOG // log own positions and other received to SPIFFS and possibly to uSD
#define WITH_BT_SPP // Bluetooth serial port for smartphone/tablet link
// #define WITH_WIFI // attempt to connect to the wifi router for uploading the log files
// #define WITH_SPIFFS_LOG // log transmitted and received packets to SPIFFS
// #define WITH_ENCRYPT // Encrypt (optionally) the position

71
T-Beam_v10.cfg 100644
Wyświetl plik

@ -0,0 +1,71 @@
#define DEFAULT_AcftType 1 // [0..15] default aircraft-type: glider
#define DEFAULT_GeoidSepar 40 // [m]
#define DEFAULT_CONbaud 115200
#define DEFAULT_PPSdelay 100
#define DEFAULT_FreqPlan 0
// #define WITH_HELTEC // HELTEC module: PCB LED on GPI025
// #define WITH_TTGO // TTGO module: PCB LED on GPIO2, GPIO25 free to use as DAC2 output
// #define WITH_TBEAM // T-Beam module
#define WITH_TBEAM_V10 // T-Beam module
// #define WITH_JACEK // JACEK ESP32 OGN-Tracker
// #define WITH_M5_JACEK // JACEK M5 ESP32 OGN-Tracker
// #define WITH_FollowMe // by Avionix
// #define WITH_ILI9341 // 320x240 M5stack
// #define WITH_ST7789 // IPS 240x240 ST7789
// #define WITH_TFT_LCD // TFT LCD
// #define WITH_OLED // OLED display on the I2C: some TTGO modules are without OLED display
// #define WITH_OLED2 // 2nd OLED display, I2C address next higher
// #define WITH_U8G2_OLED // I2C OLED through the U8g2 library
// #define WITH_U8G2_SH1106
#define WITH_RFM95 // RF chip selection: both HELTEC and TTGO use sx1276 which is same as RFM95
// #define WITH_SLEEP // with software sleep mode controlled by the long-press on the button
#define WITH_AXP // with AXP192 power controller (T-BEAM V1.0)
// #define WITH_BQ // with BQ24295 power controller (FollowMe)
// #define WITH_LED_RX
// #define WITH_LED_TX
// #define WITH_GPS_ENABLE // use GPS_ENABLE control line to turn the GPS ON/OFF
#define WITH_GPS_PPS // use the PPS signal from GPS for precise time-sync.
#define WITH_GPS_CONFIG // attempt to configure higher GPS baud rate and airborne mode
#define WITH_GPS_UBX // GPS understands UBX
// #define WITH_GPS_MTK // GPS understands MTK
// #define WITH_GPS_SRF
// #define WITH_MAVLINK
// #define WITH_GPS_UBX_PASS // to pass directly UBX packets to/from GPS
// #define WITH_GPS_NMEA_PASS // to pass directly NMEA to/from GPS
// #define WITH_BMP180 // BMP180 pressure sensor
// #define WITH_BMP280 // BMP280 pressure sensor
#define WITH_BME280 // BMP280 with humidity (but still works with BMP280)
// #define WITH_MS5607 // MS5607 pressure sensor
#define WITH_PFLAA // PFLAU and PFLAA for compatibility with XCsoar and LK8000
// #define WITH_POGNT
#define WITH_LOOKOUT
#define WITH_CONFIG // interpret the console input: $POGNS to change parameters
// #define WITH_BEEPER // with digital buzzer
// #define WITH_SOUND // with analog sound produced by DAC on pin 25
// #define WITH_KNOB
// #define WITH_VARIO
// #define WITH_SD // use the SD card in SPI mode and FAT file system
#define WITH_SPIFFS // use SPIFFS file system in Flash
#define WITH_LOG // log own positions and other received to SPIFFS and possibly to uSD
#define WITH_BT_SPP // Bluetooth serial port for smartphone/tablet link
// #define WITH_WIFI // attempt to connect to the wifi router for uploading the log files
// #define WITH_SPIFFS_LOG // log transmitted and received packets to SPIFFS
// #define WITH_ENCRYPT // Encrypt (optionally) the position

Wyświetl plik

@ -1,3 +1,6 @@
#ifndef __ATMOSPHERE_H__
#define __ATMOSPHERE_H__
#include <math.h>
#include <stdint.h>
@ -56,3 +59,5 @@ class Atmosphere
} ;
#endif // __ATMOSPHERE_H__

265
main/axp192.h 100644
Wyświetl plik

@ -0,0 +1,265 @@
#ifndef __AXP192_H__
#define __AXP192_H__
#include <stdint.h>
class AXP192
{ private:
static const uint8_t AXP192_ADDR = 0x34; // possible I2C addresses
// registers
static const uint8_t REG_STATUS = 0x00; // bit #2 = charge/discharge, bit #0 = power-on triggered by Vbus/ACin
static const uint8_t REG_MODE_CHGSTATUS = 0x01; // charge mode and status
static const uint8_t REG_ID = 0x03;
static const uint8_t REG_LDO234_DC23_CTL = 0x12; // [bit mask] control outputs ON/OFF
static const uint8_t REG_DC1_VOLTAGE = 0x26; // [25mV] controls DC1 voltage
static const uint8_t REG_OFF_CTL = 0x32; // power-off, battery check, LED control
static const uint8_t REG_CHARGE_1 = 0x33; // target voltage, current
static const uint8_t REG_CHARGE_2 = 0x34;
static const uint8_t REG_BACKUP_CHG = 0x35; // backup battery charge
static const uint8_t REG_POK_SET = 0x36; // press-key parameters
static const uint8_t REG_INTEN1 = 0x40; // IRQ enable
static const uint8_t REG_INTEN2 = 0x41;
static const uint8_t REG_INTEN3 = 0x42;
static const uint8_t REG_INTEN4 = 0x43;
static const uint8_t REG_INTEN5 = 0x4A;
static const uint8_t REG_INTSTS1 = 0x44; // IRQ status
static const uint8_t REG_INTSTS2 = 0x45;
static const uint8_t REG_INTSTS3 = 0x46;
static const uint8_t REG_INTSTS4 = 0x47;
static const uint8_t REG_INTSTS5 = 0x4D;
static const uint8_t REG_VBUS_VOLT_H8 = 0x5A; // [1.7mV]
static const uint8_t REG_VBUS_VOLT_L4 = 0x5B;
static const uint8_t REG_VBUS_CURR_H8 = 0x5C; // [0.375mA]
static const uint8_t REG_VBUS_CURR_L4 = 0x5D;
static const uint8_t REG_TEMP_H8 = 0x5E; // [-144.7..264.8/0.1]degC
static const uint8_t REG_TEMP_L4 = 0x5F;
static const uint8_t REG_BAT_VOLT_H8 = 0x78; // [0.0..4504.5/1.1]mV
static const uint8_t REG_BAT_VOLT_L4 = 0x79;
static const uint8_t REG_BAT_INP_CURR_H8 = 0x7A; // [0.5mA]
static const uint8_t REG_BAT_INP_CURR_L5 = 0x7B;
static const uint8_t REG_BAT_OUT_CURR_H8 = 0x7C; // [0.5mA]
static const uint8_t REG_BAT_OUT_CURR_L5 = 0x7D;
static const uint8_t REG_ADC_EN1 = 0x82; // battery voltage enabled by default
static const uint8_t REG_ADC_EN2 = 0x83; // internal temperature enabled by default
static const uint8_t REG_ADC_SPEED = 0x84; // 25Hz by default
static const uint8_t REG_ADC_RANGE = 0x85;
static const uint8_t REG_BAT_INP_CHG = 0xB0;
static const uint8_t REG_BAT_OUT_CHG = 0xB4;
static const uint8_t REG_BAT_MON = 0xB8;
static const uint8_t AXP192_ID = 0x03; // ID for the AXP192
public:
static const uint8_t OUT_DCDC1 = 0; // supply outputs
static const uint8_t OUT_DCDC3 = 1;
static const uint8_t OUT_LDO2 = 2;
static const uint8_t OUT_LDO3 = 3;
static const uint8_t OUT_DCDC2 = 4;
static const uint8_t OUT_EXTEN = 6;
static const uint16_t ADC_TEMP = 0x8000; // ADC sampling inputs
static const uint16_t ADC_BAT_VOLT = 0x0080;
static const uint16_t ADC_BAT_CURR = 0x0040;
static const uint16_t ADC_VBUS_VOLT = 0x0008;
static const uint16_t ADC_VBUS_CURR = 0x0004;
static const uint32_t AXP202_PEK_LONGPRESS_IRQ = 0x00010000;
static const uint32_t AXP202_PEK_SHORTPRESS_IRQ = 0x00020000;
public:
uint8_t Bus; // which I2C bus
uint8_t ADDR; // detected I2C address
uint8_t ID;
public:
uint8_t Error; // error on the I2C bus (0=no error)
uint8_t checkID(void) // check ID, to make sure the AXP192 is reachable
{ ADDR=0;
Error=I2C_Read(Bus, AXP192_ADDR, REG_ID, ID);
if( (!Error) && (ID==AXP192_ID) ) { ADDR=AXP192_ADDR; return 0; }
return 1; } // 0 => no error and correct ID
uint8_t readStatus(void)
{ uint8_t Byte=0; Error=I2C_Read(Bus, ADDR, REG_STATUS, Byte); return Byte; }
uint8_t setPOK(uint8_t Byte=0xDC)
{ Error=I2C_Write(Bus, ADDR, REG_POK_SET, Byte);
return Error; }
uint8_t setPowerOutput(uint8_t Channel, bool ON=1)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_LDO234_DC23_CTL, Byte); if(Error) return Error;
uint8_t Mask=1; Mask<<=Channel;
if(ON) Byte |= Mask;
else Byte &= ~Mask;
Error=I2C_Write(Bus, ADDR, REG_LDO234_DC23_CTL, Byte);
return Error; }
uint8_t setDCDC1(uint16_t mV) // [mV] set voltage on DCDC1 output
{ if(mV>3500) mV=3500;
else if(mV<700) mV=700;
uint8_t Byte = (mV-700+12)/25;
Error=I2C_Write(Bus, ADDR, REG_DC1_VOLTAGE, Byte);
return Error; }
uint8_t ShutDown(void)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_OFF_CTL, Byte); if(Error) return Error;
Byte |= 0x80;
Error=I2C_Write(Bus, ADDR, REG_OFF_CTL, Byte);
return Error; }
uint8_t setLED(uint8_t Mode) // 0=OFF, 1=1Hz, 2=4Hz, 3=ON, 4=reflect charging status
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_OFF_CTL, Byte); if(Error) return Error;
if(Mode<4)
{ Byte |= 0x08; // control LED by software
Byte &= 0xCF;
Byte |= Mode<<4; } // set LED state: 0, 1, 2, 3
else Byte &= 0xF7; // control LED by hardware
Error=I2C_Write(Bus, ADDR, REG_OFF_CTL, Byte);
return Error; }
uint8_t enableADC(uint16_t Mask)
{ Error=I2C_Write(Bus, ADDR, REG_ADC_EN1, Mask ); if(Error) return Error;
Error=I2C_Write(Bus, ADDR, REG_ADC_EN2, Mask>>8); return Error; }
uint16_t readH8L4(uint8_t Reg) // read two-byte H8:L4 value
{ uint8_t Byte[2]; Error=I2C_Read(Bus, ADDR, Reg, Byte, 2); if(Error) return 0;
uint16_t Word = Byte[0]; Word<<=4; Word|=Byte[1]&0x0F; return Word; }
uint16_t readVbusVoltage(void)
{ uint16_t Volt=readH8L4(REG_VBUS_VOLT_H8); return (Volt*17+5)/10; } // [1mV]
uint16_t readVbusCurrent(void)
{ uint16_t Curr=readH8L4(REG_VBUS_CURR_H8); return (Curr*15+20)/40; } // [mA]
uint16_t readBatteryVoltage(void)
{ uint16_t Volt=readH8L4(REG_BAT_VOLT_H8); return (Volt*11+5)/10; } // [mV]
// uint16_t readVbusVoltage(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_VBUS_VOLT_H8, H8); if(Error) return 0;
// uint8_t L4; Error=I2C_Read(Bus, ADDR, REG_VBUS_VOLT_L4, L4); if(Error) return 0;
// uint16_t Volt=H8; Volt<<=4; Volt|=L4&0x0F; return (Volt*17+5)/10; } // [mV]
// uint16_t readVbusCurrent(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_VBUS_CURR_H8, H8); if(Error) return 0;
// uint8_t L4; Error=I2C_Read(Bus, ADDR, REG_VBUS_CURR_L4, L4); if(Error) return 0;
// uint16_t Curr=H8; Curr<<=4; Curr|=L4&0x0F; return (Curr*15+20)/40; } // [1mA]
// uint16_t readBatteryVoltage(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_BAT_VOLT_H8, H8); if(Error) return 0;
// uint8_t L4; Error=I2C_Read(Bus, ADDR, REG_BAT_VOLT_L4, L4); if(Error) return 0;
// uint16_t Volt=H8; Volt<<=4; Volt|=L4&0x0F; return (Volt*11+5)/10; } // [1mV]
int16_t readTemperature(void)
{ int16_t Temp=readH8L4(REG_TEMP_H8); return Temp-1447; } // [0.1degC]
// int16_t readTemperature(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_TEMP_H8, H8); if(Error) return 0;
// uint8_t L4; Error=I2C_Read(Bus, ADDR, REG_TEMP_L4, L4); if(Error) return 0;
// int16_t Temp=H8; Temp<<=4; Temp|=L4&0x0F; return Temp-1447; } // [0.1degC]
uint16_t readH8L5(uint8_t Reg)
{ uint8_t Byte[2]; Error=I2C_Read(Bus, ADDR, Reg, Byte, 2); if(Error) return 0;
uint16_t Word = Byte[0]; Word<<=5; Word|=Byte[1]&0x01F; return Word; }
uint16_t readBatteryInpCurrent(void)
{ uint16_t Curr=readH8L5(REG_BAT_INP_CURR_H8); return Curr/2; } // [mA]
uint16_t readBatteryOutCurrent(void)
{ uint16_t Curr=readH8L5(REG_BAT_OUT_CURR_H8); return Curr/2; } // [mA]
// uint16_t readBatteryInpCurrent(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_BAT_INP_CURR_H8, H8); if(Error) return 0;
// uint8_t L5; Error=I2C_Read(Bus, ADDR, REG_BAT_INP_CURR_L5, L5); if(Error) return 0;
// uint16_t Curr=H8; Curr<<=5; Curr|=L5&0x1F; return Curr/2; } // [1mA]
// uint16_t readBatteryOutCurrent(void)
// { uint8_t H8; Error=I2C_Read(Bus, ADDR, REG_BAT_OUT_CURR_H8, H8); if(Error) return 0;
// uint8_t L5; Error=I2C_Read(Bus, ADDR, REG_BAT_OUT_CURR_L5, L5); if(Error) return 0;
// uint16_t Curr=H8; Curr<<=5; Curr|=L5&0x1F; return Curr/2; } // [1mA]
uint16_t read32bit(uint8_t Reg)
{ uint8_t Byte[4]; Error=I2C_Read(Bus, ADDR, Reg, Byte, 4); if(Error) return 0;
uint32_t Word=Byte[0];
for(uint8_t Idx=1; Idx<4; Idx++)
{ Word<<=8; Word|=Byte[Idx]; }
return Word; }
uint32_t readBatteryInpCharge(void) // [0x8000/25 mAs]
{ uint32_t Charge = read32bit(REG_BAT_INP_CHG); return Charge; }
uint32_t readBatteryOutCharge(void) // [0x8000/25 mAs]
{ uint32_t Charge = read32bit(REG_BAT_OUT_CHG); return Charge; }
uint8_t setBatMon(uint8_t Byte=0x80)
{ Error=I2C_Write(Bus, ADDR, REG_BAT_MON, Byte); return Error; }
uint8_t enableBatMon(void) { return setBatMon(0x80); }
uint8_t disableBatMon(void) { return setBatMon(0x00); }
uint8_t stopBatMon(void) { return setBatMon(0xC0); }
uint8_t clearBatMon(void) { return setBatMon(0xA0); }
bool isBatteryConnected(void)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_MODE_CHGSTATUS, Byte); if(Error) return 0;
return Byte&0x20; } // 1=battery connected, 0=no battery connected
bool isBatteryCharging(void)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_MODE_CHGSTATUS, Byte); if(Error) return 0;
return Byte&0x40; } // 1=charging, 0=charge finished or not charging
bool isBatteryActive(void)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_MODE_CHGSTATUS, Byte); if(Error) return 0;
return Byte&0x08; } // 1=active
bool isChargeNotEnough(void)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_MODE_CHGSTATUS, Byte); if(Error) return 0;
return Byte&0x04; } // 1=not enough charging current
uint8_t enableIRQ(uint32_t IrqMask, bool Enable=1)
{ for(uint8_t Idx=0; Idx<4; Idx++)
{ uint8_t Mask = IrqMask;
if(Mask)
{ uint8_t Byte;
Error=I2C_Read(Bus, ADDR, REG_INTEN1+Idx, Byte); // if(Error) continue;
if(Enable) Byte |= Mask;
else Byte &= ~Mask;
Error=I2C_Write(Bus, ADDR, REG_INTEN1+Idx, Byte); // if(Error) continue;
}
IrqMask>>=8;
}
return Error; }
bool readLongPressIRQ(void)
{ uint8_t Byte=0;
Error=I2C_Read(Bus, ADDR, REG_INTSTS1+2, Byte); if(Error) return 0;
return Byte&0x01; }
bool readShortPressIRQ(void)
{ uint8_t Byte=0;
Error=I2C_Read(Bus, ADDR, REG_INTSTS1+2, Byte); if(Error) return 0;
return Byte&0x02; }
uint8_t clearIRQ(void)
{ uint8_t Byte=0xFF;
for(uint8_t Idx=0; Idx<4; Idx++)
{ Error=I2C_Write(Bus, ADDR, REG_INTSTS1+Idx, Byte); }
Error=I2C_Write(Bus, ADDR, REG_INTSTS5, Byte);
return Error; }
} ;
#endif // __AXP192_H__

72
main/bq24295.h 100644
Wyświetl plik

@ -0,0 +1,72 @@
#ifndef __BQ24295_H__
#define __BQ24295_H__
#include <stdint.h>
class BQ24295
{ private:
static const uint8_t BQ24295_ADDR = 0x6B; // possible I2C addresses
static const uint8_t REG_SOURCE = 0x00;
static const uint8_t REG_POWERON = 0x01;
static const uint8_t REG_CHG_CURR = 0x02; // charging current
static const uint8_t REG_PRE_CHG = 0x03; // pre-/post- charge current
static const uint8_t REG_CHG_VOLT = 0x04;
static const uint8_t REG_TIMER_CTRL = 0x05;
static const uint8_t REG_BOOST = 0x06;
static const uint8_t REG_MISC = 0x07;
static const uint8_t REG_STATUS = 0x08; //
static const uint8_t REG_FAULT = 0x09;
static const uint8_t REG_ID = 0x0A; // reads 0x23 but should read 0xC0
static const uint8_t BQ24295_ID = 0xC0; // ID for the BQ24295, but the chip returns 0x23 ?
public:
uint8_t Bus; // which I2C bus
uint8_t ADDR; // detected I2C address
uint8_t ID;
public:
uint8_t Error; // error on the I2C bus (0=no error)
uint8_t readReg(uint8_t Reg)
{ uint8_t Byte=0; Error=I2C_Read(Bus, ADDR, Reg, Byte); return Byte; }
uint8_t readStatus(void) { return readReg(REG_STATUS); } // VVCCDPTS VV=Vbus status, CC=Charge status, D=DPM, P=Power-Good, T=Thermal reg., S=Vsys-min reg.
uint8_t readFault (void) { return readReg(REG_FAULT); } // WOCCBrNN W=Watchdog, O=OTG, CC=charge fault, NN=NTC(temperature)
uint8_t readSource(void) { return readReg(REG_SOURCE); } //
uint8_t readID (void) { return readReg(REG_ID); }
uint8_t checkID(void) // check ID, to make sure the BQ24295 is reachable
{ ADDR=0;
Error=I2C_Read(Bus, BQ24295_ADDR, REG_ID, ID);
if( (!Error) /* && (ID==BQ24295_ID) */ ) { ADDR=BQ24295_ADDR; return 0; }
return 1; } // 0 => no error and correct ID
uint8_t writeSource(uint8_t Byte=0x05) // disable, 3.88V, 1.5A
{ Error=I2C_Write(Bus, ADDR, REG_SOURCE, Byte);
return Error; }
uint8_t writePowerON(uint8_t Byte=0x3F) // OTG enable, charge enable, 3.7V
{ Error=I2C_Write(Bus, ADDR, REG_POWERON, Byte);
return Error; }
uint8_t writeChargeCurr(uint8_t Byte=0x00) // 512mA
{ Error=I2C_Write(Bus, ADDR, REG_CHG_CURR, Byte);
return Error; }
uint8_t writePreCharge(uint8_t Byte=0x00) // 128mA, 128mA
{ Error=I2C_Write(Bus, ADDR, REG_PRE_CHG, Byte);
return Error; }
uint8_t writeChargeVolt(uint8_t Byte=0x9A) // 4.112V, 3.0V, 100mV
{ Error=I2C_Write(Bus, ADDR, REG_CHG_VOLT, Byte);
return Error; }
uint8_t writeTimerCtrl(uint8_t Byte=0x8C) // disable watchdog
{ Error=I2C_Write(Bus, ADDR, REG_TIMER_CTRL, Byte);
return Error; }
} ;
#endif // __BQ24295_H__

Wyświetl plik

@ -1,19 +1,31 @@
#define DEFAULT_AcftType 1 // [0..15] default aircraft-type: glider
#define DEFAULT_GeoidSepar 40 // [m]
#define DEFAULT_CONbaud 115200
#define DEFAULT_PPSdelay 80
#define DEFAULT_PPSdelay 100
#define DEFAULT_FreqPlan 0
// #define WITH_HELTEC // HELTEC module: PCB LED on GPI025
#define WITH_TTGO // TTGO module: PCB LED on GPIO2, GPIO25 free to use as DAC2 output
// #define WITH_TTGO // TTGO module: PCB LED on GPIO2, GPIO25 free to use as DAC2 output
// #define WITH_TBEAM // T-Beam module
#define WITH_TBEAM_V10 // T-Beam module
// #define WITH_JACEK // JACEK ESP32 OGN-Tracker
// #define WITH_M5_JACEK // JACEK M5 ESP32 OGN-Tracker
// #define WITH_FollowMe // by Avionix
#define WITH_RFM95
// #define WITH_RFM69
#define WITH_OLED // OLED display on the I2C: some TTGO modules are without OLED display
// #define WITH_ILI9341 // 320x240 M5stack
// #define WITH_ST7789 // IPS 240x240 ST7789
// #define WITH_TFT_LCD // TFT LCD
// #define WITH_OLED // OLED display on the I2C: some TTGO modules are without OLED display
// #define WITH_OLED2 // 2nd OLED display, I2C address next higher
// #define WITH_U8G2_OLED // I2C OLED through the U8g2 library
// #define WITH_U8G2_SH1106
#define WITH_RFM95 // RF chip selection: both HELTEC and TTGO use sx1276 which is same as RFM95
// #define WITH_SLEEP // with software sleep mode controlled by the long-press on the button
#define WITH_AXP // with AXP192 power controller (T-BEAM V1.0)
// #define WITH_BQ // with BQ24295 power controller (FollowMe)
// #define WITH_LED_RX
// #define WITH_LED_TX
@ -21,6 +33,7 @@
// #define WITH_GPS_ENABLE // use GPS_ENABLE control line to turn the GPS ON/OFF
#define WITH_GPS_PPS // use the PPS signal from GPS for precise time-sync.
#define WITH_GPS_CONFIG // attempt to configure higher GPS baud rate and airborne mode
#define WITH_GPS_UBX // GPS understands UBX
// #define WITH_GPS_MTK // GPS understands MTK
// #define WITH_GPS_SRF
@ -31,20 +44,28 @@
// #define WITH_BMP180 // BMP180 pressure sensor
// #define WITH_BMP280 // BMP280 pressure sensor
// #define WITH_BME280 // BMP280 with humidity (but still works with BMP280)
#define WITH_BME280 // BMP280 with humidity (but still works with BMP280)
// #define WITH_MS5607 // MS5607 pressure sensor
#define WITH_PFLAA // PFLAU and PFLAA for compatibility with XCsoar and LK8000
// #define WITH_POGNT
#define WITH_LOOKOUT
#define WITH_CONFIG // interpret the console input: $POGNS to change parameters
// #define WITH_BEEPER
#define WITH_BEEPER // with digital buzzer
// #define WITH_SOUND // with analog sound produced by DAC on pin 25
// #define WITH_KNOB
// #define WITH_VARIO
// #define WITH_SD // use the SD card in SPI mode and FAT file system
// #define WITH_SPIFFS // use SPIFFS file system in Flash
// #define WITH_LOG // log own positions and other received to SPIFFS and possibly to uSD
#define WITH_SPIFFS // use SPIFFS file system in Flash
#define WITH_LOG // log own positions and other received to SPIFFS and possibly to uSD
#define WITH_BT_SPP // Bluetooth serial port for smartphone/tablet link
// #define WITH_WIFI // attempt to connect to the wifi router for uploading the log files
// #define WITH_SPIFFS_LOG // log transmitted and received packets to SPIFFS
// #define WITH_ENCRYPT // Encrypt (optionally) the position

Wyświetl plik

@ -6,12 +6,14 @@
#include <unistd.h>
#include "esp_system.h"
// #include "esp_sleep.h"
#include "hal.h"
#include "rf.h"
#include "sens.h"
#include "rf.h"
#include "ctrl.h"
#include "proc.h"
#include "log.h"
#include "gps.h"
@ -19,10 +21,17 @@
#include "timesync.h"
#include "format.h"
#include "disp_oled.h"
#include "disp_lcd.h"
// #include "ymodem.h"
// #define DEBUG_PRINT
static char Line[128];
FIFO<uint8_t, 8> KeyBuffer;
// ========================================================================================================================
void PrintTasks(void (*CONS_UART_Write)(char))
@ -53,338 +62,6 @@ void PrintTasks(void (*CONS_UART_Write)(char))
// ========================================================================================================================
#ifdef WITH_OLED
int OLED_DisplayStatus(uint32_t Time, uint8_t LineIdx=0)
{ Format_String(Line , "OGN Tx/Rx ");
Format_HHMMSS(Line+10, Time);
OLED_PutLine(LineIdx++, Line);
Parameters.Print(Line);
OLED_PutLine(LineIdx++, Line);
return 0; }
int OLED_DisplayPosition(GPS_Position *GPS=0, uint8_t LineIdx=2)
{ if(GPS && GPS->isValid())
{ Line[0]=' ';
Format_SignDec(Line+1, GPS->Latitude /60, 6, 4); Line[9]=' ';
Format_UnsDec (Line+10, GPS->Altitude /10, 5, 0); Line[15]='m';
OLED_PutLine(LineIdx , Line);
Format_SignDec(Line, GPS->Longitude/60, 7, 4);
Format_SignDec(Line+10, GPS->ClimbRate, 4, 1);
OLED_PutLine(LineIdx+1, Line);
Format_UnsDec (Line , GPS->Speed, 4, 1); Format_String(Line+5, "m/s ");
Format_UnsDec (Line+10, GPS->Heading, 4, 1); Line[15]='^';
OLED_PutLine(LineIdx+2, Line);
Format_String(Line, "0D/00sat DOP00.0");
Line[0]+=GPS->FixMode; Format_UnsDec(Line+3, GPS->Satellites, 2);
Format_UnsDec(Line+12, (uint16_t)GPS->HDOP, 3, 1);
OLED_PutLine(LineIdx+3, Line);
}
else { OLED_PutLine(LineIdx, 0); OLED_PutLine(LineIdx+1, 0); OLED_PutLine(LineIdx+2, 0); OLED_PutLine(LineIdx+3, 0); }
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' '; Line[9]=' '; }
else Format_String(Line, " ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+10, (uint16_t)GPS->Hour, 2, 0);
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0);
Format_UnsDec (Line+14, (uint16_t)GPS->Sec, 2, 0);
} else Line[10]=0;
OLED_PutLine(LineIdx+4, Line);
Line[0]=0;
if(GPS && GPS->hasBaro)
{ Format_String(Line , "0000.0hPa 00000m");
Format_UnsDec(Line , GPS->Pressure/40, 5, 1);
Format_UnsDec(Line+10, GPS->StdAltitude/10, 5, 0); }
OLED_PutLine(LineIdx+5, Line);
return 0; }
#endif
#ifdef WITH_U8G2
void OLED_PutLine(u8g2_t *OLED, uint8_t LineIdx, const char *Line)
{ if(Line==0) return;
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "OLED_PutLine( ,");
Format_UnsDec(CONS_UART_Write, (uint16_t)LineIdx);
CONS_UART_Write(',');
Format_String(CONS_UART_Write, Line);
Format_String(CONS_UART_Write, ")\n");
xSemaphoreGive(CONS_Mutex);
#endif
// u8g2_SetFont(OLED, u8g2_font_5x8_tr);
u8g2_SetFont(OLED, u8g2_font_amstrad_cpc_extended_8r);
u8g2_DrawStr(OLED, 0, (LineIdx+1)*8, Line);
}
void OLED_DrawStatus(u8g2_t *OLED, uint32_t Time, uint8_t LineIdx=0)
{ Format_String(Line , "OGN Tx/Rx ");
Format_HHMMSS(Line+10, Time); Line[16]=0;
OLED_PutLine(OLED, LineIdx++, Line);
Parameters.Print(Line); Line[16]=0;
OLED_PutLine(OLED, LineIdx++, Line); }
void OLED_DrawPosition(u8g2_t *OLED, GPS_Position *GPS=0, uint8_t LineIdx=2)
{ if(GPS && GPS->isValid())
{ Line[0]=' ';
Format_SignDec(Line+1, GPS->Latitude /60, 6, 4); Line[9]=' ';
Format_UnsDec (Line+10, GPS->Altitude /10, 5, 0); Line[15]='m';
OLED_PutLine(OLED, LineIdx , Line);
Format_SignDec(Line, GPS->Longitude/60, 7, 4);
Format_SignDec(Line+10, GPS->ClimbRate, 4, 1);
OLED_PutLine(OLED, LineIdx+1, Line);
Format_UnsDec (Line , GPS->Speed, 4, 1); Format_String(Line+5, "m/s ");
Format_UnsDec (Line+10, GPS->Heading, 4, 1); Line[15]='^';
OLED_PutLine(OLED, LineIdx+2, Line);
Format_String(Line, "0D/00sat DOP00.0");
Line[0]+=GPS->FixMode; Format_UnsDec(Line+3, GPS->Satellites, 2);
Format_UnsDec(Line+12, (uint16_t)GPS->HDOP, 3, 1);
OLED_PutLine(OLED, LineIdx+3, Line);
}
// else { OLED_PutLine(OLED, LineIdx, 0); OLED_PutLine(OLED, LineIdx+1, 0); OLED_PutLine(LineIdx+2, 0); OLED_PutLine(LineIdx+3, 0); }
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' '; Line[9]=' '; }
else Format_String(Line, " ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+10, (uint16_t)GPS->Hour, 2, 0);
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0);
Format_UnsDec (Line+14, (uint16_t)GPS->Sec, 2, 0);
} else Line[10]=0;
OLED_PutLine(OLED, LineIdx+4, Line);
Line[0]=0;
if(GPS && GPS->hasBaro)
{ Format_String(Line , "0000.0hPa 00000m");
Format_UnsDec(Line , GPS->Pressure/40, 5, 1);
Format_UnsDec(Line+10, GPS->StdAltitude/10, 5, 0); }
OLED_PutLine(OLED, LineIdx+5, Line);
}
void OLED_DrawGPS(u8g2_t *OLED, GPS_Position *GPS=0) // GPS time, position, altitude
{ // u8g2_SetFont(OLED, u8g2_font_ncenB14_tr);
u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
Len+=Format_String(Line+Len, "GPS ");
if(GPS && GPS->isValid())
{ Line[Len++]='0'+GPS->FixMode; Line[Len++]='D'; Line[Len++]='/';
Len+=Format_UnsDec(Line+Len, GPS->Satellites, 1);
Len+=Format_String(Line+Len, "sat DOP");
Len+=Format_UnsDec(Line+Len, (uint16_t)GPS->HDOP, 2, 1); }
else
{ Len+=Format_String(Line+Len, "(no lock)"); }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 12, Line);
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' ';
} else Format_String(Line, " . . ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+ 9, (uint16_t)GPS->Hour, 2, 0); Line[11]=':';
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0); Line[14]=':';
Format_UnsDec (Line+15, (uint16_t)GPS->Sec, 2, 0);
} else Format_String(Line+9, " : : ");
Line[17]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
Len+=Format_String(Line+Len, "Lat: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Latitude /6, 7, 5);
Line[Len++]=0xB0; }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
Len=0;
Len+=Format_String(Line+Len, "Lon: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Longitude /6, 8, 5);
Line[Len++]=0xB0; }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
Len=0;
Len+=Format_String(Line+Len, "Alt: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Altitude, 4, 1);
Line[Len++]='m'; }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
}
void OLED_DrawRF(u8g2_t *OLED) // RF
{ u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines. 12 pixels/line
uint8_t Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69"); // Type of RF chip used
if(isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272");
#endif
Line[Len++]=':';
Len+=Format_SignDec(Line+Len, (int16_t)Parameters.getTxPower()); // Tx power
Len+=Format_String(Line+Len, "dBm");
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, (int32_t)Parameters.RFchipFreqCorr, 2, 1); // frequency correction
Len+=Format_String(Line+Len, "ppm");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 12, Line);
Len=0;
Len+=Format_String(Line+Len, "Rx:"); //
Len+=Format_SignDec(Line+Len, -5*TRX.averRSSI, 2, 1); // noise level seen by the receiver
Len+=Format_String(Line+Len, "dBm ");
Len+=Format_UnsDec(Line+Len, RX_OGN_Count64); // received packet/min
Len+=Format_String(Line+Len, "/min");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
Len+=Format_SignDec(Line+Len, (int16_t)TRX.chipTemp); // RF chip internal temperature (not calibrated)
// Line[Len++]=0xB0;
// Line[Len++]='C';
Len+=Format_String(Line+Len, "\260C RxFIFO:");
Len+=Format_UnsDec(Line+Len, RF_RxFIFO.Full()); // how many packets wait in the RX queue
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
// u8g2_DrawStr(OLED, 0, 48, RF_FreqPlan.getPlanName());
Len=0;
Len+=Format_String(Line+Len, RF_FreqPlan.getPlanName()); // name of the frequency plan
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, (uint16_t)(RF_FreqPlan.getCenterFreq()/100000), 3, 1); // center frequency
Len+=Format_String(Line+Len, "MHz");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
}
void OLED_DrawBaro(u8g2_t *OLED, GPS_Position *GPS=0)
{ u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
#ifdef WITH_BMP180
Len+=Format_String(Line+Len, "BMP180 ");
#endif
#ifdef WITH_BMP280
Len+=Format_String(Line+Len, "BMP280 ");
#endif
#ifdef WITH_BME280
Len+=Format_String(Line+Len, "BME280 ");
#endif
#ifdef WITH_MS5607
Len+=Format_String(Line+Len, "MS5607 ");
#endif
Len+=Format_UnsDec(Line+Len, GPS->Pressure/4, 5, 2);
Len+=Format_String(Line+Len, "Pa");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 12, Line);
Len=0;
Len+=Format_UnsDec(Line+Len, GPS->StdAltitude, 5, 1);
Len+=Format_String(Line+Len, "m ");
Len+=Format_SignDec(Line+Len, GPS->ClimbRate, 2, 1);
Len+=Format_String(Line+Len, "m/s");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
Len+=Format_SignDec(Line+Len, GPS->Temperature, 2, 1);
Line[Len++]=0xB0;
Line[Len++]='C';
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, GPS->Humidity, 2, 1);
Line[Len++]='%';
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
}
void OLED_DrawSystem(u8g2_t *OLED)
{
u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
Len+=Format_String(Line+Len, "GPS ");
#ifdef WITH_GPS_UBX
Len+=Format_String(Line+Len, "UBX ");
#endif
#ifdef WITH_GPS_MTK
Len+=Format_String(Line+Len, "MTK ");
#endif
#ifdef WITH_GPS_SRF
Len+=Format_String(Line+Len, "SRF ");
#endif
Len+=Format_UnsDec(Line+Len, GPS_getBaudRate(), 1);
Len+=Format_String(Line+Len, "bps");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 12, Line);
Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69 v"); // Type of RF chip used
if(isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95 v");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272 v");
#endif
Len+=Format_Hex(Line+Len, TRX.chipVer);
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, (int16_t)TRX.chipTemp);
Line[Len++]=0xB0;
Line[Len++]='C';
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
#ifdef WITH_BMP180
Len+=Format_String(Line+Len, "BMP180 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BMP280
Len+=Format_String(Line+Len, "BMP280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BME280
Len+=Format_String(Line+Len, "BME280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_MS5607
Len+=Format_String(Line+Len, "MS5607 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
#ifdef WITH_SD
Len=0;
Len += Format_String(Line+Len, "SD ");
if(SD_isMounted())
{ Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectors());
Line[Len++]='x';
Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectorSize()*5/512, 2, 1);
Len+=Format_String(Line+Len, "KB"); }
else
{ Len+=Format_String(Line+Len, "none"); }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
#endif
}
void OLED_DrawID(u8g2_t *OLED)
{ u8g2_SetFont(OLED, u8g2_font_9x15_tr);
strcpy(Line, "ID: "); Parameters.Print(Line+4); Line[14]=0;
u8g2_DrawStr(OLED, 0, 20, Line);
u8g2_SetFont(OLED, u8g2_font_10x20_tr);
#ifdef WITH_FollowMe
u8g2_DrawStr(OLED, 8, 40, "FollowMe868");
u8g2_DrawStr(OLED, 16, 60, "by AVIONIX");
#endif
}
#endif
// ========================================================================================================================
@ -457,6 +134,21 @@ static void ProcessNMEA(void) // process a valid NMEA that got to the consol
#endif
}
static uint8_t Verbose=0;
static uint32_t VerboseSuspendTime=0;
const uint32_t VerboseSuspendTimeout=60;
static void CheckCtrlV(void)
{ if(VerboseSuspendTime==0) return;
uint32_t Time = TimeSync_Time()-VerboseSuspendTime;
if(Time>VerboseSuspendTimeout) { Parameters.Verbose=Verbose; VerboseSuspendTime=0; }
}
static void ProcessCtrlV(void)
{ if(VerboseSuspendTime) { Parameters.Verbose=Verbose; VerboseSuspendTime=0; return; }
Verbose = Parameters.Verbose; VerboseSuspendTime=TimeSync_Time(); Parameters.Verbose=0;
}
static void ProcessCtrlC(void) // print system state to the console
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Parameters.Print(Line);
@ -532,13 +224,107 @@ static void ProcessCtrlL(void) // print syste
#endif
}
void SleepIn(void)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "Sleep-in\n");
xSemaphoreGive(CONS_Mutex);
#ifdef WITH_GPS_UBX
#ifdef WITH_GPA_ENA
GPS_DISABLE();
#endif
UBX_RXM_PMREQ PMREQ;
PMREQ.duration = 0;
PMREQ.flags = 0x01;
UBX_RxMsg::Send(0x02, 0x41, GPS_UART_Write, (uint8_t *)(&PMREQ), sizeof(PMREQ));
#endif
#ifdef WITH_GPS_MTK
#ifdef WITH_GPA_ENA
GPS_DISABLE();
Format_String(GPS_UART_Write, "$PMTK225,4*2F\r\n"); // backup mode: 7uA
#else
Format_String(GPS_UART_Write, "$PMTK161,0*28\r\n"); // standby mode: 1mA
#endif
#endif
#ifdef WITH_OLED
OLED_DisplayON(0);
#endif
#ifdef WITH_U8G2_OLED
u8g2_SetPowerSave(&U8G2_OLED, 1);
#endif
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
LCD_SetBacklightLevel(0);
#endif
PowerMode=0;
vTaskDelay(1000); }
void SleepOut(void)
{
#ifdef WITH_GPS_ENABLE
GPS_DISABLE();
#endif
PowerMode=2;
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
LCD_SetBacklightLevel(6);
#endif
#ifdef WITH_U8G2_OLED
u8g2_SetPowerSave(&U8G2_OLED, 0);
#endif
#ifdef WITH_OLED
OLED_DisplayON(1);
#endif
#ifdef WITH_GPS_UBX
#ifdef WITH_GPS_ENABLE
GPS_ENABLE();
#endif
Format_String(GPS_UART_Write, "\n");
#endif
#ifdef WITH_GPS_MTK
#ifdef WITH_GPS_ENABLE
GPS_ENABLE();
#else
Format_String(GPS_UART_Write, "$PMTK161,1*29\r\n");
#endif
#endif
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "Sleep-out\n");
xSemaphoreGive(CONS_Mutex); }
#ifdef WITH_SLEEP
static TickType_t LowBatt_Time=0;
static void LowBatt_Watch(void) // check battery voltage
{ uint16_t BattVolt = BatteryVoltage>>8; // [mV]
if(BattVolt>=3250 || BattVolt<=700 ) { LowBatt_Time=0; return; }
int16_t BattRate = (BatteryVoltageRate+128)>>8; // [mv/sec]
if(BattRate>0) { LowBatt_Time=0; return; }
TickType_t Now=xTaskGetTickCount();
if(LowBatt_Time==0) { LowBatt_Time=Now; return; }
Now-=LowBatt_Time;
if(Now>=30000) // if low battery and voltage dropping persists for 30sec
{ SleepIn(); //
Sleep(); // enter sleep
SleepOut(); // wake up
LowBatt_Time=0; } // reset time counter
}
#endif
static void ProcessInput(void)
{ for( ; ; )
{ uint8_t Byte; int Err=CONS_UART_Read(Byte); if(Err<=0) break; // get byte from console, if none: exit the loop
#ifndef WITH_GPS_UBX_PASS
if(Byte==0x03) ProcessCtrlC(); // if Ctrl-C received
if(Byte==0x0C) ProcessCtrlL(); // if Ctrl-C received
if(Byte==0x03) ProcessCtrlC(); // if Ctrl-C received: print parameters
if(Byte==0x0C) ProcessCtrlL(); // if Ctrl-L received: list log files
if(Byte==0x16) ProcessCtrlV(); // if Ctrl-L received: suspend (verbose) printout
if(Byte==0x18) esp_restart() ; // if Ctrl-X received then restart
// if(Byte==0x19) Shutdown(); // if Ctrl-Y receiver: shutdown
#endif
NMEA.ProcessByte(Byte); // pass the byte through the NMEA processor
if(NMEA.isComplete()) // if complete NMEA:
@ -560,56 +346,164 @@ static void ProcessInput(void)
// ========================================================================================================================
const uint8_t OLED_Pages = 6;
static uint8_t OLED_Page = 1;
extern "C"
void vTaskCTRL(void* pvParameters)
{ uint32_t PrevTime=0;
{
uint32_t PrevTime=0;
GPS_Position *PrevGPS=0;
for( ; ; )
for( ; ; ) //
{ ProcessInput(); // process console input
#ifdef WITH_SLEEP
#if defined(WITH_FollowMe) || defined(WITH_TBEAM)
LowBatt_Watch();
#endif
#endif
vTaskDelay(1); //
#ifdef WITH_AXP
bool PowerOffRequest = AXP.readLongPressIRQ() /* || AXP.readShortPressIRQ() */ ;
if(PowerOffRequest)
{ PowerMode=0;
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "Power-Off Request\n");
xSemaphoreGive(CONS_Mutex);
// vTaskDelay(1000);
AXP.setLED(4);
#ifdef WITH_OLED
OLED_DisplayON(0);
#endif
#ifdef WITH_U8G2_OLED
u8g2_SetPowerSave(&U8G2_OLED, 1);
#endif
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
LCD_SetBacklightLevel(0);
#endif
AXP.setPowerOutput(AXP.OUT_LDO2, 0); // turn off RFM power
AXP.setPowerOutput(AXP.OUT_LDO3, 0); // turn off GPS power
AXP.setPowerOutput(AXP.OUT_DCDC1, 0);
// AXP.setPowerOutput(AXP.OUT_DCDC2, 0);
// AXP.setPowerOutput(AXP.OUT_DCDC3, 0);
// AXP.setPowerOutput(AXP.OUT_EXTEN, 0);
AXP.ShutDown();
vTaskDelay(1000);
// #define PIN_AXP_IRQ GPIO_NUM_35
// esp_sleep_enable_ext0_wakeup(PIN_AXP_IRQ, 0); // 1 = High, 0 = Low
// esp_deep_sleep_start();
}
bool ShortPress = AXP.readShortPressIRQ();
if(ShortPress)
{ KeyBuffer.Write(0x04);
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "AXP short-press\n");
xSemaphoreGive(CONS_Mutex);
#endif
AXP.clearIRQ(); }
#endif
LED_TimerCheck(1); // update the LED flashes
#ifdef WITH_BEEPER
Play_TimerCheck(); // update the LED flashes
#endif
bool PageChange=0;
int32_t PressRelease=Button_TimerCheck();
int32_t PressRelease=Button_TimerCheck(); // 0 = no change
// #ifdef DEBUG_PRINT
if(PressRelease!=0)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "PressRelease = ");
Format_String(CONS_UART_Write, "Button: ");
Format_SignDec(CONS_UART_Write, PressRelease);
Format_String(CONS_UART_Write, "ms\n");
xSemaphoreGive(CONS_Mutex); }
// #endif
if(PressRelease>0)
{ if(PressRelease<=300) // short button push: switch pages
{ OLED_Page++; if(OLED_Page>=OLED_Pages) OLED_Page=0;
PageChange=1; }
else if(PressRelease<=2000) // long button push: some page action
{ if(PressRelease<=700) // short button push: switch pages
{ KeyBuffer.Write(0x01); }
else if(PressRelease<=3000) // longer button push: some page action
{ KeyBuffer.Write(0x41); }
else // very long push
{ }
}
uint32_t Time=TimeSync_Time();
GPS_Position *GPS = GPS_getPosition();
bool TimeChange = Time!=PrevTime;
uint32_t Sec = (Time-1)%60;
GPS_Position *GPS = GPS_getPosition(Sec);
bool GPSchange = GPS!=PrevGPS;
if( (!TimeChange) && (!GPSchange) ) continue;
PrevTime=Time; PrevGPS=GPS;
#ifdef WITH_OLED
if(Button_SleepRequest)
{ OLED_DisplayON(0); }
else
{ esp_err_t StatErr=ESP_OK;
esp_err_t PosErr=ESP_OK;
if(TimeChange)
{ StatErr = OLED_DisplayStatus(Time, 0); }
if(GPSchange)
{ PosErr = OLED_DisplayPosition(GPS, 2); }
}
if(TimeChange) CheckCtrlV();
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
if(TimeChange)
{ Format_String(CONS_UART_Write, "TimeChange: ");
// Format_HHMMSS(CONS_UART_Write, Sec);
Format_HHMMSS(CONS_UART_Write, Time);
Format_String(CONS_UART_Write, "\n"); }
if(GPSchange && GPS)
{ Format_String(CONS_UART_Write, "GPSchange: ");
GPS->PrintLine(Line);
Format_String(CONS_UART_Write, Line); }
xSemaphoreGive(CONS_Mutex);
#endif
#ifdef WITH_BQ
#ifdef DEBUG_PRINT
if(TimeChange)
{ uint16_t Batt = BatterySense(2);
// uint8_t ID = BQ.readID();
// uint8_t Status = BQ.readStatus();
// uint8_t Fault = BQ.readFault();
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "BQ24295:");
for(uint8_t Reg=0; Reg<=10; Reg++)
{ uint8_t Val=BQ.readReg(Reg); CONS_UART_Write(' '); Format_Hex(CONS_UART_Write, Val); }
// Format_Hex(CONS_UART_Write, ID);
// CONS_UART_Write('/');
// Format_Hex(CONS_UART_Write, Status);
// CONS_UART_Write('/');
// Format_Hex(CONS_UART_Write, Fault);
Format_String(CONS_UART_Write, " Batt=");
Format_UnsDec(CONS_UART_Write, Batt, 4, 3);
Format_String(CONS_UART_Write, "V\n");
xSemaphoreGive(CONS_Mutex); }
#endif
#endif
#ifdef WITH_AXP
// #ifdef DEBUG_PRINT
if(TimeChange)
{ uint16_t Batt=AXP.readBatteryVoltage(); // [mV]
uint16_t InpCurr=AXP.readBatteryInpCurrent(); // [mA]
uint16_t OutCurr=AXP.readBatteryOutCurrent(); // [mA]
uint16_t Vbus=AXP.readVbusVoltage(); // [mV]
uint16_t VbusCurr=AXP.readVbusCurrent(); // [mA]
int16_t Temp=AXP.readTemperature(); // [0.1degC]
uint32_t InpCharge=AXP.readBatteryInpCharge();
uint32_t OutCharge=AXP.readBatteryOutCharge();
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "AXP192: Batt=");
Format_UnsDec(CONS_UART_Write, Batt, 4, 3);
Format_String(CONS_UART_Write, "V, ");
Format_UnsDec(CONS_UART_Write, InpCurr, 4, 3);
Format_String(CONS_UART_Write, "/");
Format_UnsDec(CONS_UART_Write, OutCurr, 4, 3);
Format_String(CONS_UART_Write, "A, USB: ");
Format_UnsDec(CONS_UART_Write, Vbus, 4, 3);
Format_String(CONS_UART_Write, "V, ");
Format_UnsDec(CONS_UART_Write, VbusCurr, 4, 3);
Format_String(CONS_UART_Write, "A, Charge=");
Format_UnsDec(CONS_UART_Write, ((InpCharge<<12)+562)/1125, 2, 1);
Format_String(CONS_UART_Write, "-");
Format_UnsDec(CONS_UART_Write, ((OutCharge<<12)+562)/1125, 2, 1);
Format_String(CONS_UART_Write, "mAh, Temp=");
Format_SignDec(CONS_UART_Write, Temp, 2, 1);
Format_String(CONS_UART_Write, "degC\n");
xSemaphoreGive(CONS_Mutex); }
// #endif
#endif
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
if(TimeChange)
@ -622,26 +516,6 @@ void vTaskCTRL(void* pvParameters)
Format_String(CONS_UART_Write, "\n"); }
xSemaphoreGive(CONS_Mutex);
#endif
#endif // WITH_OLED
#ifdef WITH_U8G2
if(Button_SleepRequest)
{ u8g2_SetPowerSave(&U8G2_OLED, 0); }
else if(TimeChange || PageChange)
{ u8g2_ClearBuffer(&U8G2_OLED);
switch(OLED_Page)
{ case 2: OLED_DrawGPS (&U8G2_OLED, GPS); break;
case 3: OLED_DrawRF (&U8G2_OLED); break;
case 4: OLED_DrawBaro (&U8G2_OLED, GPS); break;
case 1: OLED_DrawID (&U8G2_OLED); break;
case 5: OLED_DrawSystem(&U8G2_OLED); break;
default:
{ OLED_DrawStatus(&U8G2_OLED, Time, 0);
OLED_DrawPosition(&U8G2_OLED, GPS, 2); }
}
u8g2_SendBuffer(&U8G2_OLED);
}
#endif
#ifdef DEBUG_PRINT // in debug mode print the parameters and state every 60sec
if((Time%60)!=0) continue;

Wyświetl plik

@ -1,5 +1,8 @@
#include "fifo.h"
#include "hal.h"
extern FIFO<uint8_t, 8> KeyBuffer;
#ifdef __cplusplus
extern "C"
#endif

182
main/disp.cpp 100644
Wyświetl plik

@ -0,0 +1,182 @@
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include "esp_system.h"
// #include "esp_sleep.h"
#include "hal.h"
#include "sens.h"
#include "rf.h"
#include "ctrl.h"
#include "proc.h"
#include "log.h"
#include "gps.h"
#include "ubx.h"
#include "timesync.h"
#include "format.h"
#include "disp_oled.h"
#include "disp_lcd.h"
#ifdef WITH_U8G2_OLED
const uint8_t DISP_Pages = 6;
static uint8_t DISP_Page = 1;
#endif
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
const uint8_t DISP_Pages = 9;
static uint8_t DISP_Page = 0;
#endif
extern "C"
void vTaskDISP(void* pvParameters)
{
#ifdef WITH_U8G2_OLED
u8g2_ClearBuffer(&U8G2_OLED);
OLED_DrawLogo(&U8G2_OLED);
u8g2_SendBuffer(&U8G2_OLED);
#endif
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
// LCD_Start();
LCD_LogoPage_Draw();
LCD_SetBacklightLevel(6); // backlight level
#endif
uint32_t PrevTime=0;
GPS_Position *PrevGPS=0;
for( ; ; ) //
{
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
if(PowerMode==0)
{ vTaskDelay(200); LCD_SetBacklightLevel(0); continue; }
#endif
#ifdef WITH_U8G2_OLED
if(PowerMode==0)
{ vTaskDelay(200); /* u8g2_SetPowerSave(&U8G2_OLED, 1); */ continue; }
#endif
vTaskDelay(1); //
bool PageChange = 0;
uint8_t Key = 0;
KeyBuffer.Read(Key); // read key pressed from the buffer
if(Key) PageChange=1; // page-change on any key
uint32_t Time=TimeSync_Time();
bool TimeChange = Time!=PrevTime; // did time change = a new second ?
uint32_t Sec = (Time-1)%60;
GPS_Position *GPS = GPS_getPosition(Sec);
bool GPSchange = GPS!=PrevGPS; // did GPS data change = new position ?
if(!PageChange) // if no page change was requested
{ if( (!TimeChange) && (!GPSchange) ) continue;
PrevTime=Time; PrevGPS=GPS; }
#if defined(WITH_U8G2_OLED) || defined(WITH_ST7789) || defined(WITH_ILI9341)
if(PageChange) DISP_Page++; if(DISP_Page>=DISP_Pages) DISP_Page=0;
#endif
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
static uint8_t LCD_Backlight = 8*16+8;
const uint8_t LCD_BacklightLimit=4*16+8; // lower limit for the backlight
#ifdef WITH_AXP
uint16_t Vbus=AXP.readVbusVoltage(); // external supply (USB) voltage
if(PageChange || Vbus>=4000) LCD_Backlight=8*16+8; // high backlight on page-change or when charging
#else
if(PageChange) LCD_Backlight=8*16+8; // high backlight on page change
#endif
switch(DISP_Page)
{ case 0: if(PageChange) LCD_LogoPage_Draw(Time, GPS); // logo with basic information
LCD_LogoPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 1: if(PageChange) LCD_GPSpage_Draw(Time, GPS); // GPS data
LCD_GPSpage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 2: if(PageChange) LCD_RFpage_Draw(Time, GPS); // RF data
LCD_RFpage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 3: if(PageChange) LCD_BaroPage_Draw(Time, GPS); // Baro data
LCD_BaroPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 4: if(PageChange) LCD_BattPage_Draw(Time, GPS); // Battery
LCD_BattPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 5: if(PageChange) LCD_ParmPage_Draw(Time, GPS); // ID and parameters
LCD_ParmPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 6: if(PageChange) LCD_RelayPage_Draw(Time, GPS); // RELAY list
LCD_RelayPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 7: if(PageChange) LCD_LookPage_Draw(Time, GPS); // Look targets
LCD_LookPage_Update(Time, GPS, TimeChange, GPSchange);
break;
case 8: if(PageChange) LCD_SysPage_Draw(Time, GPS); // System overview
LCD_SysPage_Update(Time, GPS, TimeChange, GPSchange);
break;
}
if(TimeChange) // on each new second
{ LCD_SetBacklightLevel(LCD_Backlight/16); //
if(LCD_Backlight>LCD_BacklightLimit) LCD_Backlight--; } // reduce backlight a lttle if above minimum
#endif // if WITH_ST7789 or WITH_ILI9341
#ifdef WITH_OLED
// if(Button_SleepRequest)
// { OLED_DisplayON(0); }
// else
{ esp_err_t StatErr=ESP_OK;
esp_err_t PosErr=ESP_OK;
if(TimeChange)
{ StatErr = OLED_DisplayStatus(Time, 0); }
if(GPSchange)
{ PosErr = OLED_DisplayPosition(GPS, 2); }
}
#endif // WITH_OLED
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
if(TimeChange)
{ Format_String(CONS_UART_Write, "TimeChange: ");
// Format_SignDec(CONS_UART_Write, StatErr);
Format_String(CONS_UART_Write, "\n"); }
if(GPSchange)
{ Format_String(CONS_UART_Write, "GPSchange: ");
// Format_SignDec(CONS_UART_Write, PosErr);
Format_String(CONS_UART_Write, "\n"); }
xSemaphoreGive(CONS_Mutex);
#endif
#ifdef WITH_U8G2_OLED
// if(Button_SleepRequest)
// { u8g2_SetPowerSave(&U8G2_OLED, 0); }
// else
if(PageChange || ( GPS?GPSchange:TimeChange) )
{ u8g2_ClearBuffer(&U8G2_OLED);
// if(Look.WarnLevel)
// { OLED_DrawTrafWarn(&U8G2_OLED, GPS); }
// else
{ switch(DISP_Page)
{ case 2: OLED_DrawGPS (&U8G2_OLED, GPS); break;
case 3: OLED_DrawRF (&U8G2_OLED); break;
case 4: OLED_DrawBaro (&U8G2_OLED, GPS); break;
case 1: OLED_DrawID (&U8G2_OLED); break;
case 5: OLED_DrawSystem(&U8G2_OLED); break;
// case 6: OLED_DrawRelay (&U8G2_OLED, GPS); break;
// case 7: OLED_DrawLookout(&U8G2_OLED, GPS); break;
case 0: OLED_DrawBattery(&U8G2_OLED); break;
// case 9: OLED_DrawTrafWarn(&U8G2_OLED, GPS); break;
// default:
// { OLED_DrawStatus(&U8G2_OLED, Time, 0);
// OLED_DrawPosition(&U8G2_OLED, GPS, 2); }
}
}
OLED_DrawStatusBar(&U8G2_OLED, GPS);
u8g2_SendBuffer(&U8G2_OLED);
}
#endif
}
}

7
main/disp.h 100644
Wyświetl plik

@ -0,0 +1,7 @@
#include "hal.h"
#ifdef __cplusplus
extern "C"
#endif
void vTaskDISP(void* pvParameters);

713
main/disp_lcd.cpp 100644
Wyświetl plik

@ -0,0 +1,713 @@
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
// #include "esp_system.h"
// #include "esp_sleep.h"
#include "hal.h"
#include "sens.h"
#include "rf.h"
#include "ctrl.h"
#include "proc.h"
// #include "log.h"
#include "gps.h"
// #include "ubx.h"
// #include "timesync.h"
#include "format.h"
// #include "ymodem.h"
// #define DEBUG_PRINT
// static char Line[128];
// ========================================================================================================================
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
#include "st7789.h"
#include "lcd_battery.h"
// Embedded images
// #ifdef WITH_M5_JACEK
// extern const uint8_t OGN_logo_jpg[] asm("_binary_OGN_logo_320x240_jpg_start");
// extern const uint8_t OGN_logo_end[] asm("_binary_OGN_logo_320x240_jpg_end");
// const int OGN_logo_size = OGN_logo_end-OGN_logo_jpg;
// #else
extern const uint8_t OGN_logo_jpg[] asm("_binary_OGN_logo_240x240_jpg_start");
extern const uint8_t OGN_logo_end[] asm("_binary_OGN_logo_240x240_jpg_end");
const int OGN_logo_size = OGN_logo_end-OGN_logo_jpg;
// #endif
// extern const uint8_t Club_logo_jpg[] asm("_binary_AP_logo_240x240_jpg_start");
// extern const uint8_t Club_logo_end[] asm("_binary_AP_logo_240x240_jpg_end");
// const int Club_logo_size = Club_logo_end-Club_logo_jpg;
#ifdef FOR_CKL
extern const uint8_t Club_logo_jpg[] asm("_binary_CKL_logo_240x240_jpg_start");
extern const uint8_t Club_logo_end[] asm("_binary_CKL_logo_240x240_jpg_end");
const int Club_logo_size = Club_logo_end-Club_logo_jpg;
#endif
// uint8_t LCD_Backlight = 8*16;
static void LCD_UpdateTime(uint32_t Time, GPS_Position *GPS, bool Redraw=0)
{ static char Msg[2][10] = { { 0 }, { 0 } };
static uint16_t Back[2] = { 0, 0 };
static bool Idx = 0;
Back[Idx] = RGB565_LIGHTRED;
if(GPS && GPS->isTimeValid())
{ if(GPS->isDateValid()) { Time=GPS->getUnixTime(); Back[Idx]=RGB565_GREEN; }
else { Time=GPS->getDayTime(); Back[Idx]=RGB565_GREENYELLOW; }
if(GPS->FracSec>=50) Time++; }
uint8_t Len=Format_HHcMMcSS(Msg[Idx], Time); Msg[Idx][Len]=0;
bool Redo = Redraw || Back[Idx]!=Back[Idx^1];
if(Redo) LCD_DrawString(Msg[Idx], LCD_WIDTH-LCD_StringWidth(Msg[Idx])-4, LCD_HEIGHT-LCD_FontHeight(), RGB565_BLACK, Back[Idx]);
else LCD_UpdateString(Msg[Idx], Msg[Idx^1], LCD_WIDTH-LCD_StringWidth(Msg[Idx])-4, LCD_HEIGHT-LCD_FontHeight(), RGB565_BLACK, Back[Idx]);
Idx^=1; }
static LCD_BattSymb BattSymb;
static uint8_t BattCapacity(uint16_t mVolt)
{ if(mVolt>=4100) return 100; // 4.1V or above => full capacity
if(mVolt<=3600) return 0; // 3.6V or below => zero capacity
return (mVolt-3600+2)/5; } // linear dependence (simplified)
static void LCD_UpdateBattery(bool Redraw=0)
{ // static char Volt[2][8] = { { 0 }, { 0 } };
// static uint16_t Back[2] = { 0, 0 };
static uint16_t BattCol[2] = { 0, 0 };
static bool Idx = 0;
uint16_t mVolt = (BatteryVoltage+128)>>8;
// uint16_t Voltage = (mVolt+5)/10; // [0.01V]
uint8_t Capacity = BattCapacity(mVolt); // [mv] => [%]
#ifdef WITH_AXP
static char Curr[2][8] = { { 0 }, { 0 } };
uint16_t InpCurr=AXP.readBatteryInpCurrent(); // [mA]
uint16_t OutCurr=AXP.readBatteryOutCurrent(); // [mA]
int16_t Current = InpCurr-OutCurr;
uint8_t Charging = Current>0;
#endif
/*
Back[Idx] = RGB565_LIGHTRED;
if(Voltage>=350) Back[Idx] = RGB565_YELLOW;
if(Voltage>=365) Back[Idx] = RGB565_GREENYELLOW;
if(Voltage>=375) Back[Idx] = RGB565_GREEN;
if(Voltage>=410) Back[Idx] = RGB565_CYAN;
if(Voltage>=420) Back[Idx] = RGB565_LIGHTBLUE;
if(Voltage>=425) Back[Idx] = RGB565_MAGENTA;
Format_UnsDec(Volt[Idx], Voltage, 3, 2); Volt[Idx][4]='V'; Volt[Idx][5]=0;
bool Redo = Redraw || Back[Idx]!=Back[Idx^1];
if(Redo) LCD_DrawString(Volt[Idx], 4, LCD_HEIGHT-LCD_FontHeight(), RGB565_BLACK, Back[Idx]);
else LCD_UpdateString(Volt[Idx], Volt[Idx^1], 4, LCD_HEIGHT-LCD_FontHeight(), RGB565_BLACK, Back[Idx]);
*/
uint8_t BattLev=(Capacity+10)/20;
#ifdef WITH_AXP
static uint8_t DispLev = 0;
if(Charging) { DispLev++; if(DispLev>5) DispLev = BattLev?BattLev-1:0; }
else { DispLev = BattLev; }
#else
uint8_t &DispLev = BattLev;
#endif
// static uint8_t Level = 0;
const uint16_t LevelCol[6] = { RGB565_RED, RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_GREENYELLOW, RGB565_GREEN };
// const uint16_t LevelCol[6] = { RGB565_RED, RGB565_RED, RGB565_DARKORANGE, RGB565_DARKYELLOW, RGB565_DARKGREENYELLOW, RGB565_DARKGREEN };
BattSymb.setLevel(DispLev);
#ifdef PRINT_DEBUG
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "LCD_UpdateBattery() ");
Format_Hex(CONS_UART_Write, BattSymb.CellMap);
CONS_UART_Write('/');
Format_Hex(CONS_UART_Write, BattSymb.Flags);
Format_String(CONS_UART_Write, "\n");
xSemaphoreGive(CONS_Mutex);
#endif
BattCol[Idx]=LevelCol[BattLev];
if(Redraw || BattCol[Idx]!=BattCol[Idx^1])
{ BattSymb.Xpos=0; BattSymb.Ypos=LCD_HEIGHT-BattSymb.Width;
BattSymb.FrameCol=RGB565_DARKGREY; BattSymb.FillCol=RGB565_LIGHTGREY; BattSymb.CellCol=BattCol[Idx];
if(BattSymb.CellCol==RGB565_RED) BattSymb.FrameCol=RGB565_RED;
BattSymb.Draw(); }
else
BattSymb.Update();
// Level+=1; if(Level>5) Level=0;
#ifndef FOR_CKL
#ifdef WITH_AXP
strcpy(Curr[Idx], " mA ");
Format_SignDec(Curr[Idx], Current, 3);
bool Redo = Redraw || Curr[Idx][0]!=Curr[Idx^1][0];
if(Redo)
LCD_DrawString(Curr[Idx], 0, LCD_HEIGHT-LCD_FontHeight()-LCD_FontHeight(tft_Dejavu18), RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
else
LCD_UpdateString(Curr[Idx], Curr[Idx^1], 0, LCD_HEIGHT-LCD_FontHeight()-LCD_FontHeight(tft_Dejavu18), RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
#endif
#endif
Idx^=1; }
static void LCD_UpdateRX(bool Redraw=0)
{ static char Count[2][12] = { { 0 }, { 0 } };
static char Noise[2][12] = { { 0 }, { 0 } };
static bool Idx = 0;
uint8_t Len=0;
Len+=Format_String(Count[Idx]+Len, " Rx:"); //
Len+=Format_UnsDec(Count[Idx]+Len, RX_OGN_Count64); // received packet/min
Len+=Format_String(Count[Idx]+Len, "/min");
Count[Idx][Len]=0;
if(Redraw) LCD_DrawString(Count[Idx], LCD_WIDTH-LCD_StringWidth(Count[Idx], tft_Dejavu18), 4, RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
else LCD_UpdateString(Count[Idx], Noise[Idx^1], LCD_WIDTH-LCD_StringWidth(Count[Idx], tft_Dejavu18), 4, RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
Len=0;
Len+=Format_String(Noise[Idx]+Len, " ");
Len+=Format_SignDec(Noise[Idx]+Len, -5*TRX.averRSSI, 4, 1); // noise level seen by the receiver
Len+=Format_String(Noise[Idx]+Len, "dBm");
Noise[Idx][Len]=0;
if(Redraw) LCD_DrawString(Noise[Idx], LCD_WIDTH-LCD_StringWidth(Noise[Idx], tft_Dejavu18), 4+LCD_FontHeight(tft_Dejavu18), RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
else LCD_UpdateString(Noise[Idx], Noise[Idx^1], LCD_WIDTH-LCD_StringWidth(Noise[Idx], tft_Dejavu18), 4+LCD_FontHeight(tft_Dejavu18), RGB565_BLACK, RGB565_WHITE, tft_Dejavu18);
Idx^=1; }
static void LCD_UpdateGPS(GPS_Position *GPS, bool Redraw=0)
{ static char Sat[2][8] = { { 0 }, { 0 } };
static uint16_t Back[2] = { 0, 0 };
// static char SNR[2][8] = { { 0 }, { 0 } };
static char Alt[2][8] = { { 0 }, { 0 } };
static bool Idx = 0;
Back[Idx] = RGB565_LIGHTRED;
if(GPS)
{ uint16_t Sats = 0;
if(GPS->isValid()) Sats = GPS->Satellites;
if(Sats>=5) Back[Idx] = RGB565_GREEN;
else if(Sats>=4) Back[Idx] = RGB565_GREENYELLOW;
else if(Sats>=3) Back[Idx] = RGB565_YELLOW;
if(GPS->Sec&3) { Format_UnsDec(Sat[Idx], Sats, 2); Format_String(Sat[Idx]+2, "sat"); }
else { Format_UnsDec(Sat[Idx], (GPS_SatSNR+2)/4, 2); Format_String(Sat[Idx]+2, "dB "); }
Sat[Idx][5]=0; }
else // no data from GPS ?
{ Format_String(Sat[Idx], "GPS ?"); Sat[Idx][5]=0; }
bool Redo = Redraw || Back[Idx]!=Back[Idx^1];
if(Redo) LCD_DrawString(Sat[Idx], 0, 0, RGB565_BLACK, Back[Idx]);
else LCD_UpdateString(Sat[Idx], Sat[Idx^1], 0, 0, RGB565_BLACK, Back[Idx]);
if(GPS && GPS->isValid())
{ int32_t Altitude=GPS->Altitude; if(Altitude<0) Altitude=0; Altitude=(Altitude+5)/10;
uint8_t Len=Format_UnsDec(Alt[Idx], (uint32_t)Altitude);
Len+=Format_String(Alt[Idx]+Len, "m ");
Alt[Idx][Len]=0;
#ifdef FOR_CKL
if(Redraw) LCD_DrawString(Alt[Idx], LCD_WIDTH-6*16, 0, RGB565_BLACK, RGB565_WHITE);
else LCD_UpdateString(Alt[Idx], Alt[Idx^1], LCD_WIDTH-6*16, 0, RGB565_BLACK, RGB565_WHITE);
#else
if(Redraw) LCD_DrawString(Alt[Idx], 0, LCD_FontHeight(), RGB565_BLACK, RGB565_WHITE);
else LCD_UpdateString(Alt[Idx], Alt[Idx^1], 0, LCD_FontHeight(), RGB565_BLACK, RGB565_WHITE);
#endif
}
else
{ }
Idx^=1; }
static void LCD_UpdatePosition(GPS_Position *GPS, bool Redraw=0)
{ static bool Idx = 0;
int PosY = 0;
static char Lock[2][20] = { { 0 }, { 0 } };
if(GPS)
{ strcpy(Lock[Idx], "0/00sat/00dB/00.0 ");
Lock[Idx][0] = '0'+GPS->FixQuality;
Format_UnsDec(Lock[Idx]+2, GPS->Satellites, 2);
Format_UnsDec(Lock[Idx]+8, (GPS_SatSNR+2)/4, 2);
Format_UnsDec(Lock[Idx]+13, GPS->HDOP, 3, 1); }
else
{ strcpy(Lock[Idx], "No data from GPS "); }
if(Redraw) LCD_DrawString(Lock[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else LCD_UpdateString(Lock[Idx], Lock[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
static char Time[2][18] = { { 0 }, { 0 } };
static char Lat [2][18] = { { 0 }, { 0 } };
static char Lon [2][18] = { { 0 }, { 0 } };
static char Alt [2][18] = { { 0 }, { 0 } };
static char Vrt [2][18] = { { 0 }, { 0 } };
static char Spd [2][18] = { { 0 }, { 0 } };
static char Trk [2][18] = { { 0 }, { 0 } };
static char Trn [2][18] = { { 0 }, { 0 } };
strcpy(Time[Idx], "00.00.0000 000000");
strcpy(Lat[Idx], "Lat: __._____ ");
strcpy(Lon[Idx], "Lon: ___._____ ");
strcpy(Alt[Idx], "Alt: ____._m ");
strcpy(Vrt[Idx], "Vrt: __._ m/s ");
strcpy(Spd[Idx], "Spd: __._ m/s ");
strcpy(Trk[Idx], "Trk: ___._ deg ");
strcpy(Trn[Idx], "Trn: __._ deg/s ");
if(GPS)
{ if(GPS->isTimeValid())
{ Format_UnsDec (Time[Idx]+11, (uint16_t)GPS->Hour, 2, 0);
Format_UnsDec (Time[Idx]+13, (uint16_t)GPS->Min, 2, 0);
Format_UnsDec (Time[Idx]+15, (uint16_t)GPS->Sec, 2, 0); }
if(GPS->isDateValid())
{ Format_UnsDec (Time[Idx] , (uint16_t)GPS->Day, 2, 0);
Format_UnsDec (Time[Idx]+ 3, (uint16_t)GPS->Month, 2, 0);
Format_UnsDec (Time[Idx]+ 6, (uint16_t)GPS->Year , 4, 0); }
Format_SignDec(Lat[Idx]+6, GPS->Latitude /6, 7, 5);
Format_SignDec(Lon[Idx]+5, GPS->Longitude/6, 8, 5);
Format_SignDec(Alt[Idx]+6, GPS->Altitude, 5, 1);
Format_UnsDec (Spd[Idx]+5, GPS->Speed, 3, 1);
Format_SignDec(Vrt[Idx]+5, GPS->ClimbRate, 3, 1);
Format_UnsDec (Trk[Idx]+5, GPS->Heading, 4, 1);
Format_SignDec(Trn[Idx]+5, GPS->TurnRate, 3, 1); }
if(Redraw)
LCD_DrawString(Time[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Time[Idx], Time[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw || Lat[Idx][6]!=Lat[Idx^1][6])
LCD_DrawString(Lat[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Lat[Idx], Lat[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw || Lon[Idx][5]!=Lon[Idx^1][5])
LCD_DrawString(Lon[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Lon[Idx], Lon[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw || Alt[Idx][6]!=Alt[Idx^1][6])
LCD_DrawString(Alt[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Alt[Idx], Alt[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw || Vrt[Idx][5]!=Vrt[Idx^1][5])
LCD_DrawString(Vrt[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Vrt[Idx], Vrt[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw)
LCD_DrawString(Spd[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Spd[Idx], Spd[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw)
LCD_DrawString(Trk[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Trk[Idx], Trk[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
if(Redraw || Trn[Idx][5]!=Trn[Idx^1][5])
LCD_DrawString(Trn[Idx], 4, PosY, RGB565_BLACK, RGB565_WHITE);
else
LCD_UpdateString(Trn[Idx], Trn[Idx^1], 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Idx^=1; }
static void LCD_UpdateSys(bool Redraw=0)
{ if(!Redraw) return;
int PosY = 2;
char Line[24];
uint8_t Len=0;
Len+=Format_String(Line+Len, "GPS ");
#ifdef WITH_GPS_UBX
Len+=Format_String(Line+Len, "UBX ");
#endif
#ifdef WITH_GPS_MTK
Len+=Format_String(Line+Len, "MTK ");
#endif
#ifdef WITH_GPS_SRF
Len+=Format_String(Line+Len, "SRF ");
#endif
Len+=Format_UnsDec(Line+Len, GPS_getBaudRate(), 1);
Len+=Format_String(Line+Len, "bps");
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69 v"); // Type of RF chip used
if(Parameters.isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95 v");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272 v");
#endif
Len+=Format_Hex(Line+Len, TRX.chipVer);
// Line[Len++]=' ';
// Len+=Format_SignDec(Line+Len, (int16_t)TRX.chipTemp);
// Line[Len++]=0xB0;
// Line[Len++]='C';
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Len=0;
#ifdef WITH_BMP180
Len+=Format_String(Line+Len, "BMP180 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BMP280
Len+=Format_String(Line+Len, "BMP280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BME280
Len+=Format_String(Line+Len, "BME280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_MS5607
Len+=Format_String(Line+Len, "MS5607 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Len=0;
#ifdef WITH_ST7789
Len+=Format_String(Line+Len, "ST7789 240x240");
#endif
#ifdef WITH_ILI9341
Len+=Format_String(Line+Len, "ILI9341 320x240");
#endif
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
#ifdef WITH_SPIFFS
Len=0;
Len+=Format_String(Line+Len, "SPIFFS ");
size_t Total, Used;
if(SPIFFS_Info(Total, Used)==0) // get the SPIFFS usage summary
{ Len+=Format_UnsDec(Line+Len, (Total-Used)/1024);
Len+=Format_String(Line+Len, "/");
Len+=Format_UnsDec(Line+Len, Total/1024);
Len+=Format_String(Line+Len, "kB"); }
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
#endif
#ifdef WITH_SD
Len=0;
Len+=Format_String(Line+Len, "SD ");
if(SD_isMounted())
{ Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectors());
Line[Len++]='x';
Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectorSize()*5/512, 2, 1);
Len+=Format_String(Line+Len, "KB"); }
else
{ Len+=Format_String(Line+Len, "none"); }
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
#endif
}
static void LCD_UpdateRF(bool Redraw=0)
{ static bool Idx = 0;
if(!Redraw) return;
int PosY = 2;
char Line[24];
uint8_t Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69"); // Type of RF chip used
if(Parameters.isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272");
#endif
Line[Len++]=':'; Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, (int16_t)Parameters.getTxPower()); // Tx power
Len+=Format_String(Line+Len, "dBm");
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Len=0;
Len+=Format_UnsDec(Line+Len, (uint16_t)(RF_FreqPlan.getCenterFreq()/100000), 3, 1); // center frequency
Len+=Format_String(Line+Len, "MHz");
Line[Len++]=' ';
Len+=Format_String(Line+Len, RF_FreqPlan.getPlanName()); // name of the frequency plan
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Len=0;
Len+=Format_SignDec(Line+Len, (int32_t)Parameters.RFchipFreqCorr, 2, 1); // frequency correction
Len+=Format_String(Line+Len, "ppm");
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
Idx^=1; }
static void LCD_UpdatePower(bool Redraw=0)
{ static bool Idx = 0;
static char USB[2][20] = { { 0 }, { 0 } };
Idx^=1; }
static void LCD_UpdateParameters(bool Redraw=0)
{ if(!Redraw) return;
char Line[32];
int PosY = 2;
uint8_t Len=Format_String(Line, "ID=");
Line[Len++]=HexDigit(Parameters.AcftType); Line[Len++]=':';
Line[Len++]=HexDigit(Parameters.AddrType); Line[Len++]=':';
Len+=Format_Hex(Line+Len, Parameters.Address, 6);
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight();
for(uint8_t Idx=0; Idx<Parameters.InfoParmNum; Idx++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
const char *Value = Parameters.InfoParmValue(Idx); if(Value[0]==0) continue;
uint8_t Len=Format_String(Line, OGN_Packet::InfoParmName(Idx));
Line[Len++]='=';
Len+=Format_String(Line+Len, Value);
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight(); }
}
static void LCD_UpdateRelayList(bool Redraw=0)
{ static uint8_t PrevLines=0;
if(Redraw) PrevLines=0;
char Line[24];
int PosY = 2;
uint8_t Lines=0;
for( uint8_t Idx=0; Idx<RelayQueueSize; Idx++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
OGN_RxPacket<OGN_Packet> *Packet = RelayQueue.Packet+Idx; if(Packet->Rank==0) continue;
uint8_t Len=0;
Line[Len++]='0'+Packet->Packet.Header.AddrType;
Line[Len++]=':';
Len+=Format_Hex(Line+Len, Packet->Packet.Header.Address, 6);
Line[Len++]=' ';
Len+=Format_Hex(Line+Len, Packet->Rank);
Line[Len++]=' ';
Line[Len++]=':';
Len+=Format_UnsDec(Line+Len, Packet->Packet.Position.Time, 2);
Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, RGB565_WHITE);
PosY+=LCD_FontHeight(); Lines++; }
for(uint8_t Line=Lines; Line<PrevLines; Line++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
LCD_DrawBox(0, PosY, LCD_WIDTH, LCD_FontHeight(), RGB565_WHITE);
PosY+=LCD_FontHeight(); }
PrevLines=Lines; }
static void LCD_UpdateLookList(bool Redraw=0)
{ static uint8_t PrevLines=0;
if(Redraw) PrevLines=0;
char Line[24];
int PosY = 2;
uint8_t Lines=0;
for( uint8_t Idx=0; Idx<Look.MaxTargets; Idx++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
const LookOut_Target *Tgt = Look.Target+Idx; if(!Tgt->Alloc) continue;
uint16_t Bkg = RGB565_GREEN;
uint8_t Len=0;
Len+=Format_Hex(Line+Len, Tgt->ID, 7);
Line[Len++]=' ';
if(Tgt->DistMargin) continue;
uint8_t Warn = Tgt->WarnLevel;
if(Warn>=3) Bkg = RGB565_LIGHTRED;
else if(Warn==2) Bkg = RGB565_ORANGE;
else if(Warn==1) Bkg = RGB565_YELLOW;
Len+=Format_UnsDec(Line+Len, Tgt->TimeMargin>>1, 2);
Line[Len++]='s'; Line[Len++]=' ';
// Len+=Format_UnsDec(Line+Len, ((Tgt->MissDist>>1)+50)/100, 2, 1);
Len+=Format_UnsDec(Line+Len, ((Tgt->HorDist>>1)+50)/100, 2, 1);
Line[Len++]='k'; Line[Len++]='m'; Line[Len++]=' '; Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, Bkg);
PosY+=LCD_FontHeight(); Lines++; }
for( uint8_t Idx=0; Idx<Look.MaxTargets; Idx++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
const LookOut_Target *Tgt = Look.Target+Idx; if(!Tgt->Alloc) continue;
uint16_t Bkg = RGB565_GREEN;
uint8_t Len=0;
Len+=Format_Hex(Line+Len, Tgt->ID, 7);
Line[Len++]=' ';
if(Tgt->DistMargin==0) continue;
Line[Len++]=' '; Line[Len++]=' '; Line[Len++]=' '; Line[Len++]=' '; Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, ((Tgt->HorDist>>1)+50)/100, 2, 1);
Line[Len++]='k'; Line[Len++]='m'; Line[Len++]=' '; Line[Len]=0;
LCD_DrawString(Line, 4, PosY, RGB565_BLACK, Bkg);
PosY+=LCD_FontHeight(); Lines++; }
for(uint8_t Line=Lines; Line<PrevLines; Line++)
{ if(PosY>(LCD_HEIGHT-2*LCD_FontHeight())) break;
LCD_DrawBox(0, PosY, LCD_WIDTH, LCD_FontHeight(), RGB565_WHITE);
PosY+=LCD_FontHeight(); }
PrevLines=Lines; }
static void LCD_DrawID(void)
{ const uint8_t *Font = tft_Dejavu18;
char ID[16];
uint8_t Len=0;
ID[Len++]=HexDigit(Parameters.AcftType); ID[Len++]=':';
ID[Len++]=HexDigit(Parameters.AddrType); ID[Len++]=':';
Len+=Format_Hex(ID+Len, Parameters.Address, 6);
ID[Len]=0;
int PosX = LCD_WIDTH -LCD_StringWidth(ID, Font) -4;
int PosY = LCD_HEIGHT -LCD_FontHeight(Font) -LCD_FontHeight();
LCD_DrawTranspString(ID, PosX, PosY, RGB565_BLUE, Font);
}
// ------------------------------------------------------------------------------------------------------------------------
void LCD_LogoPage_Draw(void) // Logo page: initial background draw
{ if(LCD_WIDTH!=240) LCD_ClearDisplay(RGB565_WHITE);
#ifdef FOR_CKL
LCD_DrawJPEG(Club_logo_jpg, Club_logo_size, (LCD_WIDTH-240)/2, 0);
#else
// #ifdef WITH_M5_JACEK
// LCD_DrawJPEG( OGN_logo_jpg, OGN_logo_size, (LCD_WIDTH-320)/2, 0);
// #else
LCD_DrawJPEG( OGN_logo_jpg, OGN_logo_size, (LCD_WIDTH-240)/2, 0);
// #endif
LCD_DrawID();
#endif
}
void LCD_LogoPage_Draw(uint32_t Time, GPS_Position *GPS) // Logo page: initial draw with Time/GPS data
{ LCD_LogoPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
#ifndef FOR_CKL
LCD_UpdateRX(1);
#endif
LCD_UpdateGPS(GPS, 1); }
void LCD_LogoPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange) // Logo page: update the data in the corners
{ if(TimeChange)
{ LCD_UpdateTime(Time, GPS);
LCD_UpdateBattery();
#ifndef FOR_CKL
LCD_UpdateRX();
#endif
}
if(GPSchange) { LCD_UpdateGPS(GPS); }
}
void LCD_GPSpage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_GPSpage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_GPSpage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdatePosition(GPS, 1); }
void LCD_GPSpage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
if(GPSchange) LCD_UpdatePosition(GPS); }
void LCD_RFpage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_RFpage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_RFpage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdateRF(1); }
void LCD_RFpage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
if(TimeChange) LCD_UpdateRF(); }
void LCD_BattPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_BattPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_BattPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
}
void LCD_BattPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
}
void LCD_ParmPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_ParmPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_ParmPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdateParameters(1); }
void LCD_ParmPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
LCD_UpdateParameters(); }
void LCD_BaroPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_BaroPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_BaroPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
}
void LCD_BaroPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
}
void LCD_SysPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_SysPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_SysPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdateSys(1); }
void LCD_SysPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
if(TimeChange) LCD_UpdateSys(); }
void LCD_LookPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_LookPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_LookPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdateLookList(1); }
void LCD_LookPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
if(GPSchange) LCD_UpdateLookList(); }
void LCD_RelayPage_Draw(void)
{ LCD_ClearDisplay(RGB565_WHITE); }
void LCD_RelayPage_Draw(uint32_t Time, GPS_Position *GPS)
{ LCD_RelayPage_Draw();
LCD_UpdateTime(Time, GPS, 1); LCD_UpdateBattery(1);
LCD_UpdateRelayList(1); }
void LCD_RelayPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange)
{ if(TimeChange) { LCD_UpdateTime(Time, GPS); LCD_UpdateBattery(); }
if(GPSchange) LCD_UpdateRelayList(); }
// ------------------------------------------------------------------------------------------------------------------------
#endif
// ========================================================================================================================

41
main/disp_lcd.h 100644
Wyświetl plik

@ -0,0 +1,41 @@
#if defined(WITH_ST7789) || defined(WITH_ILI9341)
#include "st7789.h"
void LCD_LogoPage_Draw(void);
void LCD_LogoPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_LogoPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_GPSpage_Draw(void);
void LCD_GPSpage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_GPSpage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_RFpage_Draw(void);
void LCD_RFpage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_RFpage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_BattPage_Draw(void);
void LCD_BattPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_BattPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_ParmPage_Draw(void);
void LCD_ParmPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_ParmPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_BaroPage_Draw(void);
void LCD_BaroPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_BaroPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_SysPage_Draw(void);
void LCD_SysPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_SysPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_LookPage_Draw(void);
void LCD_LookPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_LookPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
void LCD_RelayPage_Draw(void);
void LCD_RelayPage_Draw(uint32_t Time, GPS_Position *GPS);
void LCD_RelayPage_Update(uint32_t Time, GPS_Position *GPS, bool TimeChange, bool GPSchange);
#endif

635
main/disp_oled.cpp 100644
Wyświetl plik

@ -0,0 +1,635 @@
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
// #include "esp_system.h"
// #include "esp_sleep.h"
#include "hal.h"
#include "sens.h"
#include "rf.h"
#include "ctrl.h"
#include "proc.h"
// #include "log.h"
#include "gps.h"
// #include "ubx.h"
// #include "timesync.h"
#include "format.h"
static char Line[128];
// ========================================================================================================================
#ifdef WITH_OLED
#include "disp_oled.h"
int OLED_DisplayStatus(uint32_t Time, uint8_t LineIdx)
{ Format_String(Line , "OGN Tx/Rx ");
Format_HHMMSS(Line+10, Time);
OLED_PutLine(LineIdx++, Line);
Parameters.Print(Line);
OLED_PutLine(LineIdx++, Line);
return 0; }
int OLED_DisplayPosition(GPS_Position *GPS=0, uint8_t LineIdx=2)
{ if(GPS && GPS->isValid())
{ Line[0]=' ';
Format_SignDec(Line+1, GPS->Latitude /60, 6, 4); Line[9]=' ';
Format_UnsDec (Line+10, GPS->Altitude /10, 5, 0); Line[15]='m';
OLED_PutLine(LineIdx , Line);
Format_SignDec(Line, GPS->Longitude/60, 7, 4);
Format_SignDec(Line+10, GPS->ClimbRate, 4, 1);
OLED_PutLine(LineIdx+1, Line);
Format_UnsDec (Line , GPS->Speed, 4, 1); Format_String(Line+5, "m/s ");
Format_UnsDec (Line+10, GPS->Heading, 4, 1); Line[15]='^';
OLED_PutLine(LineIdx+2, Line);
Format_String(Line, "0D/00sat DOP00.0");
Line[0]+=GPS->FixMode; Format_UnsDec(Line+3, GPS->Satellites, 2);
Format_UnsDec(Line+12, (uint16_t)GPS->HDOP, 3, 1);
OLED_PutLine(LineIdx+3, Line);
}
else { OLED_PutLine(LineIdx, 0); OLED_PutLine(LineIdx+1, 0); OLED_PutLine(LineIdx+2, 0); OLED_PutLine(LineIdx+3, 0); }
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' '; Line[9]=' '; }
else Format_String(Line, " ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+10, (uint16_t)GPS->Hour, 2, 0);
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0);
Format_UnsDec (Line+14, (uint16_t)GPS->Sec, 2, 0);
} else Line[10]=0;
OLED_PutLine(LineIdx+4, Line);
Line[0]=0;
if(GPS && GPS->hasBaro)
{ Format_String(Line , "0000.0hPa 00000m");
Format_UnsDec(Line , GPS->Pressure/40, 5, 1);
Format_UnsDec(Line+10, GPS->StdAltitude/10, 5, 0); }
OLED_PutLine(LineIdx+5, Line);
return 0; }
#endif
// ========================================================================================================================
#ifdef WITH_U8G2_OLED
void OLED_DrawLogo(u8g2_t *OLED) // draw logo and hardware options in software
{
u8g2_DrawCircle(OLED, 96, 32, 30, U8G2_DRAW_ALL);
u8g2_DrawCircle(OLED, 96, 32, 34, U8G2_DRAW_UPPER_RIGHT);
u8g2_DrawCircle(OLED, 96, 32, 38, U8G2_DRAW_UPPER_RIGHT);
// u8g2_SetFont(OLED, u8g2_font_open_iconic_all_4x_t);
// u8g2_DrawGlyph(OLED, 64, 32, 0xF0);
u8g2_SetFont(OLED, u8g2_font_ncenB14_tr);
u8g2_DrawStr(OLED, 74, 31, "OGN");
u8g2_SetFont(OLED, u8g2_font_8x13_tr);
u8g2_DrawStr(OLED, 69, 43, "Tracker");
#ifdef WITH_FollowMe
u8g2_DrawStr(OLED, 0, 16 ,"FollowMe");
#endif
#ifdef WITH_TTGO
u8g2_DrawStr(OLED, 0, 16 ,"TTGO");
#endif
#ifdef WITH_HELTEC
u8g2_DrawStr(OLED, 0, 16 ,"HELTEC");
#endif
#if defined(WITH_TBEAM) || defined(WITH_TBEAM_V10)
u8g2_DrawStr(OLED, 0, 16 ,"T-BEAM");
#endif
#ifdef WITH_GPS_MTK
u8g2_DrawStr(OLED, 0, 28 ,"MTK GPS");
#endif
#ifdef WITH_GPS_UBX
u8g2_DrawStr(OLED, 0, 28 ,"UBX GPS");
#endif
#ifdef WITH_GPS_SRF
u8g2_DrawStr(OLED, 0, 28 ,"SRF GPS");
#endif
#ifdef WITH_RFM95
u8g2_DrawStr(OLED, 0, 40 ,"RFM95");
#endif
#ifdef WITH_RFM69
u8g2_DrawStr(OLED, 0, 40 ,"RFM69");
#endif
#ifdef WITH_BMP180
u8g2_DrawStr(OLED, 0, 52 ,"BMP180");
#endif
#ifdef WITH_BMP280
u8g2_DrawStr(OLED, 0, 52 ,"BMP280");
#endif
#ifdef WITH_BME280
u8g2_DrawStr(OLED, 0, 52 ,"BME280");
#endif
#ifdef WITH_BT_SPP
u8g2_DrawStr(OLED, 0, 64 ,"BT SPP");
#endif
}
void OLED_PutLine(u8g2_t *OLED, uint8_t LineIdx, const char *Line)
{ if(Line==0) return;
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "OLED_PutLine( ,");
Format_UnsDec(CONS_UART_Write, (uint16_t)LineIdx);
CONS_UART_Write(',');
Format_String(CONS_UART_Write, Line);
Format_String(CONS_UART_Write, ")\n");
xSemaphoreGive(CONS_Mutex);
#endif
// u8g2_SetFont(OLED, u8g2_font_5x8_tr);
u8g2_SetFont(OLED, u8g2_font_amstrad_cpc_extended_8r);
u8g2_DrawStr(OLED, 0, (LineIdx+1)*8, Line);
}
void OLED_DrawStatus(u8g2_t *OLED, uint32_t Time, uint8_t LineIdx=0)
{ Format_String(Line , "OGN Tx/Rx ");
Format_HHMMSS(Line+10, Time); Line[16]=0;
OLED_PutLine(OLED, LineIdx++, Line);
Parameters.Print(Line); Line[16]=0;
OLED_PutLine(OLED, LineIdx++, Line); }
void OLED_DrawPosition(u8g2_t *OLED, GPS_Position *GPS=0, uint8_t LineIdx=2)
{ if(GPS && GPS->isValid())
{ Line[0]=' ';
Format_SignDec(Line+1, GPS->Latitude /60, 6, 4); Line[9]=' ';
Format_UnsDec (Line+10, GPS->Altitude /10, 5, 0); Line[15]='m';
OLED_PutLine(OLED, LineIdx , Line);
Format_SignDec(Line, GPS->Longitude/60, 7, 4);
Format_SignDec(Line+10, GPS->ClimbRate, 4, 1);
OLED_PutLine(OLED, LineIdx+1, Line);
Format_UnsDec (Line , GPS->Speed, 4, 1); Format_String(Line+5, "m/s ");
Format_UnsDec (Line+10, GPS->Heading, 4, 1); Line[15]='^';
OLED_PutLine(OLED, LineIdx+2, Line);
Format_String(Line, "0D/00sat DOP00.0");
Line[0]+=GPS->FixMode; Format_UnsDec(Line+3, GPS->Satellites, 2);
Format_UnsDec(Line+12, (uint16_t)GPS->HDOP, 3, 1);
OLED_PutLine(OLED, LineIdx+3, Line);
}
// else { OLED_PutLine(OLED, LineIdx, 0); OLED_PutLine(OLED, LineIdx+1, 0); OLED_PutLine(LineIdx+2, 0); OLED_PutLine(LineIdx+3, 0); }
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' '; Line[9]=' '; }
else Format_String(Line, " ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+10, (uint16_t)GPS->Hour, 2, 0);
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0);
Format_UnsDec (Line+14, (uint16_t)GPS->Sec, 2, 0);
} else Line[10]=0;
OLED_PutLine(OLED, LineIdx+4, Line);
Line[0]=0;
if(GPS && GPS->hasBaro)
{ Format_String(Line , "0000.0hPa 00000m");
Format_UnsDec(Line , GPS->Pressure/40, 5, 1);
Format_UnsDec(Line+10, GPS->StdAltitude/10, 5, 0); }
OLED_PutLine(OLED, LineIdx+5, Line);
}
void OLED_DrawGPS(u8g2_t *OLED, GPS_Position *GPS) // GPS time, position, altitude
{ // u8g2_SetFont(OLED, u8g2_font_ncenB14_tr);
u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
/*
Len+=Format_String(Line+Len, "GPS ");
if(GPS && GPS->isValid())
{ Line[Len++]='0'+GPS->FixMode; Line[Len++]='D'; Line[Len++]='/';
Len+=Format_UnsDec(Line+Len, GPS->Satellites, 1);
Len+=Format_String(Line+Len, "sat DOP");
Len+=Format_UnsDec(Line+Len, (uint16_t)GPS->HDOP, 2, 1); }
else
{ Len+=Format_String(Line+Len, "(no lock)"); }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 12, Line);
*/
if(GPS && GPS->isDateValid())
{ Format_UnsDec (Line , (uint16_t)GPS->Day, 2, 0); Line[2]='.';
Format_UnsDec (Line+ 3, (uint16_t)GPS->Month, 2, 0); Line[5]='.';
Format_UnsDec (Line+ 6, (uint16_t)GPS->Year , 2, 0); Line[8]=' ';
} else Format_String(Line, " . . ");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+ 9, (uint16_t)GPS->Hour, 2, 0); Line[11]=':';
Format_UnsDec (Line+12, (uint16_t)GPS->Min, 2, 0); Line[14]=':';
Format_UnsDec (Line+15, (uint16_t)GPS->Sec, 2, 0);
} else Format_String(Line+9, " : : ");
Line[17]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
Len+=Format_String(Line+Len, "Lat: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Latitude /6, 7, 5);
Line[Len++]=0xB0; }
else Len+=Format_String(Line+Len, "---.-----");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
Len=0;
Len+=Format_String(Line+Len, "Lon: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Longitude /6, 8, 5);
Line[Len++]=0xB0; }
else Len+=Format_String(Line+Len, "----.-----");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
Len=0;
Len+=Format_String(Line+Len, "Alt: ");
if(GPS && GPS->isValid())
{ Len+=Format_SignDec(Line+Len, GPS->Altitude, 4, 1);
Line[Len++]='m'; }
else Len+=Format_String(Line+Len, "-----.-");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
}
void OLED_DrawRF(u8g2_t *OLED) // RF
{ u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines. 12 pixels/line
uint8_t Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69"); // Type of RF chip used
if(Parameters.isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272");
#endif
Line[Len++]=':';
Len+=Format_SignDec(Line+Len, (int16_t)Parameters.getTxPower()); // Tx power
Len+=Format_String(Line+Len, "dBm");
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, (int32_t)Parameters.RFchipFreqCorr, 2, 1); // frequency correction
Len+=Format_String(Line+Len, "ppm");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
Len+=Format_String(Line+Len, "Rx:"); //
Len+=Format_SignDec(Line+Len, -5*TRX.averRSSI, 2, 1); // noise level seen by the receiver
Len+=Format_String(Line+Len, "dBm ");
Len+=Format_UnsDec(Line+Len, RX_OGN_Count64); // received packet/min
Len+=Format_String(Line+Len, "/min");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
Len=0;
Len+=Format_SignDec(Line+Len, (int16_t)TRX.chipTemp); // RF chip internal temperature (not calibrated)
// Line[Len++]=0xB0;
// Line[Len++]='C';
Len+=Format_String(Line+Len, "\260C RxFIFO:");
Len+=Format_UnsDec(Line+Len, RF_RxFIFO.Full()); // how many packets wait in the RX queue
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
// u8g2_DrawStr(OLED, 0, 48, RF_FreqPlan.getPlanName());
Len=0;
Len+=Format_String(Line+Len, RF_FreqPlan.getPlanName()); // name of the frequency plan
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, (uint16_t)(RF_FreqPlan.getCenterFreq()/100000), 3, 1); // center frequency
Len+=Format_String(Line+Len, "MHz");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
}
void OLED_DrawRelay(u8g2_t *OLED, GPS_Position *GPS)
{ u8g2_SetFont(OLED, u8g2_font_amstrad_cpc_extended_8r);
uint8_t Len=Format_String(Line, "Relay queue :");
if(GPS && GPS->Sec>=0) Len+=Format_UnsDec(Line+Len, (uint16_t)(GPS->Sec), 2);
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
uint8_t LineIdx=1;
for( uint8_t Idx=0; Idx<RelayQueueSize; Idx++)
{ OGN_RxPacket<OGN_Packet> *Packet = RelayQueue.Packet+Idx; if(Packet->Rank==0) continue;
uint8_t Len=0;
Line[Len++]='0'+Packet->Packet.Header.AddrType;
Line[Len++]=':';
Len+=Format_Hex(Line+Len, Packet->Packet.Header.Address, 6);
Line[Len++]=' ';
Len+=Format_Hex(Line+Len, Packet->Rank);
Line[Len++]=' ';
Line[Len++]=':';
Len+=Format_UnsDec(Line+Len, Packet->Packet.Position.Time, 2);
Line[Len]=0;
u8g2_DrawStr(OLED, 0, (LineIdx+3)*8, Line);
LineIdx++; if(LineIdx>=8) break;
}
}
void OLED_DrawLookout(u8g2_t *OLED, GPS_Position *GPS)
{ u8g2_SetFont(OLED, u8g2_font_amstrad_cpc_extended_8r);
uint8_t Len=Format_String(Line, "Look ");
if(Look.WarnLevel)
{ const LookOut_Target *Tgt = Look.Target+Look.WorstTgtIdx;
Len+=Format_Hex(Line+Len, Tgt->ID, 7);
Line[Len++]='/';
Line[Len++]='0'+Tgt->WarnLevel;
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, Tgt->TimeMargin>>1);
Line[Len++]='s';
}
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
uint8_t LineIdx=1;
for( uint8_t Idx=0; Idx<Look.MaxTargets; Idx++)
{ const LookOut_Target *Tgt = Look.Target+Idx; if(!Tgt->Alloc) continue;
uint8_t Len=0;
Len+=Format_Hex(Line+Len, Tgt->ID, 7);
Line[Len++]=' ';
if(Tgt->DistMargin)
{ Len+=Format_UnsDec(Line+Len, Tgt->HorDist>>1);
Line[Len++]='m'; }
else
{ Len+=Format_UnsDec(Line+Len, Tgt->TimeMargin>>1);
Line[Len++]='s';
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, Tgt->MissDist>>1);
Line[Len++]='m'; }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, (LineIdx+3)*8, Line);
LineIdx++; if(LineIdx>=8) break;
}
}
void OLED_DrawTrafWarn(u8g2_t *OLED, GPS_Position *GPS)
{ u8g2_SetFont(OLED, u8g2_font_9x15_tr);
if(Look.WarnLevel==0) return;
const LookOut_Target *Tgt = Look.Target+Look.WorstTgtIdx;
uint8_t Len=0;
Len+=Format_Hex(Line+Len, Tgt->ID, 7);
Line[Len++]='/';
Line[Len++]='0'+Tgt->WarnLevel;
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 30, Line);
Len=0;
Len+=Format_UnsDec(Line+Len, Tgt->TimeMargin*5, 2, 1);
Line[Len++]='s';
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, Tgt->MissDist*5, 2, 1);
Line[Len++]='m';
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 45, Line);
Len=0;
Len+=Format_UnsDec(Line+Len, Tgt->getRelHorSpeed()*5, 2, 1);
Line[Len++]='m';
Line[Len++]='/';
Line[Len++]='s';
Line[Len++]=' ';
Len+=Format_UnsDec(Line+Len, Tgt->HorDist*5, 2, 1);
Line[Len++]='m';
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
}
void OLED_DrawBaro(u8g2_t *OLED, GPS_Position *GPS)
{ u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
#ifdef WITH_BMP180
Len+=Format_String(Line+Len, "BMP180 ");
#endif
#ifdef WITH_BMP280
Len+=Format_String(Line+Len, "BMP280 ");
#endif
#ifdef WITH_BME280
Len+=Format_String(Line+Len, "BME280 ");
#endif
#ifdef WITH_MS5607
Len+=Format_String(Line+Len, "MS5607 ");
#endif
if(GPS && GPS->hasBaro)
{ Len+=Format_UnsDec(Line+Len, GPS->Pressure/4, 5, 2);
Len+=Format_String(Line+Len, "Pa "); }
else Len+=Format_String(Line+Len, "----.--Pa ");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
if(GPS && GPS->hasBaro)
{ Len+=Format_SignDec(Line+Len, GPS->StdAltitude, 5, 1);
Len+=Format_String(Line+Len, "m ");
Len+=Format_SignDec(Line+Len, GPS->ClimbRate, 2, 1);
Len+=Format_String(Line+Len, "m/s "); }
else Len+=Format_String(Line+Len, "-----.-m --.-m/s ");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
Len=0;
if(GPS && GPS->hasBaro)
{ Len+=Format_SignDec(Line+Len, GPS->Temperature, 2, 1);
Line[Len++]=0xB0;
Line[Len++]='C';
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, GPS->Humidity, 2, 1);
Line[Len++]='%'; }
else Len+=Format_String(Line+Len, "---.- C --.-% ");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
}
static uint8_t BattCapacity(uint16_t mVolt)
{ if(mVolt>=4100) return 100;
if(mVolt<=3600) return 0;
return (mVolt-3600+2)/5; }
void OLED_DrawBattery(u8g2_t *OLED)
{ uint8_t Cap=BattCapacity(BatteryVoltage>>8);
// u8g2_SetFont(OLED, u8g2_font_battery19_tn);
// u8g2_DrawGlyph(OLED, 120, 60, '0'+(Cap+10)/20);
// u8g2_SetFont(OLED, u8g2_font_6x10_tr);
// u8g2_DrawStr(OLED, 0, 24, Line);
// u8g2_DrawStr(OLED, 0, 24, "Battery");
u8g2_SetFont(OLED, u8g2_font_9x15_tr);
strcpy(Line, " %");
if(Cap>=100) Format_UnsDec(Line, Cap, 3);
else if(Cap>=10) Format_UnsDec(Line+1, Cap, 2);
else Line[2]='0'+Cap;
u8g2_DrawStr (OLED, 16, 32, Line);
u8g2_DrawFrame(OLED, 12, 20, 42, 14);
u8g2_DrawBox (OLED, 8, 23, 4, 8);
strcpy(Line, " . V");
Format_UnsDec(Line, BatteryVoltage>>8, 4, 3);
u8g2_DrawStr(OLED, 0, 48, Line);
strcpy(Line, " . mV/min ");
Format_SignDec(Line, (600*BatteryVoltageRate+128)>>8, 3, 1);
u8g2_DrawStr(OLED, 0, 60, Line);
#ifdef WITH_BQ
// uint8_t Status = BQ.readStatus();
// uint8_t State = (Status>>4)&0x03;
// const char *StateName[4] = { "Not charging", "Pre-charge", "Fast charge", "Full" } ;
// u8g2_SetFont(OLED, u8g2_font_amstrad_cpc_extended_8r);
// u8g2_SetFont(OLED, u8g2_font_6x10_tr);
// u8g2_DrawStr(OLED, 0, 50, StateName[State]);
// u8g2_DrawStr(OLED, 0, 60, Status&0x04?"Power-is-Good":"Power-is-not-Good");
/* print BQ registers for debug
u8g2_SetFont(OLED, u8g2_font_6x10_tr);
for(uint8_t Reg=0; Reg<=10; Reg++)
{ uint8_t Val = BQ.readReg(Reg);
Format_Hex(Line+3*Reg, Val); Line[3*Reg+2]=' '; }
Line[33]=0;
u8g2_DrawStr(OLED, 0, 60, Line+15);
Line[15]=0; u8g2_DrawStr(OLED, 0, 50, Line);
*/
#endif
}
void OLED_DrawStatusBar(u8g2_t *OLED, GPS_Position *GPS)
{ static bool Odd=0;
uint8_t Cap=BattCapacity(BatteryVoltage>>8);
uint8_t BattLev = (Cap+10)/20;
uint8_t Charging = 0;
#ifdef WITH_BQ
uint8_t Status = BQ.readStatus();
Charging = (Status>>4)&0x03;
static uint8_t DispLev = 0;
if(Charging==1 || Charging==2) { DispLev++; if(DispLev>5) DispLev = BattLev?BattLev-1:0; }
else { DispLev = BattLev; }
#else
uint8_t &DispLev = BattLev;
#endif
if(BattLev==0 && !Charging && Odd) // when battery is empty, then flash it at 0.5Hz
{ }
else
{ u8g2_SetFont(OLED, u8g2_font_battery19_tn);
u8g2_SetFontDirection(OLED, 3);
u8g2_DrawGlyph(OLED, 24, 10, '0'+DispLev);
u8g2_SetFontDirection(OLED, 0); }
Odd=!Odd;
// u8g2_SetFont(OLED, u8g2_font_5x7_tr);
// u8g2_SetFont(OLED, u8g2_font_5x8_tr);
static uint8_t Sec=0;
u8g2_SetFont(OLED, u8g2_font_6x12_tr);
// strcpy(Line, "[ %] --sat --:--");
strcpy(Line, "--sat --:--Z");
if(GPS && GPS->isTimeValid())
{ Format_UnsDec (Line+6, (uint16_t)GPS->Hour, 2, 0); Line[8]=':';
Format_UnsDec (Line+9, (uint16_t)GPS->Min, 2, 0);
} else Format_String(Line+6, "--:--");
if(GPS)
{ if(Sec)
{ Format_UnsDec(Line, (uint16_t)GPS->Satellites, 2); memcpy(Line+2, "sat", 3); }
else
{ Format_UnsDec(Line, (uint16_t)(GPS_SatSNR+2)/4, 2); memcpy(Line+2, "dB ", 3);}
}
else Format_String(Line, "--sat");
u8g2_DrawStr(OLED, 40, 10, Line);
Sec++; if(Sec>=3) Sec=0; }
void OLED_DrawSystem(u8g2_t *OLED)
{
u8g2_SetFont(OLED, u8g2_font_7x13_tf); // 5 lines, 12 pixels/line
uint8_t Len=0;
Len+=Format_String(Line+Len, "GPS ");
#ifdef WITH_GPS_UBX
Len+=Format_String(Line+Len, "UBX ");
#endif
#ifdef WITH_GPS_MTK
Len+=Format_String(Line+Len, "MTK ");
#endif
#ifdef WITH_GPS_SRF
Len+=Format_String(Line+Len, "SRF ");
#endif
Len+=Format_UnsDec(Line+Len, GPS_getBaudRate(), 1);
Len+=Format_String(Line+Len, "bps");
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 24, Line);
Len=0;
#ifdef WITH_RFM69
Len+=Format_String(Line+Len, "RFM69 v"); // Type of RF chip used
if(Parameters.isTxTypeHW()) Line[Len++]='H';
Line[Len++]='W';
#endif
#ifdef WITH_RFM95
Len+=Format_String(Line+Len, "RFM95 v");
#endif
#ifdef WITH_SX1272
Len+=Format_String(Line+Len, "SX1272 v");
#endif
Len+=Format_Hex(Line+Len, TRX.chipVer);
Line[Len++]=' ';
Len+=Format_SignDec(Line+Len, (int16_t)TRX.chipTemp);
Line[Len++]=0xB0;
Line[Len++]='C';
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 36, Line);
Len=0;
#ifdef WITH_BMP180
Len+=Format_String(Line+Len, "BMP180 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BMP280
Len+=Format_String(Line+Len, "BMP280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_BME280
Len+=Format_String(Line+Len, "BME280 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
#ifdef WITH_MS5607
Len+=Format_String(Line+Len, "MS5607 0x");
Len+=Format_Hex(Line+Len, Baro.ADDR);
#endif
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 48, Line);
#ifdef WITH_SPIFFS
Len=0;
Len+=Format_String(Line+Len, "SPIFFS ");
size_t Total, Used;
if(SPIFFS_Info(Total, Used)==0) // get the SPIFFS usage summary
{ Len+=Format_UnsDec(Line+Len, (Total-Used)/1024);
Len+=Format_String(Line+Len, "/");
Len+=Format_UnsDec(Line+Len, Total/1024);
Len+=Format_String(Line+Len, "kB"); }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
#endif
/*
#ifdef WITH_SD
Len=0;
Len+=Format_String(Line+Len, "SD ");
if(SD_isMounted())
{ Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectors());
Line[Len++]='x';
Len+=Format_UnsDec(Line+Len, (uint32_t)SD_getSectorSize()*5/512, 2, 1);
Len+=Format_String(Line+Len, "KB"); }
else
{ Len+=Format_String(Line+Len, "none"); }
Line[Len]=0;
u8g2_DrawStr(OLED, 0, 60, Line);
#endif
*/
}
void OLED_DrawID(u8g2_t *OLED)
{ u8g2_SetFont(OLED, u8g2_font_9x15_tr);
strcpy(Line, "ID "); Parameters.Print(Line+3); Line[13]=0;
u8g2_DrawStr(OLED, 0, 28, Line);
// u8g2_SetFont(OLED, u8g2_font_10x20_tr);
#ifdef WITH_FollowMe
u8g2_SetFont(OLED, u8g2_font_7x13_tf);
u8g2_DrawStr(OLED, 15, 44, "FollowMe868");
u8g2_DrawStr(OLED, 20, 56, "by AVIONIX");
#endif
u8g2_SetFont(OLED, u8g2_font_5x8_tr);
u8g2_DrawStr(OLED, 96, 62, "v0.0.0");
}
#endif
// ========================================================================================================================

25
main/disp_oled.h 100644
Wyświetl plik

@ -0,0 +1,25 @@
#ifdef WITH_OLED
int OLED_DisplayStatus(uint32_t Time, uint8_t LineIdx=0);
int OLED_DisplayPosition(GPS_Position *GPS=0, uint8_t LineIdx=2);
#endif
#ifdef WITH_U8G2_OLED
void OLED_DrawLogo(u8g2_t *OLED);
void OLED_DrawStatus(u8g2_t *OLED, uint32_t Time, uint8_t LineIdx=0);
void OLED_DrawPosition(u8g2_t *OLED, GPS_Position *GPS=0, uint8_t LineIdx=2);
void OLED_DrawGPS(u8g2_t *OLED, GPS_Position *GPS=0);
void OLED_DrawRF(u8g2_t *OLED);
void OLED_DrawRelay(u8g2_t *OLED, GPS_Position *GPS=0);
void OLED_DrawLookout(u8g2_t *OLED, GPS_Position *GPS=0);
void OLED_DrawTrafWarn(u8g2_t *OLED, GPS_Position *GPS=0);
void OLED_DrawBaro(u8g2_t *OLED, GPS_Position *GPS=0);
void OLED_DrawBattery(u8g2_t *OLED);
void OLED_DrawStatusBar(u8g2_t *OLED, GPS_Position *GPS);
void OLED_DrawSystem(u8g2_t *OLED);
void OLED_DrawID(u8g2_t *OLED);
#endif

Wyświetl plik

@ -69,6 +69,22 @@ void Format_Hex( void (*Output)(char), uint32_t Word )
{ Format_Hex(Output, (uint8_t)(Word>>24)); Format_Hex(Output, (uint8_t)(Word>>16));
Format_Hex(Output, (uint8_t)(Word>>8)); Format_Hex(Output, (uint8_t)Word); }
void Format_MAC( void (*Output)(char), uint8_t *MAC, uint8_t Len)
{ for(uint8_t Idx=0; Idx<Len; Idx++)
{ if(Idx) (*Output)(':');
Format_Hex(Output, MAC[Idx]); }
}
uint8_t Format_HHcMMcSS(char *Out, uint32_t Time)
{ uint32_t DayTime=Time%86400;
uint32_t Hour=DayTime/3600; DayTime-=Hour*3600;
uint32_t Min=DayTime/60; DayTime-=Min*60;
uint32_t Sec=DayTime;
uint32_t HHMMSS = 1000000*Hour + 1000*Min + Sec;
uint8_t Len=Format_UnsDec(Out, HHMMSS, 8);
Out[2]=':'; Out[5]=':';
return Len; }
uint8_t Format_HHMMSS(char *Out, uint32_t Time)
{ uint32_t DayTime=Time%86400;
uint32_t Hour=DayTime/3600; DayTime-=Hour*3600;
@ -77,6 +93,14 @@ uint8_t Format_HHMMSS(char *Out, uint32_t Time)
uint32_t HHMMSS = 10000*Hour + 100*Min + Sec;
return Format_UnsDec(Out, HHMMSS, 6); }
void Format_HHMMSS(void (*Output)(char), uint32_t Time)
{ uint32_t DayTime=Time%86400;
uint32_t Hour=DayTime/3600; DayTime-=Hour*3600;
uint32_t Min=DayTime/60; DayTime-=Min*60;
uint32_t Sec=DayTime;
uint32_t HHMMSS = 10000*Hour + 100*Min + Sec;
Format_UnsDec(Output, HHMMSS, 6); }
void Format_UnsDec( void (*Output)(char), uint16_t Value, uint8_t MinDigits, uint8_t DecPoint)
{ uint16_t Base; uint8_t Pos;
for( Pos=5, Base=10000; Base; Base/=10, Pos--)

Wyświetl plik

@ -3,6 +3,8 @@
#include <stdint.h>
#define WITH_AUTOCR
char HexDigit(uint8_t Val);
void Format_Bytes ( void (*Output)(char), const uint8_t *Bytes, uint8_t Len);
@ -14,6 +16,7 @@ void Format_String( void (*Output)(char), const char *String, uint8_t MinLen,
void Format_Hex( void (*Output)(char), uint8_t Byte );
void Format_Hex( void (*Output)(char), uint16_t Word );
void Format_Hex( void (*Output)(char), uint32_t Word );
void Format_MAC( void (*Output)(char), uint8_t *MAC, uint8_t Len=6);
void Format_UnsDec ( void (*Output)(char), uint16_t Value, uint8_t MinDigits=1, uint8_t DecPoint=0);
void Format_SignDec( void (*Output)(char), int16_t Value, uint8_t MinDigits=1, uint8_t DecPoint=0);
@ -34,8 +37,19 @@ uint8_t Format_Hex( char *Output, uint8_t Byte );
uint8_t Format_Hex( char *Output, uint16_t Word );
uint8_t Format_Hex( char *Output, uint32_t Word );
uint8_t Format_Hex( char *Output, uint32_t Word, uint8_t Digits);
uint8_t Format_Hex( char *Output, uint64_t Word );
// uint8_t Format_Hex( char *Output, uint64_t Word, uint8_t Digits);
template <class Type>
uint8_t Format_Hex( char *Output, Type Word, uint8_t Digits)
{ for(uint8_t Idx=Digits; Idx>0; )
{ Output[--Idx]=HexDigit(Word&0x0F);
Word>>=4; }
return Digits; }
uint8_t Format_HHcMMcSS(char *Out, uint32_t Time);
uint8_t Format_HHMMSS(char *Out, uint32_t Time);
void Format_HHMMSS(void (*Output)(char), uint32_t Time);
uint8_t Format_Latitude (char *Out, int32_t Lat); // [1/600000deg] => DDMM.MMMMs
uint8_t Format_Longitude(char *Out, int32_t Lon); // [1/600000deg] => DDDMM.MMMMs
@ -49,60 +63,60 @@ int16_t Read_Dec3(const char *Inp); // convert three digit decimal n
int16_t Read_Dec4(const char *Inp); // convert three digit decimal number into an integer
template <class Type>
int8_t Read_Hex(Type &Int, const char *Inp) // convert variable number of digits hexadecimal number into an integer
{ Int=0; int8_t Len=0;
if(Inp==0) return 0;
for( ; ; )
int8_t Read_Hex(Type &Int, const char *Inp, uint8_t MaxDig=0) // convert variable number of digits hexadecimal number into an integer
{ if(Inp==0) return 0;
if(MaxDig==0) MaxDig=2*sizeof(Type);
Int=0; int8_t Len=0;
for( ; MaxDig; MaxDig--)
{ int8_t Dig=Read_Hex1(Inp[Len]); if(Dig<0) break;
Int = (Int<<4) + Dig; Len++; }
return Len; } // return number of characters read
template <class Type>
int8_t Read_UnsDec(Type &Int, const char *Inp) // convert variable number of digits unsigned decimal number into an integer
{ Int=0; int8_t Len=0;
if(Inp==0) return 0;
for( ; ; )
{ int8_t Dig=Read_Dec1(Inp[Len]); if(Dig<0) break;
Int = 10*Int + Dig; Len++; }
return Len; } // return number of characters read
template <class Type>
int8_t Read_UnsDec(Type &Int, const char *Inp) // convert variable number of digits unsigned decimal number into an integer
{ Int=0; int8_t Len=0;
if(Inp==0) return 0;
for( ; ; )
{ int8_t Dig=Read_Dec1(Inp[Len]); if(Dig<0) break;
Int = 10*Int + Dig; Len++; }
return Len; } // return number of characters read
template <class Type>
int8_t Read_SignDec(Type &Int, const char *Inp) // convert signed decimal number into in16_t or int32_t
{ Int=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0];
if((Sign=='+')||(Sign=='-')) Len++;
Len+=Read_UnsDec(Int, Inp+Len); if(Sign=='-') Int=(-Int);
return Len; } // return number of characters read
template <class Type>
int8_t Read_SignDec(Type &Int, const char *Inp) // convert signed decimal number into in16_t or int32_t
{ Int=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0];
if((Sign=='+')||(Sign=='-')) Len++;
Len+=Read_UnsDec(Int, Inp+Len); if(Sign=='-') Int=(-Int);
return Len; } // return number of characters read
template <class Type>
int8_t Read_Int(Type &Value, const char *Inp)
{ Value=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0]; int8_t Dig;
if((Sign=='+')||(Sign=='-')) Len++;
if((Inp[Len]=='0')&&(Inp[Len+1]=='x'))
{ Len+=2; Dig=Read_Hex(Value, Inp+Len); }
else
{ Dig=Read_UnsDec(Value, Inp+Len); }
if(Dig<=0) return Dig;
Len+=Dig;
if(Sign=='-') Value=(-Value); return Len; }
template <class Type>
int8_t Read_Float1(Type &Value, const char *Inp) // read floating point, take just one digit after decimal point
{ Value=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0]; int8_t Dig;
if((Sign=='+')||(Sign=='-')) Len++;
Len+=Read_UnsDec(Value, Inp+Len); Value*=10;
if(Inp[Len]!='.') goto Ret;
Len++;
Dig=Read_Dec1(Inp[Len]); if(Dig<0) goto Ret;
Value+=Dig; Len++;
Dig=Read_Dec1(Inp[Len]); if(Dig>=5) Value++;
Ret: if(Sign=='-') Value=(-Value); return Len; }
template <class Type>
int8_t Read_Int(Type &Value, const char *Inp)
{ Value=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0]; int8_t Dig;
if((Sign=='+')||(Sign=='-')) Len++;
if((Inp[Len]=='0')&&(Inp[Len+1]=='x'))
{ Len+=2; Dig=Read_Hex(Value, Inp+Len); }
else
{ Dig=Read_UnsDec(Value, Inp+Len); }
if(Dig<=0) return Dig;
Len+=Dig;
if(Sign=='-') Value=(-Value); return Len; }
template <class Type>
int8_t Read_Float1(Type &Value, const char *Inp) // read floating point, take just one digit after decimal point
{ Value=0; int8_t Len=0;
if(Inp==0) return 0;
char Sign=Inp[0]; int8_t Dig;
if((Sign=='+')||(Sign=='-')) Len++;
Len+=Read_UnsDec(Value, Inp+Len); Value*=10;
if(Inp[Len]!='.') goto Ret;
Len++;
Dig=Read_Dec1(Inp[Len]); if(Dig<0) goto Ret;
Value+=Dig; Len++;
Dig=Read_Dec1(Inp[Len]); if(Dig>=5) Value++;
Ret: if(Sign=='-') Value=(-Value); return Len; }
int8_t Read_LatDDMMSS(int32_t &Lat, const char *Inp);
int8_t Read_LonDDMMSS(int32_t &Lon, const char *Inp);

Wyświetl plik

@ -1,9 +1,10 @@
#include <stdint.h>
#include <stdlib.h>
#include "hal.h"
#include "gps.h"
// #include "ctrl.h"
#include "ctrl.h"
#include "nmea.h"
#include "ubx.h"
@ -23,6 +24,7 @@
#ifdef DEBUG_PRINT
static char Line[128];
static void CONS_HexDump(char Byte) { Format_Hex(CONS_UART_Write, (uint8_t)Byte); }
#endif
// ----------------------------------------------------------------------------
@ -37,13 +39,16 @@ static UBX_RxMsg UBX; // UBX messages catcher
static MAV_RxMsg MAV; // MAVlink message catcher
#endif
uint16_t GPS_PosPeriod = 0;
uint16_t GPS_PosPeriod = 0; // [0.01s] time between succecive GPS readouts
// uint8_t GPS_PowerMode = 2; // 0=shutdown, 1=reduced power, 2=normal
const uint8_t PosPipeIdxMask = GPS_PosPipeSize-1;
static GPS_Position Position[GPS_PosPipeSize]; // GPS position pipe
static uint8_t PosIdx; // Pipe index, increments with every GPS position received
static GPS_Position Position[GPS_PosPipeSize]; // GPS position pipe
static TickType_t Burst_TickCount; // [msec] TickCount when the data burst from GPS started
static TickType_t PPS_Tick; // [msec] System Tick when the PPS arrived
static TickType_t Burst_Tick; // [msec] System Tick when the data burst from GPS started
uint32_t GPS_TimeSinceLock; // [sec] time since the GPS has a lock
uint32_t GPS_FatTime = 0; // [sec] UTC date/time in FAT format
@ -52,18 +57,20 @@ static TickType_t Burst_TickCount; // [msec] TickCount when the data bur
int32_t GPS_Longitude = 0; //
int16_t GPS_GeoidSepar= 0; // [0.1m]
uint16_t GPS_LatCosine = 3000; //
uint32_t GPS_Random = 0x12345678; // random number from the LSB of the GPS data
uint16_t GPS_SatSNR = 0; // [0.25dB]
Status GPS_Status;
static union
{ uint8_t Flags;
struct
{ bool Spare:1; //
bool Active:1; // has started
bool GxRMC:1; // GPRMC or GNRMC registered
{ bool GxRMC:1; // GPRMC or GNRMC registered
bool GxGGA:1; // GPGGA or GNGGA registered
bool GxGSA:1; // GPGSA or GNGSA registered
bool Complete:1; // all GPS data is supplied and thus ready for processing
bool Spare:1;
bool Active:1; // has started and data from the GPS is flowing
bool Complete:1; // all GPS data we need is supplied and thus ready for processing
} ;
} GPS_Burst;
// for the autobaud on the GPS port
@ -77,7 +84,65 @@ uint32_t GPS_getBaudRate (void) { return BaudRate[BaudRateIdx]; }
uint32_t GPS_nextBaudRate(void) { BaudRateIdx++; if(BaudRateIdx>=BaudRates) BaudRateIdx=0; return GPS_getBaudRate(); }
const uint32_t GPS_TargetBaudRate = 57600; // BaudRate[4]; // [bps] must be one of the baud rates known by the autbaud
const uint8_t GPS_TargetDynModel = 7; // for UBX GPS's: 6 = airborne with >1g, 7 = with >2g
// const uint8_t GPS_TargetDynModel = 7; // for UBX GPS's: 6 = airborne with >1g, 7 = with >2g
static char GPS_Cmd[64];
// ----------------------------------------------------------------------------
static uint16_t SatSNRsum = 0;
static uint8_t SatSNRcount = 0;
struct GPS_Sat // store GPS satellite data in single 32-bit word
{ union
{ uint32_t Word;
struct
{ uint16_t Azim: 9; // [deg]
uint8_t Elev: 7; // [deg]
uint8_t SNR: 7; // [dB/Hz]
uint16_t PRN: 9; // [1..96] GPS:1..32, SBAS:33..64, GNSS:65..96
} ;
} ;
} ;
static void ProcessGSV(NMEA_RxMsg &GSV) // process GxGSV to extract satellite data
{
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, (const char *)GSV.Data, 0, GSV.Len);
Format_String(CONS_UART_Write, " (");
Format_UnsDec(CONS_UART_Write, (uint16_t)GSV.Parms);
Format_String(CONS_UART_Write, ")\n");
xSemaphoreGive(CONS_Mutex);
#endif
if(!GSV.isGPGSV()) return; // for now, only the GPS satellites, before we learn to mix the others in
if(GSV.Parms<3) return;
int8_t Pkts=Read_Dec1((const char *)GSV.ParmPtr(0)); if(Pkts<0) return;
int8_t Pkt =Read_Dec1((const char *)GSV.ParmPtr(1)); if(Pkt <0) return;
int8_t Sats=Read_Dec2((const char *)GSV.ParmPtr(2));
if(Sats<0) Sats=Read_Dec1((const char *)GSV.ParmPtr(2));
if(Sats<0) return;
for( int Parm=3; Parm<GSV.Parms; )
{ int8_t PRN =Read_Dec2((const char *)GSV.ParmPtr(Parm++)); if(PRN <0) break;
int8_t Elev=Read_Dec2((const char *)GSV.ParmPtr(Parm++)); if(Elev<0) break;
int16_t Azim=Read_Dec3((const char *)GSV.ParmPtr(Parm++)); if(Azim<0) break;
int8_t SNR =Read_Dec2((const char *)GSV.ParmPtr(Parm++)); if(SNR<=0) continue;
SatSNRsum+=SNR; SatSNRcount++; }
if(Pkt==Pkts)
{
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "SatSNR: ");
Format_UnsDec(CONS_UART_Write, SatSNRsum);
CONS_UART_Write('/');
Format_UnsDec(CONS_UART_Write, (uint16_t)SatSNRcount);
Format_String(CONS_UART_Write, "\n");
xSemaphoreGive(CONS_Mutex);
#endif
if(SatSNRcount) GPS_SatSNR = (4*SatSNRsum+SatSNRcount/2)/SatSNRcount;
else GPS_SatSNR = 0;
SatSNRsum=0; SatSNRcount=0; }
}
// ----------------------------------------------------------------------------
@ -97,21 +162,21 @@ int16_t GPS_AverageSpeed(void) // get average speed based
static void GPS_PPS_On(void) // called on rising edge of PPS
{ static TickType_t PrevTickCount=0;
TickType_t TickCount = xTaskGetTickCount(); // [ms] TickCount now
TickType_t Delta = TickCount-PrevTickCount; // [ms] time difference to the previous PPS
PrevTickCount = TickCount; // [ms]
if(abs((int)Delta-1000)>10) return; // [ms] filter out difference away from 1.00sec
TimeSync_HardPPS(TickCount);
PPS_Tick = xTaskGetTickCount(); // [ms] TickCount now
TickType_t Delta = PPS_Tick-PrevTickCount; // [ms] time difference to the previous PPS
PrevTickCount = PPS_Tick; // [ms]
if(abs((int)Delta-1000)>=20) return; // [ms] filter out difference away from 1.00sec
TimeSync_HardPPS(PPS_Tick); // [ms] synchronize the UTC time to the PPS at given Tick
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(),3);
Format_String(CONS_UART_Write, " -> PPS\n");
xSemaphoreGive(CONS_Mutex);
#endif
GPS_Status.PPS=1;
LED_PCB_Flash(50);
LED_PCB_Flash(100);
// uint8_t Sec=GPS_Sec; Sec++; if(Sec>=60) Sec=0; GPS_Sec=Sec;
// GPS_UnixTime++;
// #ifdef WITH_MAVLINK
@ -158,12 +223,12 @@ static void GPS_LockEnd(void) // called when GPS looses a
// ----------------------------------------------------------------------------
static void GPS_BurstStart(void) // when GPS starts sending the data on the serial port
{ Burst_TickCount=xTaskGetTickCount();
{ Burst_Tick=xTaskGetTickCount();
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60);
Format_UnsDec(CONS_UART_Write, TimeSync_Time(Burst_Tick)%60, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(),3);
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(Burst_Tick), 3);
Format_String(CONS_UART_Write, " -> GPS_BurstStart() GPS:");
Format_Hex(CONS_UART_Write, GPS_Status.Flags);
Format_String(CONS_UART_Write, "\n");
@ -196,34 +261,71 @@ static void GPS_BurstStart(void) // wh
CFG_MSG.msgID = 0x04; // ID for GSA
UBX_RxMsg::Send(0x06, 0x01, GPS_UART_Write, (uint8_t *)(&CFG_MSG), sizeof(CFG_MSG));
}
#endif
#ifdef WITH_GPS_MTK
if(Parameters.NavRate)
{ strcpy(GPS_Cmd, "$PMTK220,"); // MTK command to change the navigation rate
uint8_t Len = strlen(GPS_Cmd);
uint16_t OneSec = 1000;
Len += Format_UnsDec(GPS_Cmd+Len, OneSec/Parameters.NavRate);
Len += NMEA_AppendCheck(GPS_Cmd, Len);
GPS_Cmd[Len++]='\r';
GPS_Cmd[Len++]='\n';
GPS_Cmd[Len]=0;
Format_String(GPS_UART_Write, GPS_Cmd, Len, 0);
GPS_Status.ModeConfig=1;
}
if(Parameters.NavMode)
{ strcpy(GPS_Cmd, "$PMTK886,"); // MTK command to change the navigation mode
uint8_t Len = strlen(GPS_Cmd);
GPS_Cmd[Len++]='0'+Parameters.NavMode;
Len += NMEA_AppendCheck(GPS_Cmd, Len);
GPS_Cmd[Len++]='\r';
GPS_Cmd[Len++]='\n';
GPS_Cmd[Len]=0;
Format_String(GPS_UART_Write, GPS_Cmd, Len, 0);
GPS_Status.ModeConfig=1;
}
#endif
}
if(!GPS_Status.BaudConfig) // if GPS baud config is not done yet
{ // Format_String(CONS_UART_Write, "CFG_PRT query...\n");
#ifdef WITH_GPS_UBX
// uint8_t UART1_Port=1;
// UBX_RxMsg::Send(0x06, 0x00, GPS_UART_Write, &UART1_Port, 1); // send the query for the port config to have a template configuration packet
UBX_CFG_PRT CFG_PRT; // send in blind the config message for the UART
CFG_PRT.portID=1;
CFG_PRT.reserved1=0x00;
CFG_PRT.txReady=0x0000;
CFG_PRT.mode=0x08D0;
CFG_PRT.baudRate=GPS_TargetBaudRate;
CFG_PRT.inProtoMask=3;
CFG_PRT.outProtoMask=3;
CFG_PRT.flags=0x0000;
CFG_PRT.reserved2=0x0000;
UBX_RxMsg::Send(0x06, 0x00, GPS_UART_Write, (uint8_t*)(&CFG_PRT), sizeof(CFG_PRT));
#ifdef DEBUG_PRINT
Format_String(CONS_UART_Write, "GPS <- CFG_PRT: ");
UBX_RxMsg::Send(0x06, 0x00, CONS_HexDump, (uint8_t*)(&CFG_PRT), sizeof(CFG_PRT));
Format_String(CONS_UART_Write, "\n");
#endif
UBX_RxMsg::Send(0x06, 0x00, GPS_UART_Write); // send the query for the port config to have a template configuration packet
#endif
#ifdef WITH_GPS_MTK
char GPS_Cmd[36];
strcpy(GPS_Cmd, "$PMTK251,"); // MTK command to change the baud rate
uint8_t Len = strlen(GPS_Cmd);
Len += Format_UnsDec(GPS_Cmd+Len, GPS_TargetBaudRate);
Len += NMEA_AppendCheck(GPS_Cmd, Len);
GPS_Cmd[Len++]='\r'; // this is apparently needed but it should not, as ESP32 does auto-CR ??
GPS_Cmd[Len++]='\n';
GPS_Cmd[Len]=0;
Format_String(GPS_UART_Write, GPS_Cmd, Len, 0);
{ strcpy(GPS_Cmd, "$PMTK251,"); // MTK command to change the baud rate
uint8_t Len = strlen(GPS_Cmd);
Len += Format_UnsDec(GPS_Cmd+Len, GPS_TargetBaudRate);
Len += NMEA_AppendCheck(GPS_Cmd, Len);
GPS_Cmd[Len++]='\r'; // this is apparently needed but it should not, as ESP32 does auto-CR ??
GPS_Cmd[Len++]='\n';
GPS_Cmd[Len]=0;
Format_String(GPS_UART_Write, GPS_Cmd, Len, 0); }
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "GPS <- ");
Format_String(CONS_UART_Write, GPS_Cmd, Len, 0);
xSemaphoreGive(CONS_Mutex);
#endif
#endif
#endif // WITH_GPS_MTK
#ifdef WITH_GPS_SRF
char GPS_Cmd[36];
strcpy(GPS_Cmd, "$PSRF100,1,"); // SiRF command to change the baud rate
Len = strlen(GPS_Cmd);
Len += Format_UnsDec(GPS_Cmd+Len, GPS_TargetBaudRate);
@ -234,7 +336,7 @@ static void GPS_BurstStart(void) // wh
GPS_Cmd[Len++]='\n';
GPS_Cmd[Len]=0;
Format_String(GPS_UART_Write, GPS_Cmd, Len, 0);
#endif
#endif // WITH_GPS_SRF
}
QueryWait=60;
}
@ -243,6 +345,18 @@ static void GPS_BurstStart(void) // wh
#endif // WITH_GPS_CONFIG
}
static void GPS_Random_Update(uint8_t Bit)
{ GPS_Random = (GPS_Random<<1) | (Bit&1); }
static void GPS_Random_Update(GPS_Position *Pos)
{ if(Position==0) return;
GPS_Random_Update(Pos->Altitude);
GPS_Random_Update(Pos->Speed);
GPS_Random_Update(Pos->Latitude);
GPS_Random_Update(Pos->Longitude);
if(Pos->hasBaro) GPS_Random_Update(Pos->Pressure);
XorShift32(GPS_Random); }
static void GPS_BurstComplete(void) // when GPS has sent the essential data for position fix
{
#ifdef WITH_MAVLINK
@ -262,14 +376,15 @@ static void GPS_BurstComplete(void) // wh
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(),3);
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(), 3);
Format_String(CONS_UART_Write, " -> GPS_BurstComplete() GPS:");
Format_Hex(CONS_UART_Write, GPS_Status.Flags);
Format_String(CONS_UART_Write, "\nGPS");
CONS_UART_Write('0'+PosIdx); CONS_UART_Write(':'); CONS_UART_Write(' ');
Format_String(CONS_UART_Write, "\nGPS[");
CONS_UART_Write('0'+PosIdx); CONS_UART_Write(']'); CONS_UART_Write(' ');
Format_String(CONS_UART_Write, Line);
xSemaphoreGive(CONS_Mutex);
#endif
GPS_Random_Update(Position+PosIdx);
if(Position[PosIdx].hasGPS) // GPS position data complete
{ Position[PosIdx].isReady=1; // mark this record as ready for processing => producing packets for transmission
if(Position[PosIdx].isTimeValid()) // if time is valid already
@ -277,7 +392,15 @@ static void GPS_BurstComplete(void) // wh
{ uint32_t UnixTime=Position[PosIdx].getUnixTime();
GPS_FatTime=Position[PosIdx].getFatTime();
#ifndef WITH_MAVLINK // with MAVlink we sync. with the SYSTEM_TIME message
TimeSync_SoftPPS(Burst_TickCount, UnixTime, Parameters.PPSdelay);
int32_t msDiff = Position[PosIdx].FracSec;
if(msDiff>=50) { msDiff-=100; UnixTime++; } // [0.01s]
msDiff*=10; // [ms]
// if(abs(msDiff)<=200) // if (almost) full-second burst
{ // TickType_t PPS_Age = Burst_Tick-PPS_Tick;
// if(PPS_Age>10000) TimeSync_SoftPPS(Burst_Tick, UnixTime, Parameters.PPSdelay);
// else TimeSync_SetTime(Burst_Tick-Parameters.PPSdelay, UnixTime);
TimeSync_SoftPPS(Burst_Tick, UnixTime, msDiff+Parameters.PPSdelay);
}
#endif
}
}
@ -305,7 +428,7 @@ static void GPS_BurstComplete(void) // wh
if(!Position[PrevIdx2].isValid()) break;
TimeDiff = Position[PosIdx].calcTimeDiff(Position[PrevIdx2]);
PrevIdx=PrevIdx2; }
TimeDiff=Position[PosIdx].calcDifferences(Position[PrevIdx]);
TimeDiff=Position[PosIdx].calcDifferentials(Position[PrevIdx]);
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "calcDiff() => ");
@ -317,7 +440,7 @@ static void GPS_BurstComplete(void) // wh
Format_String(CONS_UART_Write, "s\n");
xSemaphoreGive(CONS_Mutex);
#endif
LED_PCB_Flash(100); }
LED_PCB_Flash(200); }
}
else // complete but no valid lock
{ if(GPS_TimeSinceLock) { GPS_LockEnd(); GPS_TimeSinceLock=0; }
@ -339,13 +462,13 @@ static void GPS_BurstComplete(void) // wh
if(Period>0) GPS_PosPeriod = (Period+GPS_PosPipeSize/2)/(GPS_PosPipeSize-1);
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write,"GPS");
CONS_UART_Write('0'+PosIdx); CONS_UART_Write(':'); CONS_UART_Write(' ');
Format_String(CONS_UART_Write,"GPS[");
CONS_UART_Write('0'+PosIdx); CONS_UART_Write(']'); CONS_UART_Write(' ');
Format_UnsDec(CONS_UART_Write, (uint16_t)Position[PosIdx].Sec, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, (uint16_t)Position[PosIdx].FracSec, 2);
Format_String(CONS_UART_Write, "s ");
Format_SignDec(CONS_UART_Write, Period, 3, 2);
Format_UnsDec(CONS_UART_Write, GPS_PosPeriod, 3, 2);
Format_String(CONS_UART_Write, "s\n");
xSemaphoreGive(CONS_Mutex);
#endif
@ -367,15 +490,15 @@ static void GPS_BurstEnd(void) // wh
// ----------------------------------------------------------------------------
GPS_Position *GPS_getPosition(uint8_t &BestIdx, int16_t &BestRes, int8_t Sec, int8_t Frac) // return GPS position closest to the given Sec.Frac
{ int16_t TargetTime = Frac+(int16_t)Sec*100;
{ int16_t TargetTime = Frac+(int16_t)Sec*100; // target time including the seconds
BestIdx=0; BestRes=0x7FFF;
for(uint8_t Idx=0; Idx<GPS_PosPipeSize; Idx++)
for(uint8_t Idx=0; Idx<GPS_PosPipeSize; Idx++) // run through the GPS positions stored in the pipe
{ GPS_Position *Pos=Position+Idx;
if(!Pos->isReady) continue;
int16_t Diff = TargetTime - (Pos->FracSec + (int16_t)Pos->Sec*100);
if(Diff<(-3000)) Diff+=6000;
else if(Diff>3000) Diff-=6000;
if(fabs(Diff)<fabs(BestRes)) { BestRes=Diff; BestIdx=Idx; }
if(!Pos->isReady) continue; // skip those not-ready yet
int16_t Diff = TargetTime - (Pos->FracSec + (int16_t)Pos->Sec*100); // difference from the target time
if(Diff<(-3000)) Diff+=6000; // wrap-around 60 sec
else if(Diff>=3000) Diff-=6000;
if(fabs(Diff)<fabs(BestRes)) { BestRes=Diff; BestIdx=Idx; } // store the smallest difference from target
}
return BestRes==0x7FFF ? 0:Position+BestIdx; }
@ -399,25 +522,17 @@ GPS_Position *GPS_getPosition(int8_t Sec) // retu
static void GPS_NMEA(void) // when GPS gets a correct NMEA sentence
{ GPS_Status.NMEA=1;
GPS_Status.BaudConfig = (GPS_getBaudRate() == GPS_TargetBaudRate);
LED_PCB_Flash(2); // Flash the LED for 2 ms
Position[PosIdx].ReadNMEA(NMEA); // read position elements from NMEA
LED_PCB_Flash(10); // Flash the LED for 2 ms
if(NMEA.isGxGSV()) ProcessGSV(NMEA); // process satellite data
Position[PosIdx].ReadNMEA(NMEA); // read position elements from NMEA
if(NMEA.isGxRMC()) GPS_Burst.GxRMC=1;
if(NMEA.isGxGGA()) GPS_Burst.GxGGA=1;
if(NMEA.isGxGSA()) GPS_Burst.GxGSA=1;
if(Button_SleepRequest)
{
#ifdef WITH_GPS_MTK
#ifdef WITH_GPS_ENABLE
GPS_DISABLE();
#endif
Format_String(GPS_UART_Write, /* "$PMTK161,0*28\r\n", */ "$PMTK225,4*2F\r\n", 15, 0); // 225 or "$PMTK161,0*28\r\n" request to the GPS to enter sleep
#endif
}
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(),3);
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(), 3);
Format_String(CONS_UART_Write, " -> ");
Format_Bytes(CONS_UART_Write, NMEA.Data, 6);
CONS_UART_Write(' '); Format_Hex(CONS_UART_Write, GPS_Burst.Flags);
@ -429,7 +544,7 @@ static void GPS_NMEA(void) // wh
if( NMEA.isP() || NMEA.isGxRMC() || NMEA.isGxGGA() || NMEA.isGxGSA() || NMEA.isGPTXT() )
// we would need to patch the GGA here for the GPS which does not calc. nor correct for GeoidSepar
#endif
{ // if(CONS_UART_Free()>=128)
{ if(Parameters.Verbose)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, (const char *)NMEA.Data, 0, NMEA.Len);
Format_String(CONS_UART_Write, "\n");
@ -460,7 +575,7 @@ static void DumpUBX(void)
static void GPS_UBX(void) // when GPS gets an UBX packet
{ GPS_Status.UBX=1;
GPS_Status.BaudConfig = (GPS_getBaudRate() == GPS_TargetBaudRate);
LED_PCB_Flash(2);
LED_PCB_Flash(10);
// DumpUBX();
// Position[PosIdx].ReadUBX(UBX);
#ifdef WITH_GPS_UBX_PASS
@ -477,7 +592,7 @@ static void GPS_UBX(void)
{ class UBX_CFG_PRT *CFG = (class UBX_CFG_PRT *)UBX.Word; // create pointer to the packet content
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "TaskGPS: CFG_PRT\n");
Format_String(CONS_UART_Write, "CFG_PRT: ");
DumpUBX();
Format_Hex(CONS_UART_Write, CFG->portID);
CONS_UART_Write(':');
@ -504,14 +619,15 @@ static void GPS_UBX(void)
{ class UBX_CFG_NAV5 *CFG = (class UBX_CFG_NAV5 *)UBX.Word;
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "TaskGPS: CFG_NAV5 ");
Format_String(CONS_UART_Write, "CFG_NAV5: ");
Format_Hex(CONS_UART_Write, CFG->dynModel);
Format_String(CONS_UART_Write, "\n");
xSemaphoreGive(CONS_Mutex);
#endif
if(CFG->dynModel==GPS_TargetDynModel) GPS_Status.ModeConfig=1; // dynamic model = 6 => Airborne with >1g acceleration
// if(CFG->dynModel==GPS_TargetDynModel) GPS_Status.ModeConfig=1; // dynamic model = 6 => Airborne with >1g acceleration
if(CFG->dynModel==Parameters.NavMode) GPS_Status.ModeConfig=1; // dynamic model = 6 => Airborne with >1g acceleration
else
{ CFG->dynModel=GPS_TargetDynModel; CFG->mask = 0x01; //
{ CFG->dynModel=Parameters.NavMode; CFG->mask = 0x01; //
UBX.RecalcCheck(); // reclaculate the check sum
UBX.Send(GPS_UART_Write); // send this UBX packet
}
@ -529,7 +645,7 @@ static void GPS_UBX(void)
xSemaphoreGive(CONS_Mutex);
/*
if(UBX.Byte[0]==0x06 && UBX.Byte[1]==0x00 && UBX.ID==0) // negative ACK to CFG-PRT
{ static char GPS_Cmd[36];
{
strcpy(GPS_Cmd, "$PUBX,41,1,0007,0003,"); // $PUBX command to change the baud rate
uint8_t Len = strlen(GPS_Cmd);
Len += Format_UnsDec(GPS_Cmd+Len, GPS_TargetBaudRate);
@ -582,7 +698,7 @@ static uint64_t MAV_getUnixTime(void) // [m
static void GPS_MAV(void) // when GPS gets an MAV packet
{ TickType_t TickCount=xTaskGetTickCount();
GPS_Status.MAV=1;
LED_PCB_Flash(2);
LED_PCB_Flash(10);
GPS_Status.BaudConfig = (GPS_getBaudRate() == GPS_TargetBaudRate);
uint8_t MsgID = MAV.getMsgID();
uint64_t UnixTime_ms = MAV_getUnixTime(); // get the time from the MAVlink message
@ -615,7 +731,7 @@ static void GPS_MAV(void) // wh
uint32_t UnixTime = UnixTime_ms/1000; // [ s] Unix Time
uint32_t UnixFrac = UnixTime_ms-(uint64_t)UnixTime*1000; // [ms] Second fraction of the Unix time
MAV_TimeOfs_ms=UnixTime_ms-SysTime->time_boot_ms; // [ms] difference between the Unix Time and the Ardupilot time-since-boot
TimeSync_SoftPPS(TickCount-UnixFrac, UnixTime, 70);
TimeSync_SoftPPS(TickCount-UnixFrac, UnixTime, Parameters.PPSdelay);
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "MAV_SYSTEM_TIME: ");
@ -736,9 +852,9 @@ void vTaskGPS(void* pvParameters)
GPS_Status.Flags = 0;
// PPS_TickCount=0;
Burst_TickCount=0;
Burst_Tick=0;
vTaskDelay(5);
vTaskDelay(5); // put some initial delay for lighter startup load
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "TaskGPS:");
@ -789,6 +905,7 @@ void vTaskGPS(void* pvParameters)
uint16_t MaxBytesPerTick = 1+(GPS_getBaudRate()+2500)/5000;
for( ; ; ) // loop over bytes in the GPS UART buffer
{ uint8_t Byte; int Err=GPS_UART_Read(Byte); if(Err<=0) break; // get Byte from serial port, if no bytes then break this loop
// CONS_UART_Write(Byte); // copy the GPS output to console (for debug only)
Bytes++;
LineIdle=0; // if there was a byte: restart idle counting
NMEA.ProcessByte(Byte); // process through the NMEA interpreter
@ -827,23 +944,40 @@ void vTaskGPS(void* pvParameters)
#endif
*/
if(LineIdle==0) // if any bytes were received ?
{ if(!GPS_Burst.Active) GPS_BurstStart(); // burst started
{ if(!GPS_Burst.Active) GPS_BurstStart(); // if not already started then declare burst started
GPS_Burst.Active=1;
if( (!GPS_Burst.Complete) && (GPS_Burst.GxGGA && GPS_Burst.GxRMC && GPS_Burst.GxGSA) )
{ GPS_Burst.Complete=1; GPS_BurstComplete(); }
if( (!GPS_Burst.Complete) && (GPS_Burst.GxGGA && GPS_Burst.GxRMC && GPS_Burst.GxGSA) ) // if GGA+RMC+GSA received
{ GPS_Burst.Complete=1; GPS_BurstComplete(); } // declare burst complete
}
else if(LineIdle>=GPS_BurstTimeout) // if GPS sends no more data for 10 time ticks
{ if(GPS_Burst.Active) // if still in burst
{ if(!GPS_Burst.Complete) GPS_BurstComplete();
GPS_BurstEnd(); } // burst just ended
else if(LineIdle>=GPS_BurstTimeout) // if GPS sends no more data for GPS_BurstTimeout ticks
{ if(GPS_Burst.Active) // if burst was active
{ if(!GPS_Burst.Complete && GPS_Burst.GxGGA && GPS_Burst.GxRMC) GPS_BurstComplete(); // if not complete yet, then declare burst complete
GPS_BurstEnd(); } // declare burst ended
else if(LineIdle>=1500) // if idle for more than 1.5 sec
{ GPS_Status.Flags=0; }
GPS_Burst.Flags=0;
GPS_Burst.Flags=0; // clear all flags: active and complete
}
if(NoValidData>=2000) // if no valid data from GPS for 1sec
{ GPS_Status.Flags=0; GPS_Burst.Flags=0; // assume GPS state is unknown
uint32_t NewBaudRate = GPS_nextBaudRate(); // switch to the next baud rate
if(PowerMode>0)
{
#ifdef WITH_GPS_UBS
#ifdef WITH_GPS_ENABLE
GPS_ENABLE();
#endif
GPS_UART_Write('\n');
#endif
#ifdef WITH_GPS_MTK
#ifdef WITH_GPS_ENABLE
GPS_DISABLE();
vTaskDelay(1);
GPS_ENABLE();
#endif
GPS_UART_Write('\n');
#endif
}
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "TaskGPS: ");
Format_UnsDec(CONS_UART_Write, NewBaudRate);
@ -854,3 +988,5 @@ void vTaskGPS(void* pvParameters)
}
}
}

Wyświetl plik

@ -9,6 +9,8 @@
const uint8_t GPS_PosPipeSize = 4; // number of GPS positions held in a pipe
// extern uint8_t GPS_PowerMode; // 0=shutdown, 1=reduced, 2=normal
extern uint32_t GPS_FatTime; // [2 sec] UTC time in FAT format (for FatFS)
extern int32_t GPS_Altitude; // [0.1m] altitude (height above Geoid)
extern int32_t GPS_Latitude; // [0.0001/60 deg]
@ -16,7 +18,9 @@ extern int32_t GPS_Longitude; // [0.0001/60 deg]
extern int16_t GPS_GeoidSepar; // [0.1m]
extern uint16_t GPS_LatCosine; // [1.0/(1<<12)] Latitude Cosine for distance calculations
extern uint32_t GPS_TimeSinceLock; // [sec] time since GPS has a valid lock
extern uint32_t GPS_Random; // random number produced from the GPS data
extern uint16_t GPS_PosPeriod; // [0.01sec] how often (which period) the GPS/MAV is sending the positions
extern uint16_t GPS_SatSNR; // [0.25dB] average SNR for satellites being used for navigation
typedef union
{ uint8_t Flags;
@ -26,8 +30,8 @@ typedef union
bool MAV:1; // got at least one valid MAV message
bool PPS:1; // got at least one PPS signal
bool BaudConfig:1; // baudrate is configured
bool ModeConfig:1; // mode is configured
bool :1; //
bool ModeConfig:1; // navigation mode is configured
bool RateConfig:1; // navigation rate is configured
bool :1; //
} ;
} Status;

Plik diff jest za duży Load Diff

Wyświetl plik

@ -26,10 +26,18 @@
// ============================================================================================================
#ifdef WITH_U8G2
extern uint8_t PowerMode; // 0=sleep/minimal power, 1=comprimize, 2=full power
// ============================================================================================================
#ifdef WITH_U8G2_OLED
#include "u8g2.h"
#endif
#ifdef WITH_ST7789
#include "st7789.h"
#endif
extern uint8_t BARO_I2C;
#ifdef WITH_MAVLINK
@ -81,7 +89,7 @@ int OLED_SetContrast(uint8_t Contrast, uint8_t DispIdx=0);
int OLED_PutLine(uint8_t Line, const char *Text, uint8_t DispIdx=0);
#endif
#ifdef WITH_U8G2
#ifdef WITH_U8G2_OLED
extern u8g2_t U8G2_OLED;
#endif
@ -94,7 +102,11 @@ int SD_getSectorSize(void);
#endif
#ifdef WITH_BEEPER
#ifdef WITH_KNOB
extern volatile uint8_t KNOB_Tick;
#else
const uint8_t KNOB_Tick=15; // for now, when there is no knob
#endif
const uint8_t Play_Vol_0 = 0x00;
const uint8_t Play_Vol_1 = 0x40;
@ -115,6 +127,20 @@ void Beep(uint16_t Freq, uint8_t Duty, uint8_t DoubleAmpl);
void Beep_Note(uint8_t Note);
#endif // WITH_BEEPER
#ifdef WITH_SOUND
const uint32_t Sound_SampleRate = 16000;
void Sound_PlaySilence(uint16_t Len);
void Sound_PlayU8(const uint8_t *Data, uint16_t Len);
void Sound_PlayS8(const int8_t *Data, uint16_t Len, uint8_t Vol=8);
void Sound_Beep(int16_t Freq, uint16_t Len, int16_t Ampl);
#endif
#ifdef WITH_VARIO
extern uint8_t Vario_Note;
extern uint16_t Vario_Period;
extern uint16_t Vario_Fill;
#endif
void LED_PCB_On (void); // LED on the PCB for vizual indications
void LED_PCB_Off (void);
void LED_PCB_Flash(uint8_t Time=100); // Flash the PCB LED for a period of [ms]
@ -133,7 +159,7 @@ void LED_RX_Flash(uint8_t Time=100);
void LED_TimerCheck(uint8_t Ticks=1);
extern bool Button_SleepRequest;
// extern bool Button_SleepRequest;
int32_t Button_TimerCheck(uint8_t Ticks=1);
void IO_Configuration(void); // Configure I/O
@ -152,6 +178,9 @@ int SPIFFS_Info(size_t &Total, size_t &Used, const char *Label=0);
uint8_t I2C_Read (uint8_t Bus, uint8_t Addr, uint8_t Reg, uint8_t *Data, uint8_t Len, uint8_t Wait=10);
uint8_t I2C_Write(uint8_t Bus, uint8_t Addr, uint8_t Reg, uint8_t *Data, uint8_t Len, uint8_t Wait=10);
inline uint8_t I2C_Write(uint8_t Bus, uint8_t Addr, uint8_t Reg, uint8_t Byte, uint8_t Wait=10)
{ return I2C_Write(Bus, Addr, Reg, &Byte, 1, Wait); }
template <class Type>
inline uint8_t I2C_Write(uint8_t Bus, uint8_t Addr, uint8_t Reg, Type &Object, uint8_t Wait=10)
{ return I2C_Write(Bus, Addr, Reg, (uint8_t *)&Object, sizeof(Type), Wait); }
@ -163,5 +192,22 @@ template <class Type>
uint8_t I2C_Restart(uint8_t Bus);
uint16_t BatterySense(int Samples=4); // [mV]
#ifdef WITH_TBEAM
uint16_t KnobSense (int Samples=4); // [ADC]
#endif
#ifdef WITH_BQ
#include "bq24295.h"
extern BQ24295 BQ;
#endif
#ifdef WITH_AXP
#include "axp192.h"
extern AXP192 AXP;
#endif
#ifdef WITH_SLEEP
void Sleep(void);
#endif
#endif // __HAL_H__

11
main/knob.h 100644
Wyświetl plik

@ -0,0 +1,11 @@
#ifdef WITH_KNOB
extern volatile uint8_t KNOB_Tick;
#ifdef __cplusplus
extern "C"
#endif
void vTaskKNOB(void* pvParameters);
#endif

50
main/lcd_battery.h 100644
Wyświetl plik

@ -0,0 +1,50 @@
#include <stdint.h>
#include "st7789.h"
class LCD_BattSymb
{ public:
static const uint8_t Width = 24;
static const uint8_t Border = 2;
static const uint8_t Cells = 5;
static const uint8_t CellWidth = Width-Border*4;
static const uint8_t CellLength = CellWidth*3/4;
static const uint8_t Length = (CellLength+Border)*Cells+Border*3;
static const uint8_t TipLen = Border*2;
int16_t Xpos, Ypos;
int16_t FrameCol, CellCol, FillCol;
uint8_t CellMap; // which cells are to be displayed
uint8_t Flags; // which cells and other elements are displayed
public:
LCD_BattSymb() { CellMap=0; Xpos=0; Ypos=0; FillCol=RGB565_LIGHTGREY; CellCol=RGB565_DARKGREEN; FrameCol=RGB565_BLACK; Flags=0; }
void setLevel(uint8_t Level)
{ CellMap=0; uint8_t Mask=1;
if(Level>Cells) Level=Cells;
for(uint8_t Cell=0; Cell<Level; Cell++)
{ CellMap|=Mask; Mask<<=1; } // set cells to be displayed after the battery level
}
void Draw(void)
{ LCD_DrawBox(Xpos, Ypos, Length, Width, FrameCol); // frame box
LCD_DrawBox(Xpos+Length, Ypos+Width/4, TipLen, Width/2, FrameCol); // tip
LCD_DrawBox(Xpos+Border, Ypos+Border, Length-Border*2, Width-Border*2, FillCol); // inner space
uint8_t Mask=1;
for(uint8_t Cell=0; Cell<Cells; Cell++)
{ if(CellMap&Mask)
{ uint16_t Xofs=Border*2+(CellLength+Border)*Cell;
LCD_DrawBox(Xpos+Xofs, Ypos+Border*2, CellLength, CellWidth, CellCol); }
Mask<<=1; }
Flags = 0x80 | CellMap; }
void Update(void)
{ uint8_t Mask=1;
for(uint8_t Cell=0; Cell<Cells; Cell++)
{ if((CellMap^Flags)&Mask)
{ uint16_t Xofs=Border*2+(CellLength+Border)*Cell;
LCD_DrawBox(Xpos+Xofs, Ypos+Border*2, CellLength, CellWidth, CellMap&Mask ? CellCol:FillCol); } // display or erase a cell
Mask<<=1; }
Flags = 0x80 | CellMap; }
} ;

495
main/lookout.h 100644
Wyświetl plik

@ -0,0 +1,495 @@
#ifndef __LOKOUT_H__
#define __LOKOUT_H__
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "intmath.h"
// #define DEBUG_PRINT
#include "relpos.h"
// =======================================================================================================
class LookOut_Target
{ public:
uint32_t ID; // ID of the target = aircraft ID
Acft_RelPos Pos; // Position relative to the reference Lat/Lon/Alt
int8_t Pred; // [0.5sec] amount of time by which this position has been predicted/extrapolated
uint8_t GpsPrec; // GPS position error including prediction
union
{ uint8_t Flags; // flags
struct
{ bool isMoving :1; // is a moving target
bool Alloc :1; // is allocated or not (a free slot, where a new target can go into)
// bool Reported :1; // this target has already been reported with $PFLAA
} ;
} ;
union
{ uint32_t Rank; // rank: lowest means shorter time margin, shorter distance margin thus bigger thread
struct
{ uint16_t DistMargin; // [0.5m] remaining safety margin: if positive, then considered no thread at all
uint8_t TimeMargin; // [0.5s] time to target (if no distance margin left)
uint8_t WarnLevel; // assigned warning level: 0, 1, 2 or 3
} ;
} ;
int16_t dX; // [0.5m] relative position of target
int16_t dY; // [0.5m]
int16_t dZ; // [0.5m]
int16_t Vx; // [0.5m/s] relative speed of target
int16_t Vy; // [0.5m/s]
int16_t Vz; // [0.5m/s]
// int16_t Ax; // [1/16m/s^2] relative acceleration of target
// int16_t Ay; // [1/16m/s^2]
uint16_t HorDist; // [0.5m] relltive hor. distance to target
int16_t MissTime; // [0.5s] estimated closest approach time
uint16_t MissDist; // [0.5m] estimated closest approach distance
public:
void Clear(void) { Pred=0; Flags=0; HorDist=0; MissDist=0; Rank=0xFFFF; }
// uint16_t HorRelSpeed(void) const { }
uint32_t DistSqr(void) const { return (int32_t)dX*dX + (int32_t)dY*dY + (int32_t)dZ*dZ; } // [0.25m^2] Distance-square to the Target
uint32_t VelSqr (void) const { return (int32_t)Vx*Vx + (int32_t)Vy*Vy + (int32_t)Vz*Vz; } // [0.5m/s^2] Relative velocity square of the Target
void calcVel(Acft_RelPos &RefPos) // calc. relative target velocity against a reference position
{ Pos.getSpeedVector(Vx, Vy); // get horizontal speed vector from Speed and Heading
int16_t rVx, rVy; RefPos.getSpeedVector(rVx, rVy); // same for the reference position
Vx -= rVx; Vy -=rVy; // difference
Vz = Pos.Climb-RefPos.Climb; } // vertical different
int16_t getBearing (void) const { return IntAtan2(dY, dX); } // bearing to the target
uint16_t getHorDist (void) const { return Acft_RelPos::FastDistance(dX, dY); } // relative horizontal distance to the target
uint16_t getRelHorSpeed(void) const { return Acft_RelPos::FastDistance(Vx, Vy); } // relative horiz. speed of the target
void Print(void) const
{ printf("%08lX/%+5.1fs/%7.1fm/%7.1fm/%5.1fs/%5.1fm/%+5.1fs/w%d", (long int)ID,
0.5*Pred, 0.5*DistMargin, 0.5*HorDist, 0.5*TimeMargin, 0.5*MissDist, 0.5*MissTime, WarnLevel);
// printf(" [%+7.1f,%+7.1f,%+7.1f]m [%+5.1f,%+5.1f,%+5.1f]m/s", 0.5*dX, 0.5*dY, 0.5*dZ, 0.5*Vx, 0.5*Vy, 0.5*Vz);
// printf(" [%+5.2f,%+5.2f]m/s^-2", 0.0625*Ax, 0.0625*Ay);
Pos.Print(); }
uint8_t Print(char *output)
{ uint8_t Len=0;
return Len; }
uint8_t WritePFLAA(char *NMEA)
{ uint8_t Len=0;
Len+=Format_String(NMEA+Len, "$PFLAA,"); // sentence name and alarm-level (but no alarms for trackers)
NMEA[Len++]='0'+WarnLevel;
NMEA[Len++]=',';
Len+=Format_SignDec(NMEA+Len, dX/2);
NMEA[Len++]=',';
Len+=Format_SignDec(NMEA+Len, dY/2);
NMEA[Len++]=',';
Len+=Format_SignDec(NMEA+Len, dZ/2); // [m] relative altitude
NMEA[Len++]=',';
NMEA[Len++]='0'+((ID>>24)&0x03); // address-type (3=OGN)
NMEA[Len++]=',';
uint32_t Addr = ID&0xFFFFFF; // [24-bit] address
Len+=Format_Hex(NMEA+Len, (uint8_t)(Addr>>16)); // 24-bit address: RND, ICAO, FLARM, OGN
Len+=Format_Hex(NMEA+Len, (uint16_t)Addr);
NMEA[Len++]=',';
Len+=Format_UnsDec(NMEA+Len, (225*Pos.Heading+0x800)>>12, 4, 1); // [deg] heading (by GPS)
NMEA[Len++]=',';
Len+=Format_SignDec(NMEA+Len, (225*Pos.Turn+0x800)>>12, 2, 1); // [deg/sec] turn rate
NMEA[Len++]=',';
Len+=Format_UnsDec(NMEA+Len, 5*Pos.Speed, 2, 1); // [approx. m/s] ground speed
NMEA[Len++]=',';
Len+=Format_SignDec(NMEA+Len, 5*Pos.Climb, 2, 1); // [m/s] climb/sink rate
NMEA[Len++]=',';
NMEA[Len++]=HexDigit(ID>>26); // [0..F] aircraft-type: 1=glider, 2=tow plane, etc.
Len+=NMEA_AppendCheckCRNL(NMEA, Len);
NMEA[Len]=0;
return Len; } // return number of formatted characters
} ;
// =======================================================================================================
class LookOut
{ public:
uint32_t ID; // ID of me (own aircraft)
Acft_RelPos Pos; // Position relative to the reference Lat/Lon/Alt
int8_t Pred; // [0.5sec] amount of time by which position has been predicted/extrapolated
union
{ uint8_t Flags;
struct
{ uint8_t GpsPrec :6; // GPS position precision
bool isMoving :1; // own position moving
bool hasPosition :1; // own position is valid
} ;
} ;
uint8_t WarnLevel; // highest warning level of all the targets
uint8_t WeakestIdx; // index for the weakest target (or a not allocated target)
uint32_t WeakestRank; // rank of the weakest target
uint8_t Targets; // [aircrafts] actual number of targets monitored
uint8_t WorstTgtIdx; // [] most dangereous target
uint8_t WorstTgtTime; // [0.5s] time to closest approach
uint8_t RefTime; // [sec] ref. T for the local T,X,Y,Z coord. system
int16_t LatCos; // [2^-12]
// ref. X,Y,Z for the local T,X,Y,Z coord. system
int32_t RefLat; // [1/60000deg]
int32_t RefLon; // [1/60000deg]
int32_t RefAlt; // [m]
const static uint8_t MaxTargets = 32; // maximum number of targets
LookOut_Target Target[MaxTargets]; // array of Targets
const static int32_t DistRange = 7000; // [m] drop immediately anything beyond this distance
const static int16_t MinHorizSepar = 30; // [m] minimum horizontal separation
const static int16_t MinVertSepar = 20; // [m] minimum vertical separation
const static int16_t WarnTime = 20; // [sec] target warning prior to impact
Acft_RelPos PredMe, PredTgt; // for temporary storage for predictions.
char Line[80]; // for printing
public:
void Clear(void)
{ Flags=0; ID=0; Pos.Clear(); Pred=0;
Targets=0; WeakestIdx=0; WeakestRank=0xFFFFFFFF;
WorstTgtIdx=0; WorstTgtTime=0xFF;
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ Target[Idx].Clear(); }
}
int16_t getRelBearing(const LookOut_Target *Tgt) const // [360/0x10000 deg] relative bearing to the target
{ return Tgt->getBearing()-Pos.Heading; }
uint8_t WritePOGNA(char *NMEA, const LookOut_Target *Tgt) // Alert NMEA centence
{ uint8_t Len=0;
Len+=Format_String(NMEA+Len, "$POGNA,");
Len+=NMEA_AppendCheckCRNL(NMEA, Len);
NMEA[Len]=0;
return Len; }
void PrintPFLA(void) // print (for debug) $PFLAU and PFLAA
{ WritePFLAU(Line); printf("%s", Line);
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ if(!Target[Idx].Alloc) continue;
if( Target[Idx].DistMargin) continue;
Target[Idx].WritePFLAA(Line);
printf("%s", Line);
}
}
void WritePFLA(void (*Output)(char)) // produce $PFLAU and PFLAA on the console output
{ WritePFLAU(Line); Format_String(Output, Line);
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ if(!Target[Idx].Alloc) continue;
if( Target[Idx].DistMargin) continue;
Target[Idx].WritePFLAA(Line);
Format_String(Output, Line);
}
}
uint8_t WritePFLAU(char *NMEA) // produce the FLAM anti-collision status
{ const LookOut_Target *Tgt = 0;
if(WarnLevel>0) Tgt = Target + WorstTgtIdx;
uint8_t Len=0;
Len+=Format_String(NMEA+Len, "$PFLAU,");
Len+=Format_UnsDec(NMEA+Len, Targets, 1); // number of targets received
NMEA[Len++]=',';
NMEA[Len++]='0'+hasPosition; // TX status
NMEA[Len++]=',';
NMEA[Len++]='0'+hasPosition; // GPS status: 0=no fix, 1=on the ground, 2=airborne
NMEA[Len++]=',';
NMEA[Len++]='1'; // power status: one could monitor the supply
NMEA[Len++]=',';
NMEA[Len++]='0'+WarnLevel; // Warning level: 0..3
NMEA[Len++]=',';
if(Tgt) // [deg] relative bearing: -180..+180
{ Len+=Format_SignDec(NMEA+Len, ((int32_t)getRelBearing(Tgt)*45+0x1000)>>13, 1); }
NMEA[Len++]=',';
NMEA[Len++]='0'+((WarnLevel>0)<<1); // alarm-type: 0=none, 2=aircraft, 3=obstacle/zone/terrain
NMEA[Len++]=',';
if(Tgt) // [m] relative vertical distance
{ Len+=Format_SignDec(NMEA+Len, (Tgt->dZ)>>1, 1); }
NMEA[Len++]=',';
if(Tgt) // [m] relative horizontal distance
{ Len+=Format_UnsDec(NMEA+Len, (Tgt->HorDist)>>1, 1); }
if(Tgt) // ID
{ NMEA[Len++]=',';
Len+=Format_Hex(NMEA+Len, Tgt->ID); }
Len+=NMEA_AppendCheckCRNL(NMEA, Len);
NMEA[Len]=0;
return Len; }
void Print(void) const
{ if(!hasPosition) return;
printf("Ref: %02d: [%+10.6f, %+11.6f]deg %ldm\n", RefTime, 0.0001/60*RefLat, 0.0001/60*RefLon, (long int)RefAlt);
printf("%08lX/%+5.1fs/ Margin/ HorDist/Margin/ Miss/ Miss/w%d", (long int)ID, 0.5*Pred, WarnLevel); Pos.Print();
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ const LookOut_Target *Tgt = Target+Idx;
if(Tgt->Alloc) Tgt->Print();
}
}
template <class OGNx_Packet>
int32_t Start(OGNx_Packet &Me)
{ Clear();
ID = Me.getAddressAndType() | ((uint32_t)Me.Position.AcftType<<26) ;
RefTime = Me.Position.Time;
RefLat = Me.DecodeLatitude();
RefLon = Me.DecodeLongitude();
RefAlt = Me.DecodeAltitude();
LatCos = Icos(GPS_Position::calcLatAngle16(RefLat));
Pred=0;
return Pos.Read(Me, RefTime, RefLat, RefLon, RefAlt, LatCos, DistRange); }
template <class OGNx_Packet>
const LookOut_Target *ProcessOwn(OGNx_Packet &Me) // process my own position
{ // printf("ProcessOwn() ... entry\n");
if(hasPosition) // in my position is valid
{ Pred=0;
if(Pos.Read(Me, RefTime, RefLat, RefLon, RefAlt, LatCos, DistRange)<0) // read the new position
{ hasPosition = Start(Me)>=0; } // if this fails, attempt to start from the new position
}
else
{ hasPosition = Start(Me)>=0;
}
if(hasPosition) // if already started
{ AdjustRefTime(Pos.T); // adjust time ref. point if needed
AdjustRefAlt(); // adjust vertical ref. altitude if needed
AdjustRefLatLon(Me); } // adjuest horizontal Lat/Lon position if needed.
WarnLevel=0;
Targets=0;
WorstTgtIdx=0;
WorstTgtTime=0xFF;
for(uint8_t Idx=0; Idx<MaxTargets; Idx++) // go over targets
{ LookOut_Target *Tgt = Target+Idx;
if(!Tgt->Alloc) continue; // skip empty slots
if(Tgt->DistMargin==0) // those with no safety margin
{ while(Tgt->Pos.T<=(Pos.T-4)) // bring closer in time to my (new) position
{ Tgt->Pos.StepFwd2secs(); Tgt->Pred+=4; }
}
uint8_t Warn=calcTarget(Tgt); // (re)calculate the target
if(Warn)
{ if(Warn>WarnLevel) WarnLevel=Warn;
if(Tgt->TimeMargin<WorstTgtTime) { WorstTgtTime=Tgt->TimeMargin; WorstTgtIdx=Idx; }
}
Targets++; }
// printf("ProcessOwn() ... exit\n");
// if(Targets==0) return 0; // return NULL if no targets are tracked
LookOut_Target *Tgt = Target+WorstTgtIdx;
if( (!Tgt->Alloc) || (Tgt->DistMargin>0) ) return 0; // return NULL if target is not a thread
return Tgt; } // return the pointer to the most dangerous target
template <class OGNx_Packet>
const LookOut_Target *ProcessTarget(OGNx_Packet &Packet) // process positions of other aircrafts
{ // printf("ProcessTarget(%d) ... entry\n", WeakestIdx);
LookOut_Target *New = Target+WeakestIdx; // get a free or lowest rank slot
New->Clear();
if(New->Pos.Read(Packet, RefTime, RefLat, RefLon, RefAlt, LatCos, DistRange)<0) return 0; // calculate the position against the reference position
uint32_t ID = Packet.getAddressAndType() | ((uint32_t)Packet.Position.AcftType<<26) ; // get ID
New->ID = ID; // set ID of this position
// printf("ProcessTarget() ... %08X\n", ID);
uint8_t OldIdx;
for(OldIdx=0; OldIdx<MaxTargets; OldIdx++) // scan targets already on the list
{ if(Target[OldIdx].Alloc==0) continue;
if(OldIdx==WeakestIdx) continue;
if(Target[OldIdx].ID==ID) break; } // to find previous position for the target
if(OldIdx<MaxTargets) // if found
{ if((Target[OldIdx].Pos.T-Target[OldIdx].Pred)>Target[WeakestIdx].Pos.T) return Target+OldIdx; // if position is not really older than stop processing this (not new) position
Target[OldIdx].Alloc=0; } // mark old position as "not allocated"
New->Alloc=1; // mark this position as allocated
AdjustRefTime(New->Pos.T); // possibly adjust the time reference after this new position time
// printf("ProcessTarget() ... AdjustRefTime()\n");
if(Pos.T<=(New->Pos.T-4)) // bring my position closer in time
{ Pos.StepFwd2secs(); Pred+=4; }
uint8_t Warn=calcTarget(New); // calculate the safety margin for the target
if(Warn>WarnLevel) WarnLevel=Warn;
// printf("ProcessTarget() ... calc()\n");
uint8_t MaxIdx=WeakestIdx; uint16_t Max=New->Rank; // look for the lowest rank position on the list
for( uint8_t Idx=MaxIdx; ; ) // go over targets
{ Idx++; if(Idx>=MaxTargets) Idx=0;
if(Idx==WeakestIdx) break; // end the loop when back at New
LookOut_Target &Tgt = Target[Idx];
if(!Tgt.Alloc) { MaxIdx=Idx; break; } // if unallocated target found: stop the search
if(Tgt.Rank==0xFFFF) { MaxIdx=Idx; break; } // if abs. weakest target found: stop the search
if(Tgt.Rank>=Max) { Max=Tgt.Rank; MaxIdx=Idx; } // if weaker rank found: note it
}
WeakestIdx=MaxIdx; // tqke the weakest slot for the nest time
return New; }
uint8_t calcTarget(LookOut_Target *Tgt) // calculate the savety margin for the (new) target
{
Tgt->TimeMargin=0xFF; // initially set inf. time margin
Tgt->WarnLevel=0; // warning level=0
Tgt->MissTime=0;
Tgt->MissDist=0;
uint16_t Margin = calcVertMargin(Tgt); // [0.5m] calc. vertical margin
if(Margin==0) Margin = calcHorizMargin(Tgt); // [0.5m] if vertical margin iz zero then get horizontal margin
Tgt->DistMargin = Margin; // [0.5m]
if(Margin>0) // if there is still safety margin, no more calc. (dealloc. ?)
{ // Tgt->TimeMargin = Margin/;
return 0; } // return warning level = 0
// at this point the distance is possibly small enough so we may hit the target
#ifdef DEBUG_PRINT
printf("calcTarget(0x%08X) ... no abs. safety margin\n", Tgt->ID);
#endif
Tgt->calcVel(Pos); // calculate relative velocity
// uint32_t RelVelSqr = Tgt->VelSqr(); // [0.25(m/s)^2] velocity square
int16_t dT = Pos.T - Tgt->Pos.T; // [0.5s] we need to recalc. the distance if the target time is not same as mine
if(dT) // [0.5s] if time difference is non-zero
{ int16_t Vx,Vy,Vz; // [0.5m/s] Target speed vector
Tgt->Pos.getSpeedVector(Vx, Vy); Vz=Tgt->Pos.Climb; // [0.5m/s]
Tgt->dX += (dT*Vx)>>1; // update Target relative distance
Tgt->dY += (dT*Vy)>>1;
Tgt->dZ += (dT*Vz)>>1;
Tgt->HorDist = Acft_RelPos::FastDistance(Tgt->dX, Tgt->dY); } // update Target horizontal distance
// uint32_t RelDistSqr = Tgt->DistSqr(); // [0.25m^2] distance square
// uint32_t WarnTimeSqr = (uint32_t)WarnTime*WarnTime; // [s] warning time square
// printf("calcTarget(0x%08X) ...\n", Tgt->ID);
// printf("RelDistSqr = %3.1fm^ RelVelSqr=%3.1f(m/s)^2 Time=%ds\n", 0.25*RelDistSqr, 0.25*RelVelSqr, RelVelSqr ? IntSqrt(RelDistSqr/RelVelSqr):0xFFFF);
// if((RelVelSqr*WarnTimeSqr)<=RelDistSqr) return 0;
uint16_t RelVel = Acft_RelPos::FastDistance(Tgt->Vx, Tgt->Vy, Tgt->Vz); // [0.5m/s]
uint16_t MinMissDist = 4*RelVel+Pos.Error + Tgt->Pos.Error + 2*MinHorizSepar; // [0.5m]
#ifdef DEBUG_PRINT
printf("Target: [%+4.1f, %+4.1f, %+4.1f] = %3.1fm/s MinMissDist=%3.1fm\n",
0.5*Tgt->Vx, 0.5*Tgt->Vy, 0.5*Tgt->Vz, 0.5*RelVel, 0.5*MinMissDist);
#endif
PredMe=Pos; PredTgt=Tgt->Pos; // copy my position and the target to temporary variables
int16_t TimeMargin = PredMe.StepTillMinSepar(PredTgt, MinMissDist, 2*(WarnTime+2)); // predict when minimum separation is reached
#ifdef DEBUG_PRINT
printf("StepTillMinSepar(0x%08X, %3.1fm, %1ds) => %+4.1fs\n", Tgt->ID, 0.5*MinMissDist, WarnTime+2, 0.5*TimeMargin);
#endif
Tgt->TimeMargin=TimeMargin; // store the time margin till minimum separation
Tgt->MissTime=TimeMargin;
if(TimeMargin>(2*WarnTime)) return 0; // if time-to-margin longer than warning time then return no warning
Tgt->WarnLevel=1; // otherwise set already the first warning level
if(TimeMargin>WarnTime) return Tgt->WarnLevel; // is time-to-margin longer than half the warning time, then stop calculations here, return 1st warning level
#ifdef DEBUG_PRINT
printf("Me :"); PredMe.Print();
printf("Tgt:"); PredTgt.Print();
#endif
for(uint8_t Count=3; Count; Count--)
{ int16_t MissTime = PredMe.MissTime(PredTgt, WarnTime);
#ifdef DEBUG_PRINT
printf("%d: MissTime = %+4.1fs\n", Count, 0.5*MissTime);
#endif
if(abs(MissTime)<=1) break;
PredMe.StepFwd(MissTime);
PredTgt.StepFwd(PredMe.T-PredTgt.T); }
#ifdef DEBUG_PRINT
printf("Me :"); PredMe.Print();
printf("Tgt:"); PredTgt.Print();
#endif
Tgt->MissDist = PredMe.FastDistance(PredTgt);
Tgt->MissTime = PredMe.T-Pos.T;
#ifdef DEBUG_PRINT
printf("MissTime = %+4.1f, MissDist = %4.1f\n", 0.5*Tgt->MissTime, 0.5*Tgt->MissDist);
#endif
if( (Tgt->MissTime<0) || (Tgt->MissTime>(2*WarnTime)) || (Tgt->MissDist>MinMissDist) ) Tgt->WarnLevel=0;
else if(Tgt->MissDist<(2*MinHorizSepar)) { Tgt->WarnLevel=2; if(Tgt->MissTime<(2*WarnTime/3)) Tgt->WarnLevel=3; }
#ifdef DEBUG_PRINT
printf("calcTarget(%08X) V=[%+5.1f, %+5.1f, %+5.1f]m/s D=[%+7.1f, %+7.1f, %+7.1f]m MissTime=%5.1fsec MissDist=%6.1fm\n",
Tgt->ID, 0.5*Tgt->Vx, 0.5*Tgt->Vy, 0.5*Tgt->Vz, 0.5*Tgt->dX, 0.5*Tgt->dY, 0.5*Tgt->dZ, 0.5*Tgt->MissTime, 0.5*Tgt->MissDist);
#endif
return Tgt->WarnLevel; }
uint16_t calcVertMargin(LookOut_Target *Tgt) // calculate vertical savety margin
{ Tgt->dZ = Tgt->Pos.Z - Pos.Z; // [0.5m] relative vertical distance
Tgt->Vz = Tgt->Pos.Climb - Pos.Climb; // [0.5ms/s] relative vertical speed
int16_t VertError = Pos.Error+Tgt->Pos.Error; VertError+=VertError/2; // [0.5m] est. total vertical error
VertError += 2*MinVertSepar; // [0.5m]
if(abs(Tgt->dZ)<=VertError) return 0; // if vertical distance less than margin required: return zero margin
if(Tgt->dZ>0) { if(Tgt->Vz>=0) return Tgt->dZ-VertError; } // if target is higher and is climbing return relative vertical distance
else { if(Tgt->Vz<=0) return -Tgt->dZ-VertError; } // if target is lower and is falling, return like above
int16_t dT = Tgt->Pos.T - Pos.T; // [0.5sec] time diff. in measured positions
int32_t MaxAlt = ( (int32_t)Tgt->Vz * (2*(WarnTime+4)+abs(dT)) )>>1; // [0.5m] max. altitude change within the warning time
MaxAlt = abs(MaxAlt) + VertError; // [0.5m]
// printf("calcVertMargin() dAlt=%+4.1f dT=%+4.1f Climb=%+4.1f MaxAlt=%+4.1f\n", 0.5*Tgt->dZ, 0.5*dT, 0.5*Tgt->Vz, 0.5*MaxAlt);
if(Tgt->dZ>0) // if target is above
{ if(Tgt->dZ<MaxAlt) return 0;
else return ( 2*Tgt->dZ-MaxAlt); } // [0.5m]
else // if target is below
{ if((-Tgt->dZ)<MaxAlt) return 0;
else return (-2*Tgt->dZ-MaxAlt); } // [0.5m]
} // return the vertical margin: if positive: we are safe, if zero: we are too close
uint16_t calcHorizMargin(LookOut_Target *Tgt)
{ Tgt->dX = Tgt->Pos.X - Pos.X; // [0.5m] relative distance
Tgt->dY = Tgt->Pos.Y - Pos.Y; // [0.5m]
Tgt->HorDist = Acft_RelPos::FastDistance(Tgt->dX, Tgt->dY); // [0.5m] estimate horizontal distance
int16_t HorError = Pos.Error+Tgt->Pos.Error; // [0.5m] sum GPS error from me and the target
HorError += 2*MinHorizSepar; // [0.5m] add the min. separation required
int16_t dT = abs(Tgt->Pos.T - Pos.T); // [0.5m] time difference between my data and target data
int32_t MaxDistT = ((int32_t)Tgt->Pos.Speed * (2*(WarnTime+4)+dT))>>1; // [0.5m] possible distance covered by the target
int32_t MaxDistM = ((int32_t) Pos.Speed * (2*(WarnTime+4)+dT))>>1; // [0.5m] possible distance covered by me
int32_t MaxDist = MaxDistT + MaxDistM + HorError; // [0.5m] add horizontal separation
if(MaxDist<Tgt->HorDist) return Tgt->HorDist-MaxDist; // [0.5m] return the (positive) difference: we are safe
return 0; } // zero-margin => bad !
void AdjustRefTime(int16_t TimeDelta) // adjust the time reference point
{ if(TimeDelta<(2*12)) return; // in more than 10sec into the future from the current reference
TimeDelta/=2;
RefTime+=TimeDelta; if(RefTime>=60) RefTime-=60; // shift time reference
Pos.T-=2*TimeDelta; // shift the relative time on my own position
if(Pos.T<(-2*60)) hasPosition=0; // if older than 60sec declare "no position"
for(uint8_t Idx=0; Idx<MaxTargets; Idx++) // go over the targets
{ LookOut_Target &Tgt = Target[Idx]; if(!Tgt.Alloc) continue; // skip unallocated
Tgt.Pos.T-=2*TimeDelta; // shift the relative time
if((Tgt.Pos.T-Tgt.Pred)<(-2*30)) Tgt.Alloc=0; // if older than 30sec then drop the target
}
}
void AdjustRefAlt(void) // shift the vertical reference point when we get too far off
{ if(fabs(Pos.Z)<(2*200)) return; // don't shift if less than 200m from the reference point
int16_t AltDelta=Pos.Z/2;
RefAlt+=AltDelta;
Pos.Z-=2*AltDelta;
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ if(Target[Idx].Alloc) Target[Idx].Pos.Z-=2*AltDelta; }
}
template <class OGNx_Packet>
void AdjustRefLatLon(OGNx_Packet &Me) // shift the horizontal reference point when we get too far off
{ if( (fabs(Pos.X)<(2*1000)) && (fabs(Pos.Y)<(2*500)) ) return;
int32_t LatDist, LonDist;
if(Me.calcDistanceVector(LatDist, LonDist, RefLat, RefLon, LatCos, DistRange)<0) { return; }
RefLat = Me.DecodeLatitude();
RefLon = Me.DecodeLongitude();
LatDist*=2;
LonDist*=2;
Pos.X -= LatDist;
Pos.Y -= LonDist;
for(uint8_t Idx=0; Idx<MaxTargets; Idx++)
{ if(Target[Idx].Alloc) { Target[Idx].Pos.X-=LatDist; Target[Idx].Pos.Y-=LonDist; } }
LatCos = Icos(GPS_Position::calcLatAngle16(RefLat));
}
} ;
// =======================================================================================================
#endif // __LOKOUT_H__

Wyświetl plik

@ -11,6 +11,9 @@
#include "sens.h"
#include "ctrl.h" // Control task
#include "log.h" // Data logging task
#include "knob.h" // potentiometer as rotary encoder
#include "sound.h" // sounds, warnings, alarms
#include "disp.h"
#ifdef WITH_AERO
#include "aero.h"
@ -38,6 +41,14 @@ void app_main(void)
#endif
IO_Configuration(); // initialize the GPIO/UART/I2C/SPI for Radio, GPS, OLED, Baro
#ifdef WITH_SD
if(SD_isMounted())
{ Parameters.SaveToFlash=0;
if(Parameters.ReadFromFile("/sdcard/TRACKER.CFG")>0)
{ if(Parameters.SaveToFlash) Parameters.WriteToNVS(); }
}
#endif
#ifdef WITH_BT_SPP
{ int32_t Err=BT_SPP_Init(); // start BT SPP
// #ifdef DEBUG_PRINT
@ -54,16 +65,25 @@ void app_main(void)
#ifdef WITH_LOG
xTaskCreate(vTaskLOG , "LOG", 2560, 0, tskIDLE_PRIORITY+1, 0);
#endif
xTaskCreate(vTaskPROC, "PROC", 4096, 0, tskIDLE_PRIORITY+3, 0);
xTaskCreate(vTaskPROC, "PROC", 2048, 0, tskIDLE_PRIORITY+3, 0);
xTaskCreate(vTaskGPS, "GPS", 2048, 0, tskIDLE_PRIORITY+4, 0);
#if defined(WITH_BMP180) || defined(WITH_BMP280) || defined(WITH_BME280) || defined(WITH_MS5607)
xTaskCreate(vTaskSENS, "SENS", 2048, 0, tskIDLE_PRIORITY+4, 0);
#endif
#ifdef WITH_KNOB
xTaskCreate(vTaskKNOB, "KNOB", 2048, 0, tskIDLE_PRIORITY+3, 0);
#endif
#ifdef WITH_AERO
xTaskCreate(vTaskAERO, "AERO", 2048, 0, tskIDLE_PRIORITY+4, 0);
#endif
#ifdef WITH_WIFI
xTaskCreate(vTaskWIFI, "WIFI", 4096, 0, tskIDLE_PRIORITY+2, 0);
#endif
#if defined(WITH_OLED) || defined(WITH_U8G2_OLED) || defined(WITH_ST7789) || defined(WITH_ILI9341)
xTaskCreate(vTaskDISP, "DISP", 2048, 0, tskIDLE_PRIORITY+2, 0);
#endif
#ifdef WITH_SOUND
xTaskCreate(vTaskSOUND, "SOUND", 2048, 0, tskIDLE_PRIORITY+3, 0);
#endif
// xTaskCreate(vTaskCTRL, "CTRL", 1536, 0, tskIDLE_PRIORITY+2, 0);
vTaskCTRL(0); // run directly the CTRL task, instead of creating a separate one.

Wyświetl plik

@ -11,7 +11,7 @@ inline uint8_t NMEA_AppendCheckCRNL(char *NMEA, uint8_t Len) { return NMEA_Appen
class NMEA_RxMsg // receiver for the NMEA sentences
{ public:
static const uint8_t MaxLen=96; // maximum length
static const uint8_t MaxLen=104; // maximum length
static const uint8_t MaxParms=24; // maximum number of parameters (commas)
uint8_t Data[MaxLen]; // the message itself
uint8_t Len; // number of bytes
@ -141,7 +141,7 @@ inline uint8_t NMEA_AppendCheckCRNL(char *NMEA, uint8_t Len) { return NMEA_Appen
if(Data[4]!='G') return 0;
return Data[5]=='A'; }
uint8_t isGPGSA(void) const // GPS satellite data
uint8_t isGPGSA(void) const //
{ if(!isGP()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
@ -159,6 +159,24 @@ inline uint8_t NMEA_AppendCheckCRNL(char *NMEA, uint8_t Len) { return NMEA_Appen
if(Data[4]!='S') return 0;
return Data[5]=='A'; }
uint8_t isGxGSV(void) const // GPS satellite data
{ if(!isGx()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGPGSV(void) const // GPS satellite data
{ if(!isGP()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGNGSV(void) const // GPS satellite data
{ if(!isGN()) return 0;
if(Data[3]!='G') return 0;
if(Data[4]!='S') return 0;
return Data[5]=='V'; }
uint8_t isGPTXT(void) const // GPS satellite data
{ if(!isGP()) return 0;
if(Data[3]!='T') return 0;

Wyświetl plik

@ -22,9 +22,13 @@
#include "format.h"
#include "ognconv.h"
#include "ogn1.h" // OGN v1
#include "ogn2.h" // OGN v2
#include "atmosphere.h"
// ---------------------------------------------------------------------------------------------------------------------
template <class OGNx_Packet, class OGNy_Packet>
@ -607,8 +611,9 @@ template<class OGNx_Packet, uint8_t Size=8>
void cleanTime(uint8_t Time) // clean up slots of given Time
{ for(int Idx=0; Idx<Size; Idx++)
{ if( (Packet[Idx].Rank) && (Packet[Idx].Packet.Position.Time==Time) )
{ clean(Idx); }
{ if(Packet[Idx].Rank==0) continue;
uint8_t PktTime=Packet[Idx].Packet.Position.Time;
if( PktTime==Time || PktTime>=60) clean(Idx);
}
}
@ -624,15 +629,15 @@ template<class OGNx_Packet, uint8_t Size=8>
uint8_t Print(char *Out)
{ uint8_t Len=0;
for(uint8_t Idx=0; Idx<Size; Idx++)
for(uint8_t Idx=0; Idx<Size; Idx++) // loop through the slots
{ uint8_t Rank=Packet[Idx].Rank;
Out[Len++]=' '; Len+=Format_Hex(Out+Len, Rank);
if(Rank)
{ Out[Len++]='/'; Len+=Format_Hex(Out+Len, Packet[Idx].Packet.getAddressAndType() );
Out[Len++]=':'; Len+=Format_UnsDec(Out+Len, Packet[Idx].Packet.Position.Time, 2 ); }
Out[Len++]=' '; Len+=Format_Hex(Out+Len, Rank); // print the slot Rank
if(Rank) // if Rank is none-zero
{ Out[Len++]='/'; Len+=Format_Hex(Out+Len, Packet[Idx].Packet.getAddressAndType() ); // print address-type and address
Out[Len++]=':'; Len+=Format_UnsDec(Out+Len, Packet[Idx].Packet.Position.Time, 2 ); } // [sec] print time
}
Out[Len++]=' '; Len+=Format_Hex(Out+Len, Sum);
Out[Len++]='/'; Len+=Format_Hex(Out+Len, LowIdx);
Out[Len++]=' '; Len+=Format_Hex(Out+Len, Sum); // sum of all Ranks
Out[Len++]='/'; Len+=Format_Hex(Out+Len, LowIdx); // index of the lowest Rank or a free slot
Out[Len++]='\n'; Out[Len]=0; return Len; }
} ;
@ -641,20 +646,24 @@ class GPS_Position
{ public:
union
{ uint8_t Flags; // bit #0 = GGA and RMC had same Time
{ uint8_t Flags; // bit #0 = GGA and RMC had same Time
struct
{ bool hasGPS :1; // all required GPS information has been supplied (but this is not the GPS lock status)
bool hasBaro :1; // barometric information has beed supplied
// bool hasHum :1; //
bool isReady :1; // is ready for the following treaement
bool Sent :1; // has been transmitted
bool hasTime :1; // Time has been supplied
bool hasRMC :1; // GxRMC has been supplied
bool hasGGA :1; // GxGGA has been supplied
bool hasGSA :1; // GxGSA has been supplied
// bool hasHum :1; //
// bool hasGSV :1;
} ;
} ;
// uint16_t SatSNRsum; // sum of cSNR from GPGSV
// uint8_t SatSNRcount; // count of satellites from GPGSV
int8_t FixQuality; // 0 = none, 1 = GPS, 2 = Differential GPS (can be WAAS)
int8_t FixMode; // 0 = not set (from GSA) 1 = none, 2 = 2-D, 3 = 3-D
int8_t Satellites; // number of active satellites
@ -676,7 +685,7 @@ class GPS_Position
int16_t GeoidSeparation; // [0.1 meter] difference between Geoid and Ellipsoid
int32_t Altitude; // [0.1 meter] height above Geoid (sea level)
int32_t Latitude; // [0.0001/60 deg] about 0.018m accuracy (to convert to u-Blox GPS 1e-7deg units mult by 50/3)
int32_t Latitude; // [0.0001/60 deg] about 0.18m accuracy (to convert to u-Blox GPS 1e-7deg units mult by 50/3)
int32_t Longitude; // [0.0001/60 deg]
uint16_t LatitudeCosine; // [2^-12] Latitude cosine for distance calculation
@ -684,6 +693,7 @@ class GPS_Position
uint32_t Pressure; // [0.25 Pa] from pressure sensor
int32_t StdAltitude; // [0.1 meter] standard pressure altitude (from the pressure sensor and atmosphere calculator)
int16_t Humidity; // [0.1%] relative humidity
int16_t Accel; // [0.1m/s^2] acceleration along the track
public:
@ -692,11 +702,12 @@ class GPS_Position
void Clear(void)
{ Flags=0; FixQuality=0; FixMode=0;
PDOP=0; HDOP=0; VDOP=0;
// SatSNRsum=0; SatSNRcount=0;
setDefaultDate(); setDefaultTime();
Latitude=0; Longitude=0; LatitudeCosine=3000;
Altitude=0; GeoidSeparation=0;
Speed=0; Heading=0; ClimbRate=0; TurnRate=0;
Temperature=0; Pressure=0; StdAltitude=0; }
Temperature=0; Pressure=0; StdAltitude=0; Humidity=0; }
void setDefaultDate() { Year=00; Month=1; Day=1; } // default Date is 01-JAN-2000
void setDefaultTime() { Hour=0; Min=0; Sec=0; FracSec=0; } // default Time is 00:00:00.00
@ -779,7 +790,8 @@ class GPS_Position
printf("FixQuality/Mode=%d/%d: %d satellites DOP/H/V=%3.1f/%3.1f/%3.1f ", FixQuality, FixMode, Satellites, 0.1*PDOP, 0.1*HDOP, 0.1*VDOP);
printf("FixQuality=%d: %d satellites HDOP=%3.1f ", FixQuality, Satellites, 0.1*HDOP);
printf("Lat/Lon/Alt = [%+10.6f,%+10.6f]deg %+3.1f(%+3.1f)m LatCosine=%+6.3f ", 0.0001/60*Latitude, 0.0001/60*Longitude, 0.1*Altitude, 0.1*GeoidSeparation, 1.0/(1<<12)*LatitudeCosine);
printf("Speed/Heading = %3.1fm/s %05.1fdeg\n", 0.1*Speed, 0.1*Heading);
printf("Speed/Heading = %3.1fm/s %05.1fdeg ", 0.1*Speed, 0.1*Heading);
printf("Climb = %+5.1fm/s Turn = %+5.1fdeg/sec\n", 0.1*ClimbRate, 0.1*TurnRate);
}
int Print(char *Out) const
@ -820,8 +832,8 @@ class GPS_Position
Out[Len++]='/'; Len+=Format_UnsDec(Out+Len, HDOP, 2, 1);
Out[Len++]='/'; Len+=Format_UnsDec(Out+Len, VDOP, 2, 1);
Out[Len++]=' ';
Out[Len++]='['; Len+=Format_SignDec(Out+Len, Latitude/60, 6, 4);
Out[Len++]=','; Len+=Format_SignDec(Out+Len, Longitude/60, 7, 4);
Out[Len++]='['; Len+=Format_SignDec(Out+Len, Latitude/6, 7, 5);
Out[Len++]=','; Len+=Format_SignDec(Out+Len, Longitude/6, 8, 5);
Out[Len++]=']'; Out[Len++]='d'; Out[Len++]='e'; Out[Len++]='g';
Out[Len++]=' '; Len+=Format_SignDec(Out+Len, Altitude, 4, 1); Out[Len++]='m';
Out[Len++]='/'; Len+=Format_SignDec(Out+Len, GeoidSeparation, 4, 1); Out[Len++]='m';
@ -878,6 +890,7 @@ class GPS_Position
if(RxMsg.isGNRMC()) return ReadRMC(RxMsg);
if(RxMsg.isGPGSA()) return ReadGSA(RxMsg);
if(RxMsg.isGNGSA()) return ReadGSA(RxMsg);
// if(RxMsg.isGxGSV()) return ReadGSV(RxMsg);
return 0; }
int8_t ReadNMEA(const char *NMEA)
@ -885,6 +898,7 @@ class GPS_Position
Err=ReadGGA(NMEA); if(Err!=(-1)) return Err;
Err=ReadGSA(NMEA); if(Err!=(-1)) return Err;
Err=ReadRMC(NMEA); if(Err!=(-1)) return Err;
// Err=ReadGSV(NMEA); if(Err!=(-1)) return Err;
return 0; }
int8_t ReadGGA(NMEA_RxMsg &RxMsg)
@ -899,9 +913,43 @@ class GPS_Position
ReadLongitude(*RxMsg.ParmPtr(4), (const char *)RxMsg.ParmPtr(3)); // Longitude
ReadAltitude(*RxMsg.ParmPtr(9), (const char *)RxMsg.ParmPtr(8)); // Altitude
ReadGeoidSepar(*RxMsg.ParmPtr(11), (const char *)RxMsg.ParmPtr(10)); // Geoid separation
// calcLatitudeCosine();
calcLatitudeCosine();
return 1; }
uint8_t WriteGGA(char *GGA)
{ uint8_t Len=0;
Len+=Format_String(GGA+Len, "$GPGGA,");
Len+=Format_UnsDec(GGA+Len, (uint16_t)Hour, 2);
Len+=Format_UnsDec(GGA+Len, (uint16_t)Min, 2);
Len+=Format_UnsDec(GGA+Len, (uint16_t)Sec, 2);
GGA[Len++]='.';
Len+=Format_UnsDec(GGA+Len, (uint16_t)FracSec, 2);
GGA[Len++]=',';
Len+=Format_Latitude(GGA+Len, Latitude);
GGA[Len]=GGA[Len-1]; GGA[Len-1]=','; Len++;
GGA[Len++]=',';
Len+=Format_Longitude(GGA+Len, Longitude);
GGA[Len]=GGA[Len-1]; GGA[Len-1]=','; Len++;
GGA[Len++]=',';
GGA[Len++]='0'+FixQuality;
GGA[Len++]=',';
Len+=Format_UnsDec(GGA+Len, (uint16_t)Satellites);
GGA[Len++]=',';
Len+=Format_UnsDec(GGA+Len, (uint16_t)HDOP, 2, 1);
GGA[Len++]=',';
Len+=Format_SignDec(GGA+Len, Altitude, 3, 1);
GGA[Len++]=',';
GGA[Len++]='M';
GGA[Len++]=',';
Len+=Format_SignDec(GGA+Len, GeoidSeparation, 3, 1);
GGA[Len++]=',';
GGA[Len++]='M';
GGA[Len++]=',';
GGA[Len++]=',';
Len += NMEA_AppendCheckCRNL(GGA, Len);
GGA[Len]=0;
return Len; }
int8_t ReadGGA(const char *GGA)
{ if( (memcmp(GGA, "$GPGGA", 6)!=0) && (memcmp(GGA, "$GNGGA", 6)!=0) ) return -1; // check if the right sequence
uint8_t Index[20]; if(IndexNMEA(Index, GGA)<14) return -2; // index parameters and check the sum
@ -915,7 +963,7 @@ class GPS_Position
ReadLongitude(GGA[Index[4]], GGA+Index[3]); // Longitude
ReadAltitude(GGA[Index[9]], GGA+Index[8]); // Altitude
ReadGeoidSepar(GGA[Index[11]], GGA+Index[10]); // Geoid separation
// calcLatitudeCosine();
calcLatitudeCosine();
return 1; }
int8_t ReadGSA(NMEA_RxMsg &RxMsg)
@ -934,7 +982,17 @@ class GPS_Position
ReadHDOP(GSA+Index[15]);
ReadVDOP(GSA+Index[16]);
return 1; }
/*
int8_t ReadGSV(NMEA_RxMsg &RxMsg)
{ //
return 1; }
int8_t ReadGSV(const char *GSV)
{ if( (memcmp(GSV, "$GPGSV", 6)!=0) && (memcmp(GSV, "$GNGSV", 6)!=0) ) return -1; // check if the right sequence
uint8_t Index[24]; if(IndexNMEA(Index, GSV)<20) return -2; // index parameters and check the sum
//
return 1; }
*/
int ReadRMC(NMEA_RxMsg &RxMsg)
{ if(RxMsg.Parms<12) return -1; // no less than 12 parameters
hasGPS = ReadTime((const char *)RxMsg.ParmPtr(0))>0; // read time and check if same as the GGA says
@ -964,7 +1022,7 @@ class GPS_Position
else if(TimeDiff>=3000) TimeDiff-=6000;
return TimeDiff; } // [0.01s]
int16_t calcDifferences(GPS_Position &RefPos) // calculate climb rate and turn rate with an earlier reference position
int16_t calcDifferentials(GPS_Position &RefPos) // calculate climb rate and turn rate with an earlier reference position
{ ClimbRate=0; TurnRate=0;
if(RefPos.FixQuality==0) return 0;
int16_t TimeDiff = calcTimeDiff(RefPos);
@ -974,19 +1032,29 @@ class GPS_Position
ClimbRate = Altitude-RefPos.Altitude;
if(hasBaro && RefPos.hasBaro && (abs(Altitude-StdAltitude)<2500) )
{ ClimbRate = StdAltitude-RefPos.StdAltitude; }
if(TimeDiff==100)
Accel = Speed-RefPos.Speed;
if(TimeDiff==20)
{ ClimbRate*=5;
TurnRate *=5;
Accel *=5; }
else if(TimeDiff==50)
{ ClimbRate*=2;
TurnRate *=2;
Accel *=2; }
else if(TimeDiff==100)
{ }
else if(TimeDiff==200)
{ ClimbRate=(ClimbRate+1)>>1;
TurnRate=(TurnRate+1)>>1; }
else
TurnRate=( TurnRate+1)>>1;
Accel =( Accel +1)>>1; }
else if(TimeDiff!=0)
{ ClimbRate = ((int32_t)ClimbRate*100)/TimeDiff;
TurnRate = ((int32_t)TurnRate *100)/TimeDiff; }
TurnRate = ((int32_t) TurnRate*100)/TimeDiff;
Accel = ((int32_t) Accel *100)/TimeDiff; }
return TimeDiff; } // [0.01s]
void Write(MAV_GPS_RAW_INT *MAV) const
// { MAV->time_usec = (int64_t)1000000*getUnixTime()+10000*FracSec;
{ MAV->time_usec = getUnixTime_ms()*1000; // (int64_t)1000000*getUnixTime()+10000*FracSec;
{ MAV->time_usec = (int64_t)1000000*getUnixTime()+10000*FracSec;
MAV->lat = ((int64_t)50*Latitude+1)/3;
MAV->lon = ((int64_t)50*Longitude+1)/3;
MAV->alt = 100*Altitude;
@ -1080,12 +1148,10 @@ class GPS_Position
if(hasBaro)
{ Packet.EncodeTemperature(Temperature);
Packet.Status.Pressure = (Pressure+16)>>5;
Packet.EncodeHumidity(Humidity);
}
Packet.EncodeHumidity(Humidity); }
else
{ Packet.Status.Pressure = 0;
Packet.clrHumidity();
}
Packet.clrHumidity(); }
}
// uint8_t getFreqPlan(void) const // get the frequency plan from Lat/Lon: 1 = Europe + Africa, 2 = USA/CAnada, 3 = Australia + South America, 4 = New Zeeland
@ -1120,12 +1186,37 @@ class GPS_Position
else Packet.clrBaro(); //or no-baro if pressure sensor data not there
}
void calcExtrapolation(int32_t &Lat, int32_t &Lon, int32_t &Alt, int16_t &Head, int32_t dTime) const // extrapolate GPS position by a fraction of a second
void Extrapolate(int32_t dTime) // [0.01sec] extrapolate the position by dTime
{ int16_t dSpeed = ((int32_t)Accel*dTime)/100;
Speed += dSpeed/2;
int16_t HeadAngle = ((int32_t)Heading<<12)/225; // [cordic] heading angle
int16_t TurnAngle = (((dTime*TurnRate)/25)<<9)/225; // [cordic]
HeadAngle += TurnAngle/2;
int32_t LatSpeed = ((int32_t)Speed*Icos(HeadAngle)+0x800)>>12; // [0.1m/s]
int32_t LonSpeed = ((int32_t)Speed*Isin(HeadAngle)+0x800)>>12; // [0.1m/s]
HeadAngle += TurnAngle-TurnAngle/2;
Speed += dSpeed-dSpeed/2; if(Speed<0) Speed=0;
Latitude += calcLatitudeExtrapolation (dTime, LatSpeed);
Longitude += calcLongitudeExtrapolation(dTime, LonSpeed);
int32_t dAlt = calcAltitudeExtrapolation(dTime); // [0.1m]
Altitude += dAlt; // [0.1m]
if(hasBaro)
{ StdAltitude += dAlt; // [0.1m]
Pressure += 4000*dAlt/Atmosphere::PressureLapseRate(Pressure/4, Temperature); } // [0.25Pa] ([Pa], [0.1degC])
Heading += (dTime*TurnRate)/100; // [0.1deg]
if(Heading<0) Heading+=3600; else if(Heading>=3600) Heading-=3600; // [0.1deg]
int16_t fTime = FracSec+dTime; // [0.01sec]
while(fTime>=100) { incrTimeDate(); fTime-=100; }
while(fTime< 0) { decrTimeDate(); fTime+=100; }
FracSec=fTime; }
// extrapolate GPS position by a fraction of a second
void calcExtrapolation(int32_t &Lat, int32_t &Lon, int32_t &Alt, int16_t &Head, int32_t dTime) const // [0.01sec]
{ int16_t HeadAngle = ((int32_t)Heading<<12)/225; // []
int16_t TurnAngle = (((dTime*TurnRate)/25)<<9)/225; // []
HeadAngle += TurnAngle;
int32_t LatSpeed = ((int32_t)Speed*Icos(HeadAngle))>>12; // [0.1m/s]
int32_t LonSpeed = ((int32_t)Speed*Isin(HeadAngle))>>12; // [0.1m/s]
int32_t LatSpeed = ((int32_t)Speed*Icos(HeadAngle)+0x800)>>12; // [0.1m/s]
int32_t LonSpeed = ((int32_t)Speed*Isin(HeadAngle)+0x800)>>12; // [0.1m/s]
Lat = Latitude + calcLatitudeExtrapolation (dTime, LatSpeed);
Lon = Longitude + calcLongitudeExtrapolation(dTime, LonSpeed);
Alt = Altitude + calcAltitudeExtrapolation(dTime);
@ -1135,15 +1226,15 @@ class GPS_Position
int32_t calcAltitudeExtrapolation(int32_t Time) const // [0.01s]
{ return Time*ClimbRate/100; } // [0.1m]
int32_t calcLatitudeExtrapolation(int32_t Time, int32_t LatSpeed) const // [0.01s]
{ return (Time*LatSpeed*177)>>15; } // [0.1m]
int32_t calcLatitudeExtrapolation(int32_t Time, int32_t LatSpeed) const // [0.01s] [0.1m/s]
{ return (Time*LatSpeed*177+0x4000)>>15; } // [0.1m]
int32_t calcLongitudeExtrapolation(int32_t Time, int32_t LonSpeed) const // [0.01s]
{ int16_t LatCosine = calcLatCosine(calcLatAngle16(Latitude));
return calcLongitudeExtrapolation(Time, LonSpeed, LatCosine); }
int32_t calcLongitudeExtrapolation(int32_t Time, int32_t LonSpeed, int16_t LatCosine) const // [0.01s]
{ return (((int32_t)Time*LonSpeed*177)>>3)/LatCosine; }
{ return ((((int32_t)Time*LonSpeed*177+4)>>3))/LatCosine; }
// static int32_t calcLatDistance(int32_t Lat1, int32_t Lat2) // [m] distance along latitude
// { return ((int64_t)(Lat2-Lat1)*0x2f684bda+0x80000000)>>32; }
@ -1173,7 +1264,7 @@ class GPS_Position
// // printf("Latitude=%+d, LatAngle=%04X LatCos=%08X\n", Latitude, (uint16_t)LatAngle, LatCos);
// return ((int64_t)Dist*LatCos+0x40000000)>>31; } // distance corrected by the latitude cosine
void calcLatitudeCosine(void)
void calcLatitudeCosine(void)
{ int16_t LatAngle = calcLatAngle16(Latitude);
LatitudeCosine = calcLatCosine(LatAngle); }
@ -1288,10 +1379,13 @@ class GPS_Position
// printf("%s => [%d]\n", Seq, Params);
return Params; }
uint32_t getDayTime(void) const
{ return Times60((uint32_t)(Times60((uint16_t)Hour) + Min)) + Sec; } // this appears to save about 100 bytes of code
// return (uint32_t)Hour*SecsPerHour + (uint16_t)Min*SecsPerMin + Sec; } // compared to this line
uint32_t getUnixTime(void) const // return the Unix timestamp (tested 2000-2037)
{ uint16_t Days = DaysSinceYear2000() + DaysSimce1jan();
return Times60(Times60(Times24((uint32_t)(Days+10957)))) + Times60((uint32_t)(Times60((uint16_t)Hour) + Min)) + Sec; } // this appears to save about 100 bytes of code
// return (uint32_t)(Days+10957)*SecsPerDay + (uint32_t)Hour*SecsPerHour + (uint16_t)Min*SecsPerMin + Sec; } // compared to this line
return Times60(Times60(Times24((uint32_t)(Days+10957)))) + getDayTime(); }
uint32_t getFatTime(void) const // return timestamp in FAT format
{ uint16_t Date = ((uint16_t)(Year+20)<<9) | ((uint16_t)Month<<5) | Day;
@ -1319,10 +1413,6 @@ class GPS_Position
setUnixTime(Time);
FracSec = (Time_ms-(uint64_t)Time*1000)/10; }
uint64_t getUnixTime_ms(void) const
{ return (uint64_t)getUnixTime()*1000 + (uint32_t)FracSec*10; }
private:
static const uint32_t SecsPerMin = 60;

Wyświetl plik

@ -85,7 +85,8 @@ class OGN1_Packet // Packet structure for the OGN tracker
struct
{ unsigned int Pulse : 8; // [bpm] // pilot: heart pulse rate
unsigned int Oxygen : 7; // [%] // pilot: oxygen level in the blood
unsigned int FEScurr : 5; // [A] // FES current
unsigned int SatSNR : 5; // [dB] // average SNR of GPS signals
// unsigned int FEScurr : 5; // [A] // FES current
unsigned int RxRate : 4; // [/min] // log2 of received packet rate
unsigned int Time : 6; // [sec] // same as in the position packet
unsigned int FixQuality: 2;
@ -98,7 +99,7 @@ class OGN1_Packet // Packet structure for the OGN tracker
unsigned int Satellites: 4; // [ ]
unsigned int Firmware : 8; // [ ] // firmware version
unsigned int Hardware : 8; // [ ] // hardware version
unsigned int TxPower : 4; // [dBm] // RF trancmitter power
unsigned int TxPower : 4; // [dBm] // RF trancmitter power (offset = 4)
unsigned int ReportType: 4; // [0] // 0 for the status report
unsigned int Voltage : 8; // [1/64V] VR // supply/battery voltage
} Status;
@ -138,10 +139,10 @@ class OGN1_Packet // Packet structure for the OGN tracker
// void recvBytes(const uint8_t *SrcPacket) { memcpy(Byte(), SrcPacket, Bytes); } // load data bytes e.g. from a demodulator
static const uint8_t InfoParmNum = 12; // [int] number of info-parameters and their names
static const uint8_t InfoParmNum = 14; // [int] number of info-parameters and their names
static const char *InfoParmName(uint8_t Idx) { static const char *Name[InfoParmNum] =
{ "Pilot", "Manuf", "Model", "Type", "SN", "Reg", "ID", "Class",
"Task" , "Base" , "ICE" , "PilotID" } ;
"Task" , "Base" , "ICE" , "PilotID", "Hard", "Soft" } ;
return Idx<InfoParmNum ? Name[Idx]:0; }
#ifndef __AVR__
@ -226,8 +227,8 @@ class OGN1_Packet // Packet structure for the OGN tracker
0.1*DecodeSpeed(), 0.1*DecodeHeading(), 0.1*DecodeClimbRate(), 0.1*DecodeTurnRate() );
printf("\n");
}
/* void Encode(MAV_ADSB_VEHICLE *MAV)
/*
void Encode(MAV_ADSB_VEHICLE *MAV)
{ MAV->ICAO_address = HeaderWord&0x03FFFFFF;
MAV->lat = ((int64_t)50*DecodeLatitude()+1)/3;
MAV->lon = ((int64_t)50*DecodeLongitude()+1)/3;
@ -241,7 +242,6 @@ class OGN1_Packet // Packet structure for the OGN tracker
MAV->tslc = 0;
MAV->emiter_type = 0; }
*/
void Encode(MAV_ADSB_VEHICLE *MAV)
{ MAV->ICAO_address = Header.Address;
MAV->lat = ((int64_t)50*DecodeLatitude()+1)/3; // convert coordinates to [1e-7deg]
@ -465,8 +465,8 @@ class OGN1_Packet // Packet structure for the OGN tracker
Msg[Len++] = ' ';
Msg[Len++] = '!';
Msg[Len++] = 'W';
Msg[Len++] = '0'+(Lat+5)/10;
Msg[Len++] = '0'+(Lon+5)/10;
Msg[Len++] = '0'+Lat/10;
Msg[Len++] = '0'+Lon/10;
Msg[Len++] = '!';
Msg[Len++] = ' '; Msg[Len++] = 'i'; Msg[Len++] = 'd'; Len+=Format_Hex(Msg+Len, ((uint32_t)Position.AcftType<<26) | ((uint32_t)Header.AddrType<<24) | Header.Address);
@ -727,6 +727,9 @@ class OGN1_Packet // Packet structure for the OGN tracker
// --------------------------------------------------------------------------------------------------------------
void Encrypt (const uint32_t Key[4]) { XXTEA_Encrypt(Data, 4, Key, 8); } // encrypt with given Key
void Decrypt (const uint32_t Key[4]) { XXTEA_Decrypt(Data, 4, Key, 8); } // decrypt with given Key
void Whiten (void) { TEA_Encrypt_Key0(Data, 8); TEA_Encrypt_Key0(Data+2, 8); } // whiten the position
void Dewhiten(void) { TEA_Decrypt_Key0(Data, 8); TEA_Decrypt_Key0(Data+2, 8); } // de-whiten the position

Wyświetl plik

@ -40,13 +40,13 @@ class OGN2_Packet // Packet structure for the OGN tracker
unsigned int Relay : 1; // 0 = direct packet, 1 = relayed packet
unsigned int Parity : 1; // parity takes into account bits 0..27 thus only the 28 lowest bits
unsigned int NonPos : 1; // 0 = position packet, 1 = other information like status
unsigned int Auth : 2; // Authentication: 00 = no auth. 01 = auth. this packet, 10/11 = auth response
// unsigned int Auth : 2; // Authentication: 00 = no auth. 01 = auth. this packet, 10/11 = auth response
// Auth:NonPos: 000 = position, 010 = position, to be followed by crypto-response,
// 101 = response #0, 111 = response #1
// 001 = non-position: status, info, etc.
// 100 = ???, 110 = ???, 011 = ???
// unsigned int NonOGN : 1; // 0 = OGN packet, 1 = other systems, like MAVlink
// unsigned int Encrypted : 1; // packet is encrypted
unsigned int NonOGN : 1; // 0 = OGN packet, 1 = other systems, like MAVlink
unsigned int Encrypted : 1; // packet is encrypted
unsigned int Emergency : 1; // aircraft in emergency (not used for now)
} Header ;
@ -89,7 +89,8 @@ class OGN2_Packet // Packet structure for the OGN tracker
unsigned int Pulse : 8; // [bpm] // pilot: heart pulse rate
unsigned int Oxygen : 7; // [%] // pilot: oxygen level in the blood
unsigned int FEScurr : 5; // [A] //
// unsigned int FEScurr : 5; // [A] //
unsigned int SatSNR : 5; // [dB] // average SNR of GPS signals
unsigned int RxRate : 4; // [/min] // log2 of received packet rate
unsigned int Time : 6; // [sec] // same as in the position packet
unsigned int FixQuality: 2;
@ -115,10 +116,10 @@ class OGN2_Packet // Packet structure for the OGN tracker
uint8_t *Byte(void) const { return (uint8_t *)&HeaderWord; } // packet as bytes
uint32_t *Word(void) const { return (uint32_t *)&HeaderWord; } // packet as words
static const uint8_t InfoParmNum = 12; // [int] number of info-parameters and their names
static const uint8_t InfoParmNum = 14; // [int] number of info-parameters and their names
static const char *InfoParmName(uint8_t Idx) { static const char *Name[InfoParmNum] =
{ "Pilot", "Manuf", "Model", "Type", "SN", "Reg", "ID", "Class",
"Task" , "Base" , "ICE" , "PilotID" } ;
"Task" , "Base" , "ICE" , "PilotID", "Hard", "Soft" } ;
return Idx<InfoParmNum ? Name[Idx]:0; }
void Dump(void) const

Wyświetl plik

@ -145,6 +145,9 @@ uint32_t DecodeGray(uint32_t Gray)
return Gray; }
// ==============================================================================================
// TEA encryption/decryption
// Data is 2 x 32-bit word
// Key is 4 x 32-bit word
void TEA_Encrypt (uint32_t* Data, const uint32_t *Key, int Loops)
{ uint32_t v0=Data[0], v1=Data[1]; // set up
@ -188,6 +191,42 @@ void TEA_Decrypt_Key0 (uint32_t* Data, int Loops)
Data[0]=v0; Data[1]=v1;
}
// ==============================================================================================
// XXTEA encryption/decryption
static uint32_t XXTEA_MX(uint8_t E, uint32_t Y, uint32_t Z, uint8_t P, uint32_t Sum, const uint32_t Key[4])
{ return ((((Z>>5) ^ (Y<<2)) + ((Y>>3) ^ (Z<<4))) ^ ((Sum^Y) + (Key[(P&3)^E] ^ Z))); }
void XXTEA_Encrypt(uint32_t *Data, uint8_t Words, const uint32_t Key[4], uint8_t Loops)
{ const uint32_t Delta = 0x9e3779b9;
uint32_t Sum = 0;
uint32_t Z = Data[Words-1]; uint32_t Y;
for( ; Loops; Loops--)
{ Sum += Delta;
uint8_t E = (Sum>>2)&3;
for (uint8_t P=0; P<(Words-1); P++)
{ Y = Data[P+1];
Z = Data[P] += XXTEA_MX(E, Y, Z, P, Sum, Key); }
Y = Data[0];
Z = Data[Words-1] += XXTEA_MX(E, Y, Z, Words-1, Sum, Key);
}
}
void XXTEA_Decrypt(uint32_t *Data, uint8_t Words, const uint32_t Key[4], uint8_t Loops)
{ const uint32_t Delta = 0x9e3779b9;
uint32_t Sum = Loops*Delta;
uint32_t Y = Data[0]; uint32_t Z;
for( ; Loops; Loops--)
{ uint8_t E = (Sum>>2)&3;
for (uint8_t P=Words-1; P; P--)
{ Z = Data[P-1];
Y = Data[P] -= XXTEA_MX(E, Y, Z, P, Sum, Key); }
Z = Data[Words-1];
Y = Data[0] -= XXTEA_MX(E, Y, Z, 0, Sum, Key);
Sum -= Delta;
}
}
// ==============================================================================================
void XorShift32(uint32_t &Seed) // simple random number generator
@ -202,5 +241,31 @@ void xorshift64(uint64_t &Seed)
// ==============================================================================================
const static unsigned char MapAscii85[86] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
const static uint8_t UnmapAscii85[128] =
{ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
85, 62, 85, 63, 64, 65, 66, 85, 67, 68, 69, 70, 85, 71, 85, 85, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 85, 72, 73, 74, 75, 76,
77, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 85, 85, 85, 78, 79,
80, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 81, 82, 83, 84, 85 };
uint8_t EncodeAscii85(char *Ascii, uint32_t Word)
{ for( uint8_t Idx=5; Idx; )
{ uint32_t Div = Word/85;
Idx--;
Ascii[Idx]=MapAscii85[Word-Div*85];
Word=Div; }
Ascii[5]=0;
return 5; }
uint8_t DecodeAscii85(uint32_t &Word, const char *Ascii)
{ Word=0;
for( uint8_t Idx=0; Idx<5; Idx++)
{ char Char = Ascii[Idx]; if(Char<=0) return 0;
uint8_t Dig = UnmapAscii85[(uint8_t)Char];
if(Dig>=85) return 0;
Word = Word*85+Dig; }
return 5; }
// ==============================================================================================

Wyświetl plik

@ -73,7 +73,13 @@ void TEA_Decrypt (uint32_t* Data, const uint32_t *Key, int Loops);
void TEA_Encrypt_Key0 (uint32_t* Data, int Loops);
void TEA_Decrypt_Key0 (uint32_t* Data, int Loops);
void XXTEA_Encrypt(uint32_t *Data, uint8_t Words, const uint32_t Key[4], uint8_t Loops);
void XXTEA_Decrypt(uint32_t *Data, uint8_t Words, const uint32_t Key[4], uint8_t Loops);
void XorShift32(uint32_t &Seed); // simple random number generator
void xorshift64(uint64_t &Seed);
uint8_t EncodeAscii85( char *Ascii, uint32_t Word ); // Encode 32-bit Word into 5-char Ascii-85 string
uint8_t DecodeAscii85(uint32_t &Word, const char *Ascii); // Decode 5-char Ascii-85 to 32-bit Word
#endif // __OGNCONV_H__

Wyświetl plik

@ -14,6 +14,10 @@
#include "nvs.h"
#endif
#ifdef WITH_SAMD21
#include "flashsize.h"
#endif
#ifdef WITH_STM32
#include "stm32f10x_flash.h"
#include "flashsize.h"
@ -29,8 +33,8 @@ class FlashParameters
{ uint32_t AcftID; // identification: Private:AcftType:AddrType:Address - must be different for every tracker
struct
{ uint32_t Address:24; // address (ID)
uint8_t AddrType:2;
uint8_t AcftType:4;
uint8_t AddrType:2; // 0=RND, 1=ICAO, 2=FLR, 3=OGN
uint8_t AcftType:4; // 1=glider, 2=towplane, 3=helicopter, etc.
bool NoTrack:1; // unused
bool Stealth:1; // unused
} ;
@ -44,15 +48,19 @@ class FlashParameters
int16_t PressCorr; // [0.25Pa] pressure correction for the baro
union
{ uint8_t Flags;
{ uint16_t Flags;
struct
{ bool SaveToFlash:1; // Save parameters from the config file to Flash
bool hasBT:1; // has BT interface on the console
bool BT_ON:1; // BT on after power up
bool manGeoidSepar:1; // GeoidSepar is manually configured as the GPS or MAVlink are not able to deliver it
bool Encrypt:1; // encrypt the position
uint8_t NavMode:3; // GPS navigation mode/model
uint8_t NavRate:2; // [Hz]
uint8_t Verbose:2; //
int8_t TimeCorr:4; // [sec] it appears for ArduPilot you need to correct time by 3 seconds
} ;
} ; //
int8_t TimeCorr; // [sec] it appears for ArduPilot you need to correct time by 3 seconds
} ; //
int16_t GeoidSepar; // [0.1m] Geoid-Separation, apparently ArduPilot MAVlink does not give this value (although present in the format)
// or it could be a problem of some GPSes
@ -60,7 +68,7 @@ class FlashParameters
uint8_t FreqPlan; // force given frequency hopping plan
static const uint8_t InfoParmLen = 16; // [char] max. size of an infp-parameter
static const uint8_t InfoParmNum = 12; // [int] number of info-parameters
static const uint8_t InfoParmNum = 14; // [int] number of info-parameters
char *InfoParmValue(uint8_t Idx) { return Idx<InfoParmNum ? Pilot + Idx*InfoParmLen:0; }
uint8_t InfoParmValueLen(uint8_t Idx) { return strlen(InfoParmValue(Idx)); }
// const char *InfoParmName(uint8_t Idx) const { static const char *Name[InfoParmNum] =
@ -79,6 +87,8 @@ class FlashParameters
char Base[InfoParmLen]; // Base airfield
char ICE[InfoParmLen]; // In Case of Emergency
char PilotID[InfoParmLen]; // Pilot ID based on his BT or WiFi MAC
char Hard[InfoParmLen]; // Hardware
char Soft[InfoParmLen]; // Software
// char Copilot[16]
// char Category[16]
@ -97,7 +107,15 @@ class FlashParameters
char WIFIname[WIFIsets][32];
char WIFIpass[WIFIsets][64];
#endif
#ifdef WITH_ENCRYPT
uint32_t EncryptKey[4]; // encryption key
#endif
uint32_t CheckSum;
#ifdef WITH_WIFI
const char *getWIFIpass(const char *NetName) const
{ for(uint8_t Idx=0; Idx<WIFIsets; Idx++)
{ if(strcmp(NetName, WIFIname[Idx])==0) return WIFIpass[Idx]; }
@ -116,6 +134,18 @@ class FlashParameters
static const uint32_t CheckInit = 0x89ABCDEF;
uint32_t static calcCheckSum(volatile uint32_t *Word, uint32_t Words) // calculate check-sum of pointed data
{ uint32_t Check=CheckInit;
for(uint32_t Idx=0; Idx<Words; Idx++)
{ Check += Word[Idx]; }
return Check; }
uint32_t calcCheckSum(void) const // calc. check-sum of this class data
{ return calcCheckSum((volatile uint32_t *)this, sizeof(FlashParameters)/sizeof(uint32_t) ); }
void setCheckSum(void) { CheckSum -= calcCheckSum(); }
bool goodCheckSum(void) const { return calcCheckSum()==0; }
uint8_t getAprsCall(char *Call)
{ const char *AddrTypeName[4] = { "RND", "ICA", "FLR", "OGN" };
memcpy(Call, AddrTypeName[AddrType], 3);
@ -136,6 +166,17 @@ class FlashParameters
#else
RFchipTxPower = 0x80 | 14; // [dBm] for RFM69HW
#endif
Flags = 0;
#ifdef WITH_GPS_UBX
NavMode = 6; // Avionic mode 1g for UBX
#endif
#ifdef WITH_GPS_MTK
NavMode = 2; // Avionic mode for MTK
#endif
NavRate = 1; // [Hz]
Verbose = 1;
RFchipTempCorr = 0; // [degC]
CONbaud = DEFAULT_CONbaud; // [bps]
PressCorr = 0; // [0.25Pa]
@ -144,7 +185,9 @@ class FlashParameters
FreqPlan = DEFAULT_FreqPlan; // [0..5]
PPSdelay = DEFAULT_PPSdelay; // [ms]
#ifdef WITH_ENCRYPT
for(uint8_t Idx=0; Idx<4; Idx++) EncryptKey[Idx]=0;
#endif
for(uint8_t Idx=0; Idx<InfoParmNum; Idx++)
InfoParmValue(Idx)[0] = 0;
#ifdef WITH_BT_SPP
@ -192,7 +235,71 @@ class FlashParameters
return Err; }
#endif // WITH_ESP32
#ifdef WITH_SAMD21
static uint32_t *DefaultFlashAddr(void) { return FlashStart+((uint32_t)(getFlashSizeKB()-1)<<8); } // the last KB
int8_t ReadFromFlash(volatile uint32_t *Addr=0) // read parameters from Flash
{ if(Addr==0) Addr = DefaultFlashAddr(); // default address: the last KB
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
if(calcCheckSum(Addr, Words)!=0) return -1; // agree with the check-sum in Flash ?
uint32_t *Dst = (uint32_t *)this;
for(uint32_t Idx=0; Idx<Words; Idx++) // read data from Flash
{ Dst[Idx] = Addr[Idx]; }
return 1; } // return: correct
bool CompareToFlash(volatile uint32_t *Addr=0) // are the parameters identical to those in the flash ?
{ if(Addr==0) Addr = DefaultFlashAddr(); // address in the Flash
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
if(calcCheckSum(Addr, Words)!=0) return 0; // agree with the check-sum in Flash ?
uint32_t *Dst = (uint32_t *)this;
for(uint32_t Idx=0; Idx<Words; Idx++) // read data from Flash
{ if(Dst[Idx] != Addr[Idx]) return 0; }
return 1; } // return: correct
void ErasePage4x64(volatile uint32_t *Addr) const // erase a 4x64 = 256-byte page
{ NVMCTRL->ADDR.reg = ((uint32_t)Addr)>>1;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
while (!NVMCTRL->INTFLAG.bit.READY) { }
}
void EraseSize(volatile uint32_t *Addr, uint32_t Size=1024) const // erase multiple pages for given size
{ for( ; ; )
{ if(Size==0) break;
ErasePage4x64(Addr); //
if(Size<256) break;
Addr+=64; Size-=256; }
}
int WritePage64(volatile uint32_t *Addr, const uint32_t *Data, uint32_t Words) const
{ // NVMCTRL->ADDR.reg = ((uint32_t)Addr)>>1;
NVMCTRL->CTRLB.bit.MANW = 1; // disable Automatic Page Write
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; // execute Page Buffer Clear
while (NVMCTRL->INTFLAG.bit.READY == 0) { }
uint32_t Idx=0;
for(Idx=0; (Idx<16) && (Idx<Words); Idx++) // copy Data
{ Addr[Idx] = Data[Idx]; }
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; // execute Write Page
while (NVMCTRL->INTFLAG.bit.READY == 0) { }
return Idx; }
int WriteSize(volatile uint32_t *Addr, const uint32_t *Data, uint32_t Words)
{ for( ; ; )
{ int Len = WritePage64(Addr, Data, Words);
Addr+=Len; Data+=Len; Words-=Len; if(Words==0) break; }
return 0; }
int8_t WriteToFlash(volatile uint32_t *Addr=0) // write parameters to Flash
{ if(Addr==0) Addr = DefaultFlashAddr();
setCheckSum();
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
EraseSize(Addr, Words);
WriteSize(Addr, (const uint32_t *)this, Words);
if(calcCheckSum(Addr, Words)!=0) return -1; // verify check-sum in Flash
return 0; }
#endif // WITH_SAMD21
#ifdef WITH_STM32
/*
uint32_t static CheckSum(const uint32_t *Word, uint32_t Words) // calculate check-sum of pointed data
{ uint32_t Check=CheckInit;
for(uint32_t Idx=0; Idx<Words; Idx++)
@ -201,9 +308,27 @@ class FlashParameters
uint32_t CheckSum(void) const // calc. check-sum of this class data
{ return CheckSum((uint32_t *)this, sizeof(FlashParameters)/sizeof(uint32_t) ); }
*/
static uint32_t *DefaultFlashAddr(void) { return FlashStart+((uint32_t)(getFlashSizeKB()-1)<<8); }
int8_t ReadFromFlash(volatile uint32_t *Addr=0) // read parameters from Flash
{ if(Addr==0) Addr = DefaultFlashAddr(); // default address: the last KB
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
if(calcCheckSum(Addr, Words)!=0) return -1; // agree with the check-sum in Flash ?
uint32_t *Dst = (uint32_t *)this;
for(uint32_t Idx=0; Idx<Words; Idx++) // read data from Flash
{ Dst[Idx] = Addr[Idx]; }
return 1; } // return: correct
bool CompareToFlash(volatile uint32_t *Addr=0) // are the parameters identical to those in the flash ?
{ if(Addr==0) Addr = DefaultFlashAddr(); // address in the Flash
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
if(calcCheckSum(Addr, Words)!=0) return 0; // agree with the check-sum in Flash ?
uint32_t *Dst = (uint32_t *)this;
for(uint32_t Idx=0; Idx<Words; Idx++) // read data from Flash
{ if(Dst[Idx] != Addr[Idx]) return 0; }
return 1; } // return: correct
/*
int8_t ReadFromFlash(uint32_t *Addr=0) // read parameters from Flash
{ if(Addr==0) Addr = DefaultFlashAddr();
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
@ -214,7 +339,7 @@ class FlashParameters
{ Dst[Idx] = Addr[Idx]; }
return 1; } // return: correct
int8_t CompareToFlash(uint32_t *Addr=0)
bool CompareToFlash(uint32_t *Addr=0)
{ if(Addr==0) Addr = DefaultFlashAddr(); // address in the Flash
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
uint32_t Check=CheckSum(Addr, Words); // check-sum of Flash data
@ -223,9 +348,11 @@ class FlashParameters
for(uint32_t Idx=0; Idx<Words; Idx++) // read data from Flash
{ if(Dst[Idx]!=Addr[Idx]) return 0; }
return 1; } // return: correct
*/
int8_t WriteToFlash(uint32_t *Addr=0) const // write parameters to Flash
int8_t WriteToFlash(volatile uint32_t *Addr=0) // write parameters to Flash
{ if(Addr==0) Addr = DefaultFlashAddr();
setCheckSum();
const uint32_t Words=sizeof(FlashParameters)/sizeof(uint32_t);
FLASH_Unlock(); // unlock Flash
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
@ -233,9 +360,9 @@ class FlashParameters
uint32_t *Data=(uint32_t *)this; // take data of this object
for(uint32_t Idx=0; Idx<Words; Idx++) // word by word
{ FLASH_ProgramWord((uint32_t)Addr, Data[Idx]); Addr++; } // !=FLASH_COMPLETE ? // write to Flash
FLASH_ProgramWord((uint32_t)Addr, CheckSum(Data, Words) ); // write the check-sum
// FLASH_ProgramWord((uint32_t)Addr, CheckSum(Data, Words) ); // write the check-sum
FLASH_Lock(); // re-lock Flash
if(CheckSum(Addr, Words)!=Addr[Words]) return -1; // verify check-sum in Flash
if(calcCheckSum(Addr, Words)!=0) return -1; // verify check-sum in Flash
return 0; }
#endif // WITH_STM32
@ -261,8 +388,7 @@ class FlashParameters
Line[Len++]='/';
Len+=Format_SignDec(Line+Len, (int16_t)getTxPower());
Len+=Format_String(Line+Len, "dBm");
Line[Len++]=' '; Len+=Format_SignDec(Line+Len, (int32_t)RFchipFreqCorr, 2, 1);
Len+=Format_String(Line+Len, "ppm");
Line[Len++]=' '; Len+=Format_SignDec(Line+Len, (int32_t)RFchipFreqCorr, 2, 1); Len+=Format_String(Line+Len, "ppm");
Len+=Format_String(Line+Len, " CON:");
Len+=Format_UnsDec(Line+Len, CONbaud);
Len+=Format_String(Line+Len, "bps\n");
@ -339,8 +465,32 @@ class FlashParameters
if(strcmp(Name, "GeoidSepar")==0)
{ return Read_Float1(GeoidSepar, Value)<=0; }
if(strcmp(Name, "manGeoidSepar")==0)
{ int32_t Man=0; if(Read_Int(Man, Value)<=0) return 0;
manGeoidSepar=Man; }
{ int32_t Man=0; if(Read_Int(Man, Value)<=0) return 0;
manGeoidSepar=Man; return 1; }
if(strcmp(Name, "NavMode")==0)
{ int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0;
NavMode=Mode; return 1; }
if(strcmp(Name, "NavRate")==0)
{ int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0;
if(Mode<1) Mode=1; NavRate=Mode; return 1; }
if(strcmp(Name, "Verbose")==0)
{ int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0;
Verbose=Mode; return 1; }
#ifdef WITH_ENCRYPT
if(strcmp(Name, "Encrypt")==0)
{ int32_t Encr=0; if(Read_Int(Encr, Value)<=0) return 0;
Encrypt=Encr; return 1; }
if(strcmp(Name, "EncryptKey")==0)
{ for( uint8_t Idx=0; Idx<4; Idx++)
{ uint32_t Key;
uint8_t Len=Read_Hex(Key, Value);
if(Len!=8) break;
EncryptKey[Idx]=Key;
Value+=Len;
if((*Value)!=':') break;
Value++; }
return 1; }
#endif
#ifdef WITH_BT_PWR
if(strcmp(Name, "Bluetooth")==0)
{ int32_t bton=0; if(Read_Int(bton, Value)<=0) return 0;
@ -363,6 +513,9 @@ class FlashParameters
if( (memcmp(Name, "WIFIpass", 8)==0) && (strlen(Name)==9) )
{ int Idx=Name[8]-'0'; if( (Idx>=0) && (Idx<WIFIsets) ) return Read_String(WIFIpass[Idx], Value, WIFIpassLen)<=0; }
#endif
if(strcmp(Name, "SaveToFlash")==0)
{ int32_t Save=0; if(Read_Int(Save, Value)<=0) return 0;
SaveToFlash=Save; return 1; }
return 0; }
bool ReadLine(char *Line) // read a parameter line
@ -442,20 +595,30 @@ class FlashParameters
Write_SignDec(Line, "TimeCorr" , (int32_t)TimeCorr ); strcat(Line, " # [ s]\n"); if(fputs(Line, File)==EOF) return EOF;
Write_Float1 (Line, "GeoidSepar", GeoidSepar ); strcat(Line, " # [ m]\n"); if(fputs(Line, File)==EOF) return EOF;
Write_UnsDec (Line, "manGeoidSepar" , manGeoidSepar ); strcat(Line, " # [ 1|0]\n"); if(fputs(Line, File)==EOF) return EOF;
Write_UnsDec (Line, "NavMode" , (uint32_t)NavMode ); strcat(Line, " # [ 0..7]\n"); if(fputs(Line, File)==EOF) return EOF;
Write_UnsDec (Line, "NavRate" , (uint32_t)NavRate ); strcat(Line, " # [ 1,2]\n"); if(fputs(Line, File)==EOF) return EOF;
#ifdef WITH_ENCRYPT
Write_UnsDec (Line, "Encrypt" , Encrypt ); strcat(Line, " # [ 1|0]\n"); if(fputs(Line, File)==EOF) return EOF;
// Write_Hex (Line, "EncryptKey[0]", EncryptKey[0] , 8); strcat(Line, " # [32-bit]\n"); if(fputs(Line, File)==EOF) return EOF;
// Write_Hex (Line, "EncryptKey[1]", EncryptKey[1] , 8); strcat(Line, " # [32-bit]\n"); if(fputs(Line, File)==EOF) return EOF;
// Write_Hex (Line, "EncryptKey[2]", EncryptKey[2] , 8); strcat(Line, " # [32-bit]\n"); if(fputs(Line, File)==EOF) return EOF;
// Write_Hex (Line, "EncryptKey[3]", EncryptKey[3] , 8); strcat(Line, " # [32-bit]\n"); if(fputs(Line, File)==EOF) return EOF;
#endif
Write_UnsDec (Line, "Verbose" , (uint32_t)Verbose ); strcat(Line, " # [ 0..3]\n"); if(fputs(Line, File)==EOF) return EOF;
Write_UnsDec (Line, "PPSdelay" ,(uint32_t)PPSdelay ); strcat(Line, " # [ ms]\n"); if(fputs(Line, File)==EOF) return EOF;
#ifdef WITH_BT_PWR
Write_UnsDec (Line, "Bluetooth" , BT_ON ); strcat(Line, " # [ 1|0]\n"); if(fputs(Line, File)==EOF) return EOF;
#endif
for(uint8_t Idx=0; Idx<InfoParmNum; Idx++)
{ Write_String (Line, OGN_Packet::InfoParmName(Idx), InfoParmValue(Idx)); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF; }
{ Write_String (Line, OGN_Packet::InfoParmName(Idx), InfoParmValue(Idx)); strcat(Line, "; # [char]\n"); if(fputs(Line, File)==EOF) return EOF; }
#ifdef WITH_BT_SPP
strcpy(Line, "BTname = "); strcat(Line, BTname); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
strcpy(Line, "BTname = "); strcat(Line, BTname); strcat(Line, "; # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
#endif
#ifdef WITH_WIFI
for(uint8_t Idx=0; Idx<WIFIsets; Idx++)
{ if(WIFIname[Idx][0]==0) continue;
strcpy(Line, "WIFIname"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIname[Idx]); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
strcpy(Line, "WIFIpass"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIpass[Idx]); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF; }
strcpy(Line, "WIFIname"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIname[Idx]); strcat(Line, "; # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
strcpy(Line, "WIFIpass"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIpass[Idx]); strcat(Line, "; # [char]\n"); if(fputs(Line, File)==EOF) return EOF; }
// Write_String (Line, "WIFIname", WIFIname[0]); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
// Write_String (Line, "WIFIpass", WIFIpass[0]); strcat(Line, " # [char]\n"); if(fputs(Line, File)==EOF) return EOF;
#endif
@ -481,20 +644,30 @@ class FlashParameters
Write_SignDec(Line, "TimeCorr" , (int32_t)TimeCorr ); strcat(Line, " # [ s]\n"); Format_String(Output, Line);
Write_Float1 (Line, "GeoidSepar", GeoidSepar ); strcat(Line, " # [ m]\n"); Format_String(Output, Line);
Write_UnsDec (Line, "manGeoidSepar" , manGeoidSepar ); strcat(Line, " # [ 1|0]\n"); Format_String(Output, Line);
Write_UnsDec (Line, "NavMode" , (uint32_t)NavMode ); strcat(Line, " # [ 0..7]\n"); Format_String(Output, Line);
Write_UnsDec (Line, "NavRate" , (uint32_t)NavRate ); strcat(Line, " # [ 1,2]\n"); Format_String(Output, Line);
#ifdef WITH_ENCRYPT
Write_UnsDec (Line, "Encrypt" , Encrypt ); strcat(Line, " # [ 1|0]\n"); Format_String(Output, Line);
Write_Hex (Line, "EncryptKey[0]", EncryptKey[0] , 8); strcat(Line, " # [32-bit]\n"); Format_String(Output, Line);
Write_Hex (Line, "EncryptKey[1]", EncryptKey[1] , 8); strcat(Line, " # [32-bit]\n"); Format_String(Output, Line);
Write_Hex (Line, "EncryptKey[2]", EncryptKey[2] , 8); strcat(Line, " # [32-bit]\n"); Format_String(Output, Line);
Write_Hex (Line, "EncryptKey[3]", EncryptKey[3] , 8); strcat(Line, " # [32-bit]\n"); Format_String(Output, Line);
#endif
Write_UnsDec (Line, "Verbose" , (uint32_t)Verbose ); strcat(Line, " # [ 0..3]\n"); Format_String(Output, Line);
Write_UnsDec (Line, "PPSdelay" ,(uint32_t)PPSdelay ); strcat(Line, " # [ ms]\n"); Format_String(Output, Line);
#ifdef WITH_BT_PWR
Write_UnsDec (Line, "Bluetooth" , BT_ON ); strcat(Line, " # [ 1|0]\n"); Format_String(Output, Line);
#endif
#ifdef WITH_BT_SPP
strcpy(Line, "BTname = "); strcat(Line, BTname); strcat(Line, " # [char]\n"); Format_String(Output, Line);
strcpy(Line, "BTname = "); strcat(Line, BTname); strcat(Line, "; # [char]\n"); Format_String(Output, Line);
#endif
for(uint8_t Idx=0; Idx<InfoParmNum; Idx++)
{ Write_String (Line, OGN_Packet::InfoParmName(Idx), InfoParmValue(Idx)); strcat(Line, " # [char]\n"); Format_String(Output, Line); }
{ Write_String (Line, OGN_Packet::InfoParmName(Idx), InfoParmValue(Idx)); strcat(Line, "; # [char]\n"); Format_String(Output, Line); }
#ifdef WITH_WIFI
for(uint8_t Idx=0; Idx<WIFIsets; Idx++)
{ if(WIFIname[Idx][0]==0) continue;
strcpy(Line, "WIFIname"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIname[Idx]); strcat(Line, " # [char]\n"); Format_String(Output, Line);
strcpy(Line, "WIFIpass"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIpass[Idx]); strcat(Line, " # [char]\n"); Format_String(Output, Line);; }
strcpy(Line, "WIFIname"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIname[Idx]); strcat(Line, "; # [char]\n"); Format_String(Output, Line);
strcpy(Line, "WIFIpass"); Line[8]='0'+Idx; Line[9]='='; strcpy(Line+10, WIFIpass[Idx]); strcat(Line, "; # [char]\n"); Format_String(Output, Line);; }
// Write_String (Line, "WIFIname", WIFIname[0]); strcat(Line, " # [char]\n"); Format_String(Output, Line);
// Write_String (Line, "WIFIpass", WIFIpass[0]); strcat(Line, " # [char]\n"); Format_String(Output, Line);
#endif

Wyświetl plik

@ -11,14 +11,58 @@
#include "rf.h" // RF task: transmission and reception of radio packets
#include "gps.h" // GPS task: get own time and position, set the GPS baudrate and navigation mode
#include "fifo.h"
#ifdef WITH_FLASHLOG // log own track to unused Flash pages (STM32 only)
#include "flashlog.h"
#endif
#ifdef WITH_SOUND
#include "sound.h"
#endif
#ifdef WITH_LOOKOUT // traffic awareness and warnings
#include "lookout.h"
LookOut Look;
#ifdef WITH_SOUND
const char *Dir[16] = { "N", "NNE", "NE", "NEE", "E", "SEE", "SE", "SSE", "S", "SSW", "SW", "SWW", "W", "NWW", "NW", "NNW" };
const char *RelDir[8] = { "A", "AR", "R", "BR", "B", "BL", "L", "AL" };
void Sound_TrafficWarn(const LookOut_Target *Tgt)
{ if(!Tgt) return;
uint8_t WarnLevel = Tgt->WarnLevel;
// uint16_t DistMargin = Tgt->DistMargin; // [0.5m]
uint16_t TimeMargin = Tgt->TimeMargin; // [0.5s]
uint16_t HorDist = Tgt->HorDist; // [0.5]
uint16_t Bearing = Tgt->getBearing(); //
int16_t RelBearing = Look.getRelBearing(Tgt);
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "Traffic: ");
CONS_UART_Write('#');
CONS_UART_Write('0'+WarnLevel);
CONS_UART_Write(' ');
// Format_Hex(CONS_UART_Write, Bearing);
// CONS_UART_Write(' ');
uint16_t DirIdx = (Bearing+0x800)>>12; DirIdx&=0x0F;
Format_String(CONS_UART_Write, Dir[DirIdx]);
CONS_UART_Write(' ');
uint16_t RelDirIdx = (RelBearing+0x1000)>>13; RelDirIdx&=0x07;
Format_String(CONS_UART_Write, RelDir[RelDirIdx]);
CONS_UART_Write(' ');
Format_UnsDec(CONS_UART_Write, (uint16_t)(HorDist/2));
Format_String(CONS_UART_Write, "m ");
Format_UnsDec(CONS_UART_Write, (uint16_t)(TimeMargin/2));
Format_String(CONS_UART_Write, "s\n");
xSemaphoreGive(CONS_Mutex);
// SoundMsg("Traffic");
}
#endif
#endif
// static uint16_t PrevBattVolt = 0; // [mV]
static Delay<uint16_t, 32> BatteryVoltagePipe;
uint32_t BatteryVoltage = 0; // [1/256 mV] low-pass filtered battery voltage
int32_t BatteryVoltageRate = 0; // [1/256 mV/sec] low-pass filtered battery voltage rise/drop rate
static char Line[128]; // for printing out to the console, etc.
@ -31,12 +75,12 @@ static LDPC_Decoder Decoder; // decoder and error corrector for the OGN
#ifdef WITH_LOG
static int SPIFFSlog(OGN_RxPacket<OGN_Packet> *Packet, uint32_t Time)
{ OGN_LogPacket<OGN_Packet> *LogPacket = LOG_FIFO.getWrite(); if(LogPacket==0) return -1;
LogPacket->Packet = Packet->Packet;
{ OGN_LogPacket<OGN_Packet> *LogPacket = LOG_FIFO.getWrite(); if(LogPacket==0) return -1; // allocate new packet in the LOG_FIFO
LogPacket->Packet = Packet->Packet; // copy the packet
LogPacket->Flags=0x80;
LogPacket->setTime(Time);
LogPacket->setCheck();
LOG_FIFO.Write();
LOG_FIFO.Write(); // finalize the write
return 1; }
static int SPIFFSlog(OGN_TxPacket<OGN_Packet> *Packet, uint32_t Time)
@ -52,13 +96,13 @@ static int SPIFFSlog(OGN_TxPacket<OGN_Packet> *Packet, uint32_t Time)
// ---------------------------------------------------------------------------------------------------------------------------------------
#ifdef WITH_ESP32
const uint8_t RelayQueueSize = 32;
#else
const uint8_t RelayQueueSize = 16;
#endif
// #ifdef WITH_ESP32
// const uint8_t RelayQueueSize = 32;
// #else
// const uint8_t RelayQueueSize = 16;
// #endif
static OGN_PrioQueue<OGN_Packet, RelayQueueSize> RelayQueue; // received packets and candidates to be relayed
OGN_PrioQueue<OGN_Packet, RelayQueueSize> RelayQueue; // received packets and candidates to be relayed
#ifdef DEBUG_PRINT
static void PrintRelayQueue(uint8_t Idx) // for debug
@ -78,7 +122,8 @@ static bool GetRelayPacket(OGN_TxPacket<OGN_Packet> *Packet) // prepare a p
uint8_t Idx=RelayQueue.getRand(RX_Random); // get weight-random packet from the relay queue
if(RelayQueue.Packet[Idx].Rank==0) return 0; // should not happen ...
memcpy(Packet->Packet.Byte(), RelayQueue[Idx]->Byte(), OGN_Packet::Bytes); // copy the packet
Packet->Packet.Header.Relay=1; // increment the relay count (in fact we only do single relay)
Packet->Packet.Header.Relay=1; // increment the relay count (in fact we only do single relay)
// Packet->Packet.calcAddrParity();
Packet->Packet.Whiten(); Packet->calcFEC(); // whiten and calc. the FEC code => packet ready for transmission
// PrintRelayQueue(Idx); // for debug
RelayQueue.decrRank(Idx); // reduce the rank of the packet selected for relay
@ -153,20 +198,60 @@ static void ReadStatus(OGN_Packet &Packet)
#ifdef WITH_ESP32
// Packet.clrTemperature();
uint32_t Battery = BatterySense(); // [mV]
Packet.EncodeVoltage(((Battery*64)+500)/1000); // [1/64V]
uint16_t BattVolt = BatterySense(); // [mV] measure battery voltage
if(BatteryVoltage>0)
{ // int32_t PrevVolt = BatteryVoltage;
int32_t Rate = ((uint32_t)BattVolt<<8) - BatteryVoltage; // [1/256 mV]
BatteryVoltage += (Rate+32)>>6; // [1/256 mV] low-pass battery voltage measurement
uint16_t Volt = (BatteryVoltage+16)>>5;
int16_t Diff = Volt-BatteryVoltagePipe.Input(Volt);
BatteryVoltageRate = Diff; }
// BatteryVoltageRate = BatteryVoltage - PrevVolt; }
// int16_t BattVoltDiff = BattVolt - PrevBattVolt;
// int32_t Diff = ((int32_t)BattVoltDiff<<8) - BatteryVoltageRate;
// BatteryVoltageRate += Diff/256; }
// BatteryVoltageRate = (((int32_t)BattVoltDiff<<8) + BatteryVoltageRate*255 + 128)>>8; }
else
{ BatteryVoltage = BattVolt<<8;
// PrevBattVolt = BattVolt;
BatteryVoltagePipe.Clear(BattVolt<<3);
BatteryVoltageRate = 0; }
// PrevBattVolt = BattVolt;
Packet.EncodeVoltage(((BatteryVoltage>>2)+500)/1000); // [1/64V] encode into the status packet
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "Battery: ");
// Format_UnsDec(CONS_UART_Write, BattVolt);
// CONS_UART_Write(' ');
// Format_UnsDec(CONS_UART_Write, PrevBattVolt);
// CONS_UART_Write(' ');
// Format_UnsDec(CONS_UART_Write, BatteryVoltage, 2);
// CONS_UART_Write(' ');
// Format_SignDec(CONS_UART_Write, BatteryVoltageRate, 2);
// CONS_UART_Write(' ');
Format_UnsDec(CONS_UART_Write, (10*BatteryVoltage+128)>>8, 5, 4);
Format_String(CONS_UART_Write, "V ");
Format_SignDec(CONS_UART_Write, (600*BatteryVoltageRate+128)>>8, 3, 1);
Format_String(CONS_UART_Write, "mV/min\n");
xSemaphoreGive(CONS_Mutex);
#endif
#endif
if(Packet.Status.Pressure==0) Packet.EncodeTemperature(TRX.chipTemp*10); // [0.1degC]
Packet.Status.RadioNoise = TRX.averRSSI; // [-0.5dBm] write radio noise to the status packet
Packet.Status.TxPower = Parameters.getTxPower()-4;
uint8_t TxPower = Parameters.getTxPower()-4;
if(TxPower>15) TxPower=15;
Packet.Status.TxPower = TxPower;
uint16_t RxRate = RX_OGN_Count64+1;
uint8_t RxRateLog2=0; RxRate>>=1; while(RxRate) { RxRate>>=1; RxRateLog2++; }
Packet.Status.RxRate = RxRateLog2;
{ uint8_t Len=0;
if(Parameters.Verbose)
{ uint8_t Len=0;
Len+=Format_String(Line+Len, "$POGNR,"); // NMEA report: radio status
Len+=Format_UnsDec(Line+Len, RF_FreqPlan.Plan); // which frequency plan
Line[Len++]=',';
@ -201,10 +286,6 @@ static void ReadStatus(OGN_Packet &Packet)
Format_String(Log_Write, Line, Len); // send the NMEA out to the log file
xSemaphoreGive(Log_Mutex); }
#endif
// #ifdef WITH_FollowMe
// Format_String(ADSB_UART_Write, "ADS-B UART test\n");
// #endif
}
}
@ -244,10 +325,13 @@ static void ProcessRxPacket(OGN_RxPacket<OGN_Packet> *RxPacket, uint8_t RxPacket
if(DistOK)
{ RxPacket->calcRelayRank(GPS_Altitude/10); // calculate the relay-rank (priority for relay)
OGN_RxPacket<OGN_Packet> *PrevRxPacket = RelayQueue.addNew(RxPacketIdx);
uint8_t Len=RxPacket->WritePOGNT(Line); // print on the console as $POGNT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex);
#ifdef WITH_POGNT
if(Parameters.Verbose)
{ uint8_t Len=RxPacket->WritePOGNT(Line); // print on the console as $POGNT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex); }
#endif
// Len=RxPacket->Packet.WriteAPRS(Line, RxTime); // print on the console as APRS message
// xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
// Format_String(CONS_UART_Write, Line, 0, Len);
@ -256,9 +340,9 @@ static void ProcessRxPacket(OGN_RxPacket<OGN_Packet> *RxPacket, uint8_t RxPacket
const LookOut_Target *Tgt=Look.ProcessTarget(RxPacket->Packet); // process the received target postion
if(Tgt) Warn=Tgt->WarnLevel; // remember warning level of this target
#ifdef WITH_BEEPER
if(KNOB_Tick>12) Play(Play_Vol_1 | Play_Oct_2 | (7+2*Warn), 3+16*Warn); // if Knob>12 => make a beep for every received packet
if(KNOB_Tick>12) Play(Play_Vol_1 | Play_Oct_2 | (7+2*Warn), 3+16*Warn);
#endif
#else // WITH_LOOKOUT
#else // if not WITH_LOOKOUT
#ifdef WITH_BEEPER
if(KNOB_Tick>12) Play(Play_Vol_1 | Play_Oct_2 | 7, 3); // if Knob>12 => make a beep for every received packet
#endif
@ -266,7 +350,7 @@ static void ProcessRxPacket(OGN_RxPacket<OGN_Packet> *RxPacket, uint8_t RxPacket
#ifdef WITH_LOG
bool Signif = PrevRxPacket!=0;
if(!Signif) Signif=OGN_isSignif(&(RxPacket->Packet), &(PrevRxPacket->Packet));
if(Signif) SPIFFSlog(RxPacket, RxTime);
if(Signif) SPIFFSlog(RxPacket, RxTime); // log only significant packets
#endif
#ifdef WITH_SDLOG
if(Log_Free()>=128)
@ -275,22 +359,23 @@ static void ProcessRxPacket(OGN_RxPacket<OGN_Packet> *RxPacket, uint8_t RxPacket
xSemaphoreGive(Log_Mutex); }
#endif
#ifdef WITH_PFLAA
Len=RxPacket->WritePFLAA(Line, Warn, LatDist, LonDist, RxPacket->Packet.DecodeAltitude()-GPS_Altitude/10); // print on the console
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex);
if( Parameters.Verbose // print PFLAA on the console for received packets
#ifdef WITH_LOOKOUT
&& (!Tgt)
#endif
)
{ uint8_t Len=RxPacket->WritePFLAA(Line, Warn, LatDist, LonDist, RxPacket->Packet.DecodeAltitude()-GPS_Altitude/10);
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex); }
#endif
#ifdef WITH_MAVLINK
MAV_ADSB_VEHICLE MAV_RxReport;
RxPacket->Packet.Encode(&MAV_RxReport);
MAV_RxMsg::Send(sizeof(MAV_RxReport), MAV_Seq++, MAV_SysID, MAV_COMP_ID_ADSB, MAV_ID_ADSB_VEHICLE, (const uint8_t *)&MAV_RxReport, GPS_UART_Write);
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "MAV_ADSB_VEHICLE sent. ID=");
Format_Hex(CONS_UART_Write, MAV_RxReport.ICAO_address);
CONS_UART_Write('\r'); CONS_UART_Write('\n');
xSemaphoreGive(CONS_Mutex);
#endif
// xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
// MAV_RxMsg::Send(sizeof(MAV_RxReport), MAV_Seq++, MAV_SysID, MAV_COMP_ID_ADSB, MAV_ID_ADSB_VEHICLE, (const uint8_t *)&MAV_RxReport, CONS_UART_Write);
// xSemaphoreGive(CONS_Mutex);
#endif
}
}
@ -354,7 +439,7 @@ void vTaskPROC(void* pvParameters)
{ vTaskDelay(1);
RFM_RxPktData *RxPkt = RF_RxFIFO.getRead(); // check for new received packets
if(RxPkt)
if(RxPkt) // if there is a new received packet
{
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
@ -367,11 +452,11 @@ void vTaskPROC(void* pvParameters)
xSemaphoreGive(CONS_Mutex);
#endif
DecodeRxPacket(RxPkt); // decode and process the received packet
RF_RxFIFO.Read(); }
RF_RxFIFO.Read(); } // remove this packet from the queue
static uint32_t PrevSlotTime=0; // remember previous time slot to detect a change
uint32_t SlotTime = TimeSync_Time(); // time slot
if(TimeSync_msTime()<300) SlotTime--; // lasts up to 0.300sec after the PPS
if(TimeSync_msTime()<340) SlotTime--; // lasts up to 0.300sec after the PPS
if(SlotTime==PrevSlotTime) continue; // stil same time slot, go back to RX processing
PrevSlotTime=SlotTime; // new slot started
@ -380,13 +465,13 @@ void vTaskPROC(void* pvParameters)
#ifdef WITH_MAVLINK
GPS_Position *Position = GPS_getPosition(BestIdx, BestResid, (SlotTime-1)%60, 0);
#else
GPS_Position *Position = GPS_getPosition(BestIdx, BestResid, SlotTime%60, 0);
GPS_Position *Position = GPS_getPosition(BestIdx, BestResid, SlotTime%60, 0); // get GPS position which isReady
#endif
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "getPos() => ");
Format_String(CONS_UART_Write, "getPos(");
Format_UnsDec(CONS_UART_Write, SlotTime%60, 2);
CONS_UART_Write(' ');
Format_String(CONS_UART_Write, ") => ");
Format_UnsDec(CONS_UART_Write, (uint16_t)BestIdx);
CONS_UART_Write(':');
Format_SignDec(CONS_UART_Write, BestResid, 3, 2);
@ -395,12 +480,16 @@ void vTaskPROC(void* pvParameters)
#endif
// GPS_Position *Position = GPS_getPosition();
if(Position) Position->EncodeStatus(StatPacket.Packet); // encode GPS altitude and pressure/temperature/humidity
if( Position && Position->isReady && (!Position->Sent) && Position->isReady && Position->isValid() )
else { StatPacket.Packet.Status.FixQuality=0; StatPacket.Packet.Status.Satellites=0; } // or lack of the GPS lock
{ uint8_t SatSNR = (GPS_SatSNR+2)/4;
if(SatSNR>8) { SatSNR-=8; if(SatSNR>31) SatSNR=31; }
else { SatSNR=0; }
StatPacket.Packet.Status.SatSNR = SatSNR; }
if( Position && Position->isReady && (!Position->Sent) && Position->isValid() )
{ int16_t AverSpeed=GPS_AverageSpeed(); // [0.1m/s] average speed, including the vertical speed
if(Parameters.FreqPlan==0)
RF_FreqPlan.setPlan(Position->Latitude, Position->Longitude); // set the frequency plan according to the GPS position
else RF_FreqPlan.setPlan(Parameters.FreqPlan);
/*
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60);
@ -409,7 +498,6 @@ void vTaskPROC(void* pvParameters)
Format_String(CONS_UART_Write, " -> Sent\n");
xSemaphoreGive(CONS_Mutex);
#endif
*/
PosTime=Position->getUnixTime();
PosPacket.Packet.HeaderWord=0;
PosPacket.Packet.Header.Address = Parameters.Address; // set address
@ -420,12 +508,17 @@ void vTaskPROC(void* pvParameters)
#endif
PosPacket.Packet.calcAddrParity(); // parity of (part of) the header
if(BestResid==0) Position->Encode(PosPacket.Packet); // encode position/altitude/speed/etc. from GPS position
else Position->Encode(PosPacket.Packet, BestResid);
else // extrapolate the position when if not at an exact UTC second
{ while(BestResid>=50) BestResid-=100; // remove full seconds
Position->Encode(PosPacket.Packet, BestResid); }
PosPacket.Packet.Position.AcftType = Parameters.AcftType; // aircraft-type
// { uint8_t Len=PosPacket.Packet.WriteAPRS(Line, PosTime); // print on the console as APRS message
// xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
// Format_String(CONS_UART_Write, Line, 0, Len);
// xSemaphoreGive(CONS_Mutex); }
PosPacket.Packet.Position.Stealth = 0; // Parameters.Stealth;
#ifdef DEBUG_PRINT
{ uint8_t Len=PosPacket.Packet.WriteAPRS(Line, PosTime); // print on the console as APRS message
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex); }
#endif
OGN_TxPacket<OGN_Packet> *TxPacket = RF_TxFIFO.getWrite();
TxPacket->Packet = PosPacket.Packet; // copy the position packet to the TxFIFO
#ifdef WITH_ENCRYPT
@ -437,7 +530,7 @@ void vTaskPROC(void* pvParameters)
TxPacket->calcFEC(); // whiten and calculate FEC code
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60);
Format_UnsDec(CONS_UART_Write, TimeSync_Time()%60, 2);
CONS_UART_Write('.');
Format_UnsDec(CONS_UART_Write, TimeSync_msTime(), 3);
Format_String(CONS_UART_Write, " TxFIFO <- ");
@ -446,40 +539,28 @@ void vTaskPROC(void* pvParameters)
xSemaphoreGive(CONS_Mutex);
#endif
XorShift32(RX_Random);
if( (AverSpeed>10) || ((RX_Random&0x3)==0) ) // send only some positions if the speed is less than 1m/s
RF_TxFIFO.Write(); // complete the write into the TxFIFO
if( (AverSpeed>10) || ((RX_Random&0x3)==0) ) // send only some positions if the speed is less than 1m/s
RF_TxFIFO.Write(); // complete the write into the TxFIFO
Position->Sent=1;
// #ifdef WITH_MAVLINK
// { MAV_HEARTBEAT MAV_HeartBeat;
// // = { custom_mode:0,
// // type:0,
// // autopilot:0,
// // base_mode:0,
// // system_status:4,
// // mavlink_version:1
// // };
// MAV_HeartBeat.custom_mode=0;
// MAV_HeartBeat.type=0;
// MAV_HeartBeat.autopilot=0;
// MAV_HeartBeat.base_mode=0;
// MAV_HeartBeat.system_status=4;
// MAV_HeartBeat.mavlink_version=1;
// MAV_RxMsg::Send(sizeof(MAV_HeartBeat), MAV_Seq++, MAV_SysID, MAV_COMP_ID_ADSB, MAV_ID_HEARTBEAT, (const uint8_t *)&MAV_HeartBeat, GPS_UART_Write);
// }
// #endif
#ifdef WITH_LOOKOUT
const LookOut_Target *Tgt=Look.ProcessOwn(PosPacket.Packet); // process own position, get the most dangerous target
const LookOut_Target *Tgt=Look.ProcessOwn(PosPacket.Packet); // process own position, get the most dangerous target
#ifdef WITH_PFLAA
uint8_t Len=Look.WritePFLAU(Line);
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex);
if(Parameters.Verbose)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Look.WritePFLA(CONS_UART_Write); // produce PFLAU and PFLAA for all tracked targets
xSemaphoreGive(CONS_Mutex); }
#else
if(Parameters.Verbose)
{ uint8_t Len=Look.WritePFLAU(Line); // $PFLAU, overall status
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len);
xSemaphoreGive(CONS_Mutex); }
#endif // WITH_PFLAA
uint8_t Warn = 0;
if(Tgt) Warn = Tgt->WarnLevel; // what is the warning level ?
if( (Warn>0) && (AverSpeed>=10) ) // if non-zero warning level and we seem to be moving
{ int16_t RelBearing = Look.getRelBearing(Tgt); // relative bearing to the Target
int8_t Bearing = (12*(int32_t)RelBearing+0x8000)>>16; // [-12..+12]
if( (Warn>0) /* && (AverSpeed>=10) */ ) // if non-zero warning level and we seem to be moving
{ // int16_t RelBearing = Look.getRelBearing(Tgt); // relative bearing to the Target
// int8_t Bearing = (12*(int32_t)RelBearing+0x8000)>>16; // [-12..+12]
#ifdef WITH_BEEPER // make the sound according to the level
if(Warn<=1)
{ if(KNOB_Tick>8)
@ -498,6 +579,9 @@ void vTaskPROC(void* pvParameters)
}
#endif // WITH_BEEPER
#ifdef WITH_SOUND
Sound_TrafficWarn(Tgt);
#endif
}
#endif // WITH_LOOKOUT
#ifdef WITH_FLASHLOG

Wyświetl plik

@ -1,3 +1,20 @@
extern uint32_t BatteryVoltage; // [1/256 mV] averaged
extern int32_t BatteryVoltageRate; // [1/256 mV] averaged
#ifdef WITH_LOOKOUT // traffic awareness and warnings
#include "lookout.h"
extern LookOut Look;
#endif
#ifdef WITH_ESP32
const uint8_t RelayQueueSize = 32;
#else
const uint8_t RelayQueueSize = 16;
#endif
extern OGN_PrioQueue<OGN_Packet, RelayQueueSize> RelayQueue; // received packets and candidates to be relayed
#ifdef __cplusplus
extern "C"
#endif

279
main/relpos.h 100644
Wyświetl plik

@ -0,0 +1,279 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "intmath.h"
#include "ogn.h"
// =======================================================================================================
class Acft_RelPos // 3-D relative position with speed and turn rate
{ public:
int16_t T; // [0.5sec]
int16_t X,Y,Z; // [0.5m]
uint16_t Speed; // [0.5m/s]
uint16_t Heading; // [360/0x10000 deg]
int16_t Climb; // [0.5m/s]
int16_t Turn; // [360/0x10000 deg/s] [2*PI/0x10000 rad/sec]
int16_t Dx,Dy; // [2^-12] directon vector - calc. on Heading
uint8_t Error; // [0.5m]
uint8_t Spare;
// int16_t Ax,Ay; // [1/16m/s^2] acceleration vactor
// int16_t R; // [0.5m] (signed) turning radius - calc. from Turn and Speed
// int16_t Ox,Oy; // [0.5m] turning circle center - only valid when R!=0
public:
void Print(void) const
{ printf("%+7.1f: [%+7.1f,%+7.1f,%+7.1f]m %5.1fm/s %05.1fdeg %+5.1fm/s %+5.1fdeg/sec [%3.1fm]",
0.5*T, 0.5*X, 0.5*Y, 0.5*Z, 0.5*Speed, (360.0/0x10000)*Heading, 0.5*Climb, (360.0/0x10000)*Turn, 0.5*Error);
// printf(" [%+6.2f,%+6.2f]m/s^2", 0.0625*Ax, 0.0625*Ay);
// if(R) printf(" R:%+8.1fm [%+7.1f, %+7.1f]", 0.5*R, 0.5*Ox, 0.5*Oy);
printf("\n"); }
void Clear(void)
{ T =0;
X =0; Y =0; Z =0;
Speed=0; Heading=0; Climb=0; Turn=0;
Error=4; }
uint32_t SqrDistance(Acft_RelPos &Target)
{ int32_t dX = Target.X-X;
int32_t dY = Target.Y-Y;
int32_t dZ = Target.Z-Z;
return dX*dX+dY*dY+dZ*dZ; } // [0.25m^2]
static uint32_t SqrDistance(int16_t dX, int16_t dY, int16_t dZ)
{ return (int32_t)dX*dX + (int32_t)dY*dY + (int32_t)dZ*dZ; }
static uint32_t SqrDistance(int16_t dX, int16_t dY)
{ return (int32_t)dX*dX + (int32_t)dY*dY; }
uint32_t FastDistance(Acft_RelPos &Target)
{ int16_t dX = Target.X-X;
int16_t dY = Target.Y-Y;
int16_t dZ = Target.Z-Z;
return FastDistance(dX, dY, dZ); } // [0.5m]
static uint16_t FastDistance(int16_t dX, int16_t dY)
{ dX = abs(dX); dY = abs(dY);
if(dX>dY) return dX+dY/2;
else return dY+dX/2; }
static uint16_t FastDistance(int16_t dX, int16_t dY, int16_t dZ)
{ return FastDistance((int16_t)FastDistance(dX, dY), dZ); }
// predict self and target until MinSepar is reached but no longer than MaxTime
int16_t StepTillMinSepar(Acft_RelPos &Target, uint16_t MinSepar, int16_t MaxTime=40) // [0.5m] [0.5s]
{ int16_t PredTime=0; // count time by which we predict
uint16_t MaxTurn=abs(Turn); // the max. turn rate
uint16_t Turn2=abs(Target.Turn); if(Turn2>MaxTurn) MaxTurn=Turn2;
int16_t MaxStepTime = 32; // [0.5s] max. allowed stpping time period
if(MaxTurn>=0x100) MaxStepTime=0x2000/MaxTurn; // [0.5s] maximup step time (for sharp turns)
if(MaxStepTime<2) MaxStepTime=2; // [0.5s] but don't do smaller steps than 1sec
uint16_t TotSpeed = FastDistance(Speed, Climb) + FastDistance(Target.Speed, Target.Climb); // [0.5m/s] "total" speed, thus the sum of the two speeds magnitudes
#ifdef DEBUG_PRINT
printf("StepTillMinSepar( , MinSepar=%3.1fm, MaxTime=%3.1fs)\n", 0.5*MinSepar, 0.5*MaxTime);
Print(); Target.Print();
#endif
for( ; ; )
{ if(MaxTime==0) return PredTime; // if max. prediction time reached: stop
int32_t DistMargin = FastDistance(Target); // [0.5m] Distance margin to the target
if(DistMargin<=MinSepar) return PredTime; // If distance margin already below minimum
int16_t dT = Target.T-T; // [0.5sec] Target may not be exact same time
int32_t dS = (dT*TotSpeed)>>1; // [0.5m] thus some extra distance margin
DistMargin -= abs(dS); // [0.5m] subtract margin due to time difference
if(DistMargin<=MinSepar) return PredTime; // [0.5m] if distance margin below minimum
DistMargin -= MinSepar; // [0.5m] subtract minimum separation from the distance margin
if((TotSpeed*MaxTime) < (2*DistMargin) ) // If plenty enough margin given the speed
{ uint16_t TimeMargin=MaxTime+2;
if((2*DistMargin)<(TotSpeed*TimeMargin)) TimeMargin=2*DistMargin/TotSpeed+1;
TimeMargin+=PredTime; // if(TimeMargin>240) TimeMargin=240;
return TimeMargin; }
int16_t StepTime = (2*DistMargin)/TotSpeed+1; // [0.5s] Time margin given distance margin and speed
#ifdef DEBUG_PRINT
printf("DistMargin=%3.1fm => StepTime=%+4.1fs\n", 0.5*DistMargin, 0.5*StepTime);
#endif
// if(StepTime==0) return PredTime;
// if(StepTime<1) StepTime=1; // minimum step time for prediction
if(StepTime>MaxStepTime) StepTime=MaxStepTime; // [0.5s] maximum step time for prediction
if(StepTime>MaxTime) StepTime=MaxTime;
int16_t NextTime = T+StepTime; // [0.5s] time
StepFwd(StepTime);
int16_t TgtStepTime = NextTime-Target.T;
if(TgtStepTime>0) Target.StepFwd(TgtStepTime);
#ifdef DEBUG_PRINT
Print(); Target.Print();
#endif
PredTime+=StepTime; // [0.5s] count time by which we predict
MaxTime-=StepTime; } // [0.5s] decrement the max. time we should predict
return MaxTime+1; } // return estimated time margin before the separation could fall below minimum
// predict the minimum distance: does not take turn into account thus must not be used on significant distances
int16_t MissTime(Acft_RelPos &Target, int16_t MaxTime=40) // [0.5s]
{ int32_t dX = Target.X; // [0.5m] Target distance vector
int32_t dY = Target.Y;
int32_t dZ = Target.Z;
int32_t tVx, tVy; Target.getSpeedVector(tVx, tVy); // [0.5m/s] Target speed vector
int32_t tVz = Target.Climb;
int16_t dT = Target.T - T; // [0.5sec] time difference betwen target and me
if(dT)
{ dX -= (dT*tVx)>>1; // adjust target position for the time diff.
dY -= (dT*tVy)>>1;
dZ -= (dT*tVz)>>1; }
dX -= X; // [0.5m] Target relative position
dY -= Y;
dZ -= Z;
int32_t dVx, dVy; getSpeedVector(dVx, dVy); // [0.5m/s] Target relative speed
dVx = tVx-dVx;
dVy = tVy-dVy;
int32_t dVz = tVz - Climb;
int32_t DistSqrRate = dVx*dX + dVy*dY + dVz*dZ; // [0.25m2/s] 3-D scalar product: rel. distance x rel. speed
// if(DistSqrRate==0) return MaxTime; // if target neither moving away nor closing
// if(DistSqrRate>0) return MaxTime; // if target moving away from me
int32_t RelVelSqr = dVx*dVx + dVy*dVy + dVz*dVz; // [0.25m2/s2] 3-D relative velocity square
// printf("MissTime() DistSqrRate = %+4.1fm^2/s RelVelSqr = %3.1f(m/s)^2\n", 0.25*DistSqrRate, 0.5*RelVelSqr);
if( ( RelVelSqr*MaxTime) <= (-2*DistSqrRate) ) return MaxTime; // if min. approach time is longer than maximum
if( (-RelVelSqr*MaxTime) >= (-2*DistSqrRate) ) return -MaxTime; // if min. approach time is longer than maximum
return (-2*DistSqrRate+(RelVelSqr/2))/RelVelSqr; } // [0.5sec] time of the closest approach
void calcDir(void) // calculate the direction unity vector
{ Dx = Icos(Heading); // DirX = cos(Heading)
Dy = Isin(Heading); } // DirY = sin(Heading)
// void calcAccel(void)
// { int32_t A = ((int32_t)Turn*Speed*201+0x8000)>>16; // [1/64m/s^2]
// Ax = (-A*Dy+0x2000)>>14; // [1/16m/s^2]
// Ay = (+A*Dx+0x2000)>>14; }
// void calcTurn(int16_t MaxRadius=10000) // calculate the turning radius and turning circle center
// { R=getTurnRadius(MaxRadius); if(R==0) return;
// Ox = X - ((Dy*R)>>12);
// Oy = Y + ((Dx*R)>>12); }
int16_t getTurnDev(int16_t dT) // approx. horizontal deviation from straight line due to the turn
{ int32_t A = ((int32_t)Turn*Speed*201+0x8000)>>16; // [1/64m/s^2] acceleration
// printf("getTurnDev() A=%6.3fm/s^2\n", A/64.0);
return (A*dT*dT+0x80)>>8; } // [0.5m]
int16_t getTurnRadius(int32_t MaxRadius=0x7FFF) const
{ int32_t Radius = (int32_t)Speed*10436;
if(Turn==0) return 0; // return zero for radius larger than maximum defined
Radius/=Turn;
if(abs(Radius)>MaxRadius) return 0;
return Radius; } // return turning radius [0.5m] = turning circle diameter [m]
void getSpeedVector(int32_t &Vx, int32_t &Vy) // [0.5m/s] [0.5m/s]
{ Vx = ((int32_t)Dx*Speed+0x800)>>12; // Vx = DirX * Speed
Vy = ((int32_t)Dy*Speed+0x800)>>12; } // Vy = DirY * Speed
void getSpeedVector(int16_t &Vx, int16_t &Vy) // [0.5m/s] [0.5m/s]
{ Vx = ((int32_t)Dx*Speed+0x800)>>12; // Vx = DirX * Speed
Vy = ((int32_t)Dy*Speed+0x800)>>12; } // Vy = DirY * Speed
void StepFwd(int16_t Time) // [0.5s] predict the position into the future
{ int16_t Vx, Vy;
int16_t Time1 = Time>>1;
int16_t Time2 = Time-Time1;
getSpeedVector(Vx, Vy);
int16_t dX = Time1*Vx; int16_t dY = Time1*Vy;
Heading += (Time*Turn)>>1;
calcDir(); // calcAccel();
getSpeedVector(Vx, Vy);
dX += Time2*Vx; dY += Time2*Vy;
X += dX/2;
Y += dY/2;
Z += (Time*Climb)>>1;
T += Time; }
void StepFwdSecs(int16_t Secs) // [sec] predict the position Secs into the future
{ int16_t Vx, Vy;
int16_t Secs1=Secs>>1;
int16_t Secs2=Secs-Secs1;
getSpeedVector(Vx, Vy);
X += Secs1*Vx; Y += Secs1*Vy;
Heading += Secs*Turn;
calcDir(); // calcAccel();
getSpeedVector(Vx, Vy);
X += Secs2*Vx; Y += Secs2*Vy;
Z += Secs*Climb;
T += 2*Secs; }
void StepFwd4secs(void) // predict the position four second into the future
{ int16_t Vx, Vy;
getSpeedVector(Vx, Vy);
X += 2*Vx; Y += 2*Vy;
Heading += 4*Turn;
calcDir(); // calcAccel();
getSpeedVector(Vx, Vy);
X += 2*Vx; Y += 2*Vy;
Z += 4*Climb;
T += 8; }
void StepFwd2secs(void) // predict the position two seconds into the future
{ int16_t Vx, Vy;
getSpeedVector(Vx, Vy); // get hor. speed vector from speed and dir. vector
X += Vx; Y += Vy; // incr. the hor. coordinates by half the speed
Heading += 2*Turn; // increment the heading by the turning rate
calcDir(); // calcAccel(); // recalc. direction and acceleration
getSpeedVector(Vx, Vy); // recalc. the speed vector
X += Vx; Y += Vy; // incr. horizotal coord. by half the speed vector
Z += 2*Climb; // increment the rel. altitude by the climb rate
T += 4; } // increment time by 2sec
void StepFwd1sec(void) // predict the position one second into the future
{ int16_t Vx, Vy;
getSpeedVector(Vx, Vy);
X += Vx/2; Y += Vy/2;
Heading += Turn;
calcDir(); // calcAccel();
getSpeedVector(Vx, Vy);
X += Vx/2; Y += Vy/2;
Z += Climb;
T += 2; }
template <class OGNx_Packet> // read position from an OGN packet, use provided reference
int32_t Read(OGNx_Packet &Packet, uint8_t RefTime, int32_t RefLat, int32_t RefLon, int32_t RefAlt, uint16_t LatCos=3000, int32_t MaxDist=10000)
{ T = (int16_t)Packet.Position.Time-(int16_t)RefTime;
if(T<=(-30)) T+=60; else if(T>30) T-=60;
T<<=1;
int32_t LatDist, LonDist;
if(Packet.calcDistanceVector(LatDist, LonDist, RefLat, RefLon, LatCos, MaxDist)<0) return -1;
X = LatDist<<1; // [m] => [0.5m]
Y = LonDist<<1; // [m] => [0.5m]
Z = (Packet.DecodeAltitude()-RefAlt)<<1; // [m] => [0.5m]
Speed = (Packet.DecodeSpeed()+2)/5; // [0.1m/s] => [0.5m/s]
Heading = Packet.getHeadingAngle(); // [360/0x10000deg]
Climb = Packet.DecodeClimbRate()/5; // [0.1m/s] => [0.5m/s]
Turn = ((int32_t)Packet.DecodeTurnRate()*1165+32)>>6; // [0.1deg/s] => [360/0x10000deg/s]
calcDir();
Error = (2*Packet.DecodeDOP()+22)/5;
// calcAccel();
// calcTurn();
// printf("Read: "); Packet.Print();
// Print();
return 1; }
template <class OGNx_Packet> // write position into the OGN packet, using given reference
void Write(OGNx_Packet &Packet, uint8_t RefTime, int32_t RefLat, int32_t RefLon, int32_t RefAlt, uint16_t LatCos=3000)
{ int16_t Time=RefTime+(T>>1);
// if(Time<0) Time+=60; else if(Time>=60) Time-=60;
Packet.Position.Time = Time%60;
Packet.setDistanceVector(X>>1, Y>>1, RefLat, RefLon, LatCos);
Packet.EncodeAltitude(RefAlt+(Z>>1)); //
Packet.clrBaro(); // don't know the standard pressure altitude
Packet.EncodeSpeed(Speed*5); // [0.5m/s] => [0.1m/s]
Packet.setHeadingAngle(Heading); //
Packet.EncodeClimbRate(Climb*5); // [0.5m/s] => [0.1m/s]
Packet.EncodeTurnRate((Turn*7+64)>>7); // [360/0x10000deg/s] => [0.1deg/s]
Packet.EncodeDOP((5*Error)/2-10);
}
} ;
// =======================================================================================================

Wyświetl plik

@ -157,16 +157,27 @@ static void SetFreqPlan(void) // set the RF TRX a
}
static uint8_t StartRFchip(void)
{ TRX.RESET(1); // RESET active
vTaskDelay(10); // wait 10ms
{ TRX.WriteMode(RF_OPMODE_STANDBY);
vTaskDelay(1);
TRX.RESET(1); // RESET active
vTaskDelay(1); // wait 10ms
TRX.RESET(0); // RESET released
vTaskDelay(10); // wait 10ms
vTaskDelay(5); // wait 10ms
SetFreqPlan(); // set TRX base frequency and channel separation after the frequency hopping plan
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
TRX.PrintReg(CONS_UART_Write);
xSemaphoreGive(CONS_Mutex);
#endif
#ifdef WITH_RFM95
TRX.WriteDefaultReg();
#endif
TRX.Configure(0, OGN_SYNC); // setup RF chip parameters and set to channel #0
TRX.WriteMode(RF_OPMODE_STANDBY); // set RF chip mode to STANDBY
uint8_t Version = TRX.ReadVersion();
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
TRX.PrintReg(CONS_UART_Write);
Format_String(CONS_UART_Write, "StartRFchip() v");
Format_Hex(CONS_UART_Write, Version);
CONS_UART_Write(' ');
@ -196,7 +207,7 @@ extern "C"
TRX.DIO0_isOn = RFM_IRQ_isOn;
TRX.RESET = RFM_RESET;
RF_FreqPlan.setPlan(Parameters.FreqPlan); // 1 = Europe/Africa, 2 = USA/CA, 3 = Australia and South America
RF_FreqPlan.setPlan(Parameters.FreqPlan); // 1 = Europe/Africa, 2 = USA/CA, 3 = Australia and South America
vTaskDelay(5);
@ -204,8 +215,8 @@ extern "C"
{ uint8_t ChipVersion = StartRFchip();
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "TaskRF: ");
CONS_UART_Write('v'); Format_Hex(CONS_UART_Write, ChipVersion);
Format_String(CONS_UART_Write, "TaskRF: v");
Format_Hex(CONS_UART_Write, ChipVersion);
CONS_UART_Write('\r'); CONS_UART_Write('\n');
xSemaphoreGive(CONS_Mutex);
@ -227,9 +238,10 @@ extern "C"
for( ; ; )
{
while(Button_SleepRequest)
{ TRX.WriteMode(RF_OPMODE_SLEEP);
vTaskDelay(100); }
// #ifdef DEBUG_PRINT
// RF_Print();
// #endif
uint32_t RxRssiSum=0; uint16_t RxRssiCount=0; // measure the average RSSI for lower frequency
do
@ -246,6 +258,14 @@ extern "C"
TRX.WriteMode(RF_OPMODE_STANDBY); // switch to standy
vTaskDelay(1);
if(PowerMode==0)
{ TRX.WriteMode(RF_OPMODE_SLEEP);
while(PowerMode==0)
vTaskDelay(1);
TRX.WriteMode(RF_OPMODE_STANDBY);
vTaskDelay(1); }
SetFreqPlan();
TRX.averRSSI=RX_RSSI.getOutput();

Wyświetl plik

@ -23,12 +23,12 @@ class RFM_RxPktData // packet received by the RF chip
void Print(void (*CONS_UART_Write)(char), uint8_t WithData=0) const
{ // uint8_t ManchErr = Count1s(RxPktErr, 26);
Format_String(CONS_UART_Write, "RxPktData: ");
Format_UnsDec(CONS_UART_Write, Time);
Format_HHMMSS(CONS_UART_Write, Time);
CONS_UART_Write('+');
Format_UnsDec(CONS_UART_Write, msTime, 4, 3);
CONS_UART_Write(' '); Format_Hex(CONS_UART_Write, Channel);
CONS_UART_Write('/');
Format_SignDec(CONS_UART_Write, (int16_t)(-5*(int16_t)RSSI), 4, 1);
Format_SignDec(CONS_UART_Write, (int16_t)(-5*(int16_t)RSSI), 3, 1);
Format_String(CONS_UART_Write, "dBm\n");
if(WithData==0) return;
for(uint8_t Idx=0; Idx<Bytes; Idx++)
@ -128,17 +128,17 @@ class RFM_RxPktData // packet received by the RF chip
#define RF_IRQ_PllLock 0x1000 //
#define RF_IRQ_Rssi 0x0800
#define RF_IRQ_Timeout 0x0400
//
#define RF_IRQ_PreambleDetect 0x0200
#define RF_IRQ_SyncAddrMatch 0x0100
#define RF_IRQ_FifoFull 0x0080 //
#define RF_IRQ_FifoNotEmpty 0x0040 // at least one byte in the FIFO
#define RF_IRQ_FifoLevel 0x0020 // more bytes than FifoThreshold
#define RF_IRQ_FifoOverrun 0x0010 // write this bit to clear the FIFO
#define RF_IRQ_PacketSent 0x0008 // packet transmission was completed
#define RF_IRQ_PayloadReady 0x0004
#define RF_IRQ_CrcOk 0x0002
#define RF_IRQ_LowBat 0x0001
#define RF_IRQ_FifoFull 0x0080 //
#define RF_IRQ_FifoNotEmpty 0x0040 // at least one byte in the FIFO
#define RF_IRQ_FifoLevel 0x0020 // more bytes than FifoThreshold
#define RF_IRQ_FifoOverrun 0x0010 // write this bit to clear the FIFO
#define RF_IRQ_PacketSent 0x0008 // packet transmission was completed
#define RF_IRQ_PayloadReady 0x0004
#define RF_IRQ_CrcOk 0x0002
#define RF_IRQ_LowBat 0x0001
#include "manchester.h"
@ -180,8 +180,30 @@ class RFM_TRX
uint8_t chipVer; // [] version ID read from the RF chip
int8_t chipTemp; // [degC] temperature read from the RF chip
uint8_t averRSSI; // [-0.5dB]
uint8_t dummy;
#ifdef WITH_RFM95
void WriteDefaultReg(void)
{ const uint8_t Default[64] = { 0x00, 0x01, 0x1A, 0x0B, 0x00, 0x52, 0xE4, 0xC0, 0x00, 0x0F, 0x19, 0x2B, 0x20, 0x08, 0x02, 0x0A,
0xFF, 0x00, 0x15, 0x0B, 0x28, 0x0C, 0x12, 0x47, 0x32, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x93, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x90, 0x40, 0x40, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF5, 0x20, 0x82, 0x00, 0x02, 0x80, 0x40 };
WriteBytes(Default+0x01, 0x0F, 0x01);
WriteBytes(Default+0x10, 0x10, 0x10);
WriteBytes(Default+0x20, 0x10, 0x20);
WriteBytes(Default+0x30, 0x10, 0x30);
WriteWord(0x0000, 0x40);
WriteByte(0x2D, 0x44);
WriteByte(0x09, 0x4B);
WriteByte(0x84, 0x4D);
WriteByte(0x00, 0x5D);
WriteByte(0x13, 0x61);
WriteByte(0x0E, 0x62);
WriteByte(0x5B, 0x63);
WriteByte(0xDB, 0x64);
}
#endif
// private:
static uint32_t calcSynthFrequency(uint32_t Frequency) { return (((uint64_t)Frequency<<16)+7812)/15625; }
public:
@ -355,7 +377,7 @@ class RFM_TRX
{ if(SyncTol>7) SyncTol=7;
if(WriteSize>8) WriteSize=8;
WriteBytes(SyncData+(8-WriteSize), WriteSize, REG_SYNCVALUE1); // write the SYNC, skip some initial bytes
WriteByte( 0x90 | (WriteSize-1), REG_SYNCCONFIG); // write SYNC length [bytes]
WriteByte( 0x90 | (WriteSize-1), REG_SYNCCONFIG); // write SYNC length [bytes] (or 0xB0 for reversed preamble) => p.92
WriteWord( 9-WriteSize, REG_PREAMBLEMSB); } // write preamble length [bytes] (page 71)
// ^ 8 or 9 ?
#endif
@ -366,7 +388,7 @@ class RFM_TRX
uint16_t ReadIrqFlags(void) { return ReadWord(REG_IRQFLAGS1); }
void ClearIrqFlags(void) { WriteWord(RF_IRQ_FifoOverrun | RF_IRQ_Rssi, REG_IRQFLAGS1); }
void ClearIrqFlags(void) { WriteWord(RF_IRQ_FifoOverrun | RF_IRQ_Rssi | RF_IRQ_PreambleDetect | RF_IRQ_SyncAddrMatch, REG_IRQFLAGS1); }
#ifdef WITH_RFM69
void WriteTxPower_W(int8_t TxPower=10) // [dBm] for RFM69W: -18..+13dBm
@ -401,12 +423,12 @@ class RFM_TRX
void WriteTxPowerMin(void) { WriteTxPower_W(-18); } // set minimal Tx power and setup for reception
int Configure(int16_t Channel, const uint8_t *Sync)
int Configure(int16_t Channel, const uint8_t *Sync, bool PW=0)
{ WriteMode(RF_OPMODE_STANDBY); // mode = STDBY
ClearIrqFlags();
WriteByte( 0x02, REG_DATAMODUL); // [0x00] Packet mode, FSK, 0x02: BT=0.5, 0x01: BT=1.0, 0x03: BT=0.3
WriteWord(0x0140, REG_BITRATEMSB); // bit rate = 100kbps
WriteWord(0x0333, REG_FDEVMSB); // FSK deviation = +/-50kHz
WriteWord(PW?0x0341:0x0140, REG_BITRATEMSB); // bit rate = 100kbps
WriteWord(PW?0x013B:0x0333, REG_FDEVMSB); // FSK deviation = +/-50kHz
setChannel(Channel); // operating channel
WriteSYNC(8, 7, Sync); // SYNC pattern (setup for reception)
WriteByte( 0x00, REG_PACKETCONFIG1); // [0x10] Fixed size packet, no DC-free encoding, no CRC, no address filtering
@ -434,42 +456,95 @@ class RFM_TRX
#if defined(WITH_RFM95) || defined(WITH_SX1272)
void WriteTxPower(int8_t TxPower=0)
{ if(TxPower<2) TxPower=2;
else if(TxPower>17) TxPower=17;
{ if(TxPower>17)
{ if(TxPower>20) TxPower=20;
WriteByte(0x87, REG_PADAC);
WriteByte(0xF0 | (TxPower-5), REG_PACONFIG); }
else // if(TxPower>14)
{ if(TxPower<2) TxPower=2;
WriteByte(0x84, REG_PADAC);
WriteByte(0xF0 | (TxPower-2), REG_PACONFIG); }
// else
// { if(TxPower<0) TxPower=0;
// WriteByte(0x84, REG_PADAC);
// WriteByte(0x70 | (TxPower+1), REG_PACONFIG); }
// if(TxPower<2) TxPower=2;
// else if(TxPower>17) TxPower=17;
// if(TxPower<=14)
// { WriteByte(0x70 | TxPower , REG_PACONFIG);
// }
// else
{ WriteByte(0xF0 | (TxPower-2), REG_PACONFIG);
}
// { WriteByte(0xF0 | (TxPower-2), REG_PACONFIG); }
}
void WriteTxPowerMin(void) { WriteTxPower(0); }
int Configure(int16_t Channel, const uint8_t *Sync)
int Configure(int16_t Channel, const uint8_t *Sync, bool PW=0)
{ WriteMode(RF_OPMODE_STANDBY); // mode: STDBY, modulation: FSK, no LoRa
// usleep(1000);
WriteTxPower(0);
// ClearIrqFlags();
WriteWord(0x0140, REG_BITRATEMSB); // bit rate = 100kbps (32MHz/100000)
ClearIrqFlags();
WriteWord(PW?0x0341:0x0140, REG_BITRATEMSB); // bit rate = 100kbps (32MHz/100000) (0x0341 = 38.415kbps)
WriteByte(0x00, REG_BITRATEFRAC); //
// ReadWord(REG_BITRATEMSB);
WriteWord(0x0333, REG_FDEVMSB); // FSK deviation = +/-50kHz [32MHz/(1<<19)]
WriteWord(PW?0x013B:0x0333, REG_FDEVMSB); // FSK deviation = +/-50kHz [32MHz/(1<<19)] (0x013B = 19.226kHz)
// ReadWord(REG_FDEVMSB);
setChannel(Channel); // operating channel
WriteSYNC(8, 7, Sync); // SYNC pattern (setup for reception)
WriteByte( 0x8A, REG_PREAMBLEDETECT); // preamble detect: 1 byte, page 92
WriteByte( 0x85, REG_PREAMBLEDETECT); // preamble detect: 1 byte, page 92 (or 0x85 ?)
WriteByte( 0x00, REG_PACKETCONFIG1); // Fixed size packet, no DC-free encoding, no CRC, no address filtering
WriteByte( 0x40, REG_PACKETCONFIG2); // Packet mode
WriteByte( 51, REG_FIFOTHRESH); // TxStartCondition=FifoNotEmpty, FIFO threshold = 51 bytes
WriteByte( 2*26, REG_PAYLOADLENGTH); // Packet size = 26 bytes Manchester encoded into 52 bytes
WriteByte( 51, REG_FIFOTHRESH); // TxStartCondition=FifoNotEmpty, FIFO threshold = 51 bytes
WriteWord(0x3030, REG_DIOMAPPING1); // DIO signals: DIO0=00, DIO1=11, DIO2=00, DIO3=00, DIO4=00, DIO5=11, => p.64, 99
WriteByte( 0x4A, REG_RXBW); // +/-100kHz Rx bandwidth => p.27+67
WriteByte( 0x02, REG_RXBW); // +/-125kHz Rx (single-side) bandwidth => p.27,67,83,90
WriteByte( 0x02, REG_AFCBW); // +/-125kHz AFC bandwidth
WriteByte( 0x49, REG_PARAMP); // BT=0.5 shaping, 40us ramp up/down
WriteByte( 0x07, REG_RSSICONFIG); // 256 samples for RSSI, p.90
WriteByte( 0x0E, REG_RXCONFIG); // => p.90 (or 0x8E ?)
WriteByte( 0x07, REG_RSSICONFIG); // 256 samples for RSSI, no offset, => p.90,82
WriteByte( 0x20, REG_LNA); // max. LNA gain, => p.89
return 0; }
uint8_t ReadLowBat(void) { return ReadByte(REG_LOWBAT ); }
uint8_t ReadLowBat(void) { return ReadByte(REG_LOWBAT ); }
void PrintReg(void (*CONS_UART_Write)(char))
{ Format_String(CONS_UART_Write, "RFM95 Mode:");
uint8_t RxMode=ReadMode();
Format_Hex(CONS_UART_Write, RxMode);
CONS_UART_Write(' '); CONS_UART_Write('0'+DIO0_isOn());
Format_String(CONS_UART_Write, " IRQ:");
Format_Hex(CONS_UART_Write, ReadWord(REG_IRQFLAGS1));
Format_String(CONS_UART_Write, " Pre:");
Format_Hex(CONS_UART_Write, ReadWord(REG_PREAMBLEMSB));
Format_String(CONS_UART_Write, " SYNC:");
Format_Hex(CONS_UART_Write, ReadByte(REG_SYNCCONFIG));
CONS_UART_Write('/');
for(uint8_t Idx=0; Idx<8; Idx++)
Format_Hex(CONS_UART_Write, ReadByte(REG_SYNCVALUE1+Idx));
Format_String(CONS_UART_Write, " FREQ:");
Format_Hex(CONS_UART_Write, ReadByte(REG_FRFMSB));
Format_Hex(CONS_UART_Write, ReadByte(REG_FRFMID));
Format_Hex(CONS_UART_Write, ReadByte(REG_FRFLSB));
Format_String(CONS_UART_Write, " RATE:");
Format_Hex(CONS_UART_Write, ReadWord(REG_BITRATEMSB));
Format_String(CONS_UART_Write, " FDEV:");
Format_Hex(CONS_UART_Write, ReadWord(REG_FDEVMSB));
Format_String(CONS_UART_Write, " DIO:");
Format_Hex(CONS_UART_Write, ReadWord(REG_DIOMAPPING1));
Format_String(CONS_UART_Write, " CFG:");
Format_Hex(CONS_UART_Write, ReadByte(REG_PREAMBLEDETECT));
Format_Hex(CONS_UART_Write, ReadByte(REG_PACKETCONFIG1));
Format_Hex(CONS_UART_Write, ReadByte(REG_PACKETCONFIG2));
Format_Hex(CONS_UART_Write, ReadByte(REG_FIFOTHRESH));
Format_Hex(CONS_UART_Write, ReadByte(REG_PAYLOADLENGTH));
Format_Hex(CONS_UART_Write, ReadByte(REG_RXBW));
Format_Hex(CONS_UART_Write, ReadByte(REG_RSSICONFIG));
Format_String(CONS_UART_Write, " PA:");
Format_Hex(CONS_UART_Write, ReadByte(REG_PARAMP));
Format_Hex(CONS_UART_Write, ReadByte(REG_PACONFIG));
Format_String(CONS_UART_Write, "\n"); }
#endif

Wyświetl plik

@ -6,6 +6,7 @@
#include "parameters.h"
#include "proc.h"
#include "ctrl.h"
#include "gps.h"
@ -13,6 +14,22 @@
#if defined(WITH_BMP180) || defined(WITH_BMP280) || defined(WITH_MS5607) || defined(WITH_BME280)
#ifdef WITH_BMP180
#include "bmp180.h"
#endif
#ifdef WITH_BMP280
#include "bmp280.h"
#endif
#ifdef WITH_BME280
#include "bme280.h"
#endif
#ifdef WITH_MS5607
#include "ms5607.h"
#endif
#include "atmosphere.h"
#include "slope.h"
#include "lowpass2.h"
@ -191,7 +208,9 @@ static void ProcBaro(void)
PosPtr->Temperature = Baro.Temperature; // and temperature in the GPS record
#ifdef WITH_BME280
if(Baro.hasHumidity())
PosPtr->Humidity = Baro.Humidity;
{ PosPtr->Humidity = Baro.Humidity;
// PosPtr->hasHum=1;
}
#endif
PosPtr->hasBaro=1; } // tick "hasBaro" flag
}
@ -218,7 +237,7 @@ static void ProcBaro(void)
Line[Len++]=','; }
#endif
Len+=NMEA_AppendCheckCRNL(Line, Len);
// if(CONS_UART_Free()>=128)
if(Parameters.Verbose)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len); // send NMEA sentence to the console (UART1)
xSemaphoreGive(CONS_Mutex); }
@ -236,7 +255,25 @@ static void ProcBaro(void)
Len+=Format_String(Line+Len, "m,"); // normally f for feet, but metres and m works with XcSoar
Len+=Format_String(Line+Len, "3"); // 1 no fix, 2 - 2D, 3 - 3D; assume 3D for now
Len+=NMEA_AppendCheckCRNL(Line, Len);
// if(CONS_UART_Free()>=128)
if(Parameters.Verbose)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len); // send NMEA sentence to the console (UART1)
xSemaphoreGive(CONS_Mutex); }
Len=0;
Len+=Format_String(Line+Len, "$LK8EX1,");
Len+=Format_UnsDec(Line+Len, (uint32_t)(Pressure+2)>>2); // [Pa] pressure
Line[Len++]=',';
Len+=Format_SignDec(Line+Len, (StdAltitude+5)/10); // [m] standard altitude (calc. from pressure)
Line[Len++]=',';
Len+=Format_SignDec(Line+Len, ClimbRate); // [cm/s] climb rate
Line[Len++]=',';
Len+=Format_SignDec(Line+Len, (Baro.Temperature+5)/10); // [degC] temperature
Line[Len++]=',';
Len+=Format_UnsDec(Line+Len, (BatteryVoltage+128)>>8, 4, 3); // [mV] Battery voltage
// Len+=Format_String(Line+Len, "999"); // [%] battery level
Len+=NMEA_AppendCheckCRNL(Line, Len);
if(Parameters.Verbose)
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, Line, 0, Len); // send NMEA sentence to the console (UART1)
xSemaphoreGive(CONS_Mutex); }
@ -299,10 +336,9 @@ void vTaskSENS(void* pvParameters)
while(1)
{
if(Button_SleepRequest)
{ vTaskDelay(1000); }
#if defined(WITH_BMP180) || defined(WITH_BMP280) || defined(WITH_MS5607) || defined(WITH_BME280)
ProcBaro();
if(PowerMode) ProcBaro();
else vTaskDelay(100);
#else
vTaskDelay(1000);
#endif

11
main/sound.h 100644
Wyświetl plik

@ -0,0 +1,11 @@
#include "hal.h"
void SoundMsg(char ch);
int SoundMsg(const char *Msg);
#ifdef __cplusplus
extern "C"
#endif
void vTaskSOUND(void* pvParameters);

731
main/st7789.cpp 100644
Wyświetl plik

@ -0,0 +1,731 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/spi_master.h" // for SPI
#include "esp32/rom/tjpgd.h" // for JPEG
#include "driver/ledc.h" // for PWM backlight control
#include "st7789.h"
#include "hal.h"
#include "format.h"
// #define LCD_FLIP // flip the LCD: rotate by 180deg, works only for 240x240 displays
// =============================================================================
// pins specific for this LCD
gpio_num_t LCD_PIN_CS = GPIO_NUM_NC; // connected to ground: constantly active
gpio_num_t LCD_PIN_DC = GPIO_NUM_0; // D/C: Command = 0, Data = 1
gpio_num_t LCD_PIN_RST = GPIO_NUM_NC; // actually connected to system RESET
gpio_num_t LCD_PIN_BCKL = GPIO_NUM_4; // back-light: HIGH active
#ifdef LCD_FLIP
const int LCD_XOFS = 80; // [pixels]
#endif
int LCD_TYPE = 0; // 0 = ST7789, 1 = ILI9341
int LCD_WIDTH = 240; // [pixels]
int LCD_HEIGHT = 240; // [pixels]
static spi_device_handle_t LCD_SPI;
// =============================================================================
/*
typedef struct
{ uint8_t cmd; // command
uint8_t databytes; // Number of parameter bytes; bit 7 = delay after set; 0xFF = end of cmds.
uint8_t data[14]; // up to 16 parameter bytes per command
} lcd_init_cmd_t;
static const lcd_init_cmd_t ST7789_init_cmds[] = {
{ 0x01, 0xC0, {0} }, // LCD software reset, wait 120ms
{ 0x36, 1, {0b01100000} }, // Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0
{ 0x3A, 1, {0x55} }, // Interface Pixel Format, 16bits/pixel for RGB/MCU interface
{ 0xB2, 5, {0x0c, 0x0c, 0x00, 0x33, 0x33} }, // Porch Setting
{ 0xB7, 1, {0x45} }, // Gate Control, Vgh=13.65V, Vgl=-10.43V
{ 0xBB, 1, {0x2B} }, // VCOM Setting, VCOM=1.175V
{ 0xC0, 1, {0x2C} }, // LCM Control, XOR: BGR, MX, MH
{ 0xC2, 2, {0x01, 0xff} }, // VDV and VRH Command Enable, enable=1
{ 0xC3, 1, {0x11} }, // VRH Set, Vap=4.4+...
{ 0xC4, 1, {0x20} }, // VDV Set, VDV=0
{ 0xC6, 1, {0x0f} }, // Frame Rate Control, 60Hz, inversion=0
{ 0xD0, 2, {0xA4, 0xA1} }, // Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V
// Positive Voltage Gamma Control
{ 0xE0, 14, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19} },
// Negative Voltage Gamma Control
{ 0xE1, 14, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19} },
{ 0x11, 0xC0, {0} }, // Sleep Out, wait 120ms
{ 0x21, 0, {0} }, // Invert ON
{ 0x29, 0x10, {0} }, // Display ON, wait 10ms ?
{ 0x00, 0xFF, {0} }
};
*/
static const uint8_t ST7789_init[] = {
// 0x00, 0x10,
0x01, 0xE0, // LCD software reset, wait 140ms
0x01, 0xE0, // 2nd LCD software reset, wait 140ms
#ifdef LCD_FLIP
0x36, 0x01, 0b10100000, // flipped by 180deg but needs horizontal offset
#else
0x36, 0x01, 0b01100000, // Memory Data Access Control, MX MY MV ML RGB MH 1 0
#endif
0x3A, 0x01, 0x55, // Interface Pixel Format, 16bits/pixel for RGB/MCU interface
0xB2, 0x05, 0x0c, 0x0c, 0x00, 0x33, 0x33, // Porch Setting
0xB7, 0x01, 0x45, // Gate Control, Vgh=13.65V, Vgl=-10.43V
0xBB, 0x01, 0x2B, // VCOM Setting, VCOM=1.175V
0xC0, 0x01, 0x2C, // LCM Control, XOR: BGR, MX, MH
0xC2, 0x02, 0x01,0xff, // VDV and VRH Command Enable, enable=1
0xC3, 0x01, 0x11, // VRH Set, Vap=4.4+...
0xC4, 0x01, 0x20, // VDV Set, VDV=0
0xC6, 0x01, 0x0f, // Frame Rate Control, 60Hz, inversion=0
0xD0, 0x02, 0xA4, 0xA1, // Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V
0xE0, 0x0E, 0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19, // Positive Voltage Gamma Control
0xE1, 0x0E, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19, // Negative Voltage Gamma Control
0x11, 0xE0, // Sleep Out, wait 140ms
0x21, 0x00, // Invert ON
0x29, 0x20, // Display ON, wait 20ms ?
0x00, 0xFF // terminator
};
static const uint8_t ILI9341_init[] = {
0x01, 0xD0, // Software RESET
0xCF, 3, 0x00, 0x83, 0x30 , // Power contorl B, power control = 0, DC_ENA = 1
0xED, 4, 0x64, 0x03, 0x12, 0x81 , // Power on sequence control, cp1 keeps 1 frame, 1st frame enable, vcl = 0, ddvdh=3, vgh=1, vgl=2, DDVDH_ENH=1
0xE8, 3, 0x85, 0x01, 0x79 , // Driver timing control A, non-overlap=default +1, EQ=default - 1, CR=default, pre-charge=default - 1
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02 , // Power control A, Vcore=1.6V, DDVDH=5.6V
0xF7, 1, 0x20 , // Pump ratio control, DDVDH=2xVCl
0xEA, 2, 0x00, 0x00 , // Driver timing control, all=0 unit
// 0xC0, 1, 0x23,
// 0xC1, 1, 0x10,
// 0xC5, 2, 0x3E, 0x28,
// 0xC7, 1, 0x86,
0xC0, 1, 0x26, // Power control 1, GVDD=4.75V
0xC1, 1, 0x11, // Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3
0xC5, 2, 0x35, 0x3E , // VCOM control 1, VCOMH=4.025V, VCOML=-0.950V
0xC7, 1, 0xBE, // VCOM control 2, VCOMH=VMH-2, VCOML=VML-2
0x36, 1, 0b00001000 , // Memory Data Access Control, MX=MV=0, MY=ML=MH=0, RGB=1
0x3A, 1, 0x55 , // Pixel format, 16bits/pixel for RGB/MCU interface
0xB1, 2, 0x00, 0x1B , // Frame rate control, f=fosc, 70Hz fps
// 0xF2, 1, 0x08 , // Enable 3G, disabled
0xF2, 1, 0x00 , // 3Gamma function disable
0x26, 1, 0x01 , // Gamma set, curve 1
// 0x26, 1, 0x02 , // Gamma set, curve 1
// 0xE0, 15, 0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00 , // Positive gamma correction
// 0xE1, 15, 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F , // Negative gamma correction
0xE0, 15, 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00,
0xE1, 15, 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f,
// 0xE0, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
// 0xE1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
0x2A, 4, 0x00, 0x00, 0x00, 0xEF , // Column address set, SC=0, EC=0xEF
0x2B, 4, 0x00, 0x00, 0x01, 0x3f , // Page address set, SP=0, EP=0x013F
0x2C, 0, // Memory write
0xB7, 1, 0x07 , // Entry mode set, Low vol detect disabled, normal display
0xB6, 4, 0x0A, 0x82, 0x27, 0x00 , // Display function control
0x11, 0xC0, // Sleep out
0x29, 0x20, // Display on
0x00, 0xFF
};
/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
* until the transfer is complete.
*
* Since command transactions are usually small, they are handled in polling
* mode for higher speed. The overhead of interrupt transactions is more than
* just waiting for the transaction to complete.
*/
static esp_err_t lcd_cmd(const uint8_t cmd)
{ spi_transaction_t t;
memset(&t, 0, sizeof(t)); // Zero out the transaction
t.length=8; // Command is 8 bits
t.tx_buffer=&cmd; // The data is the cmd itself
t.user=(void*)0; // D/C needs to be set to 0
return spi_device_polling_transmit(LCD_SPI, &t); } // Transmit!
/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
* transfer is complete.
*
* Since data transactions are usually small, they are handled in polling
* mode for higher speed. The overhead of interrupt transactions is more than
* just waiting for the transaction to complete.
*/
static DRAM_ATTR uint8_t ram_buff[16];
static esp_err_t lcd_data(const uint8_t *data, int len)
{ // uint8_t ram_buff[len];
memcpy(ram_buff, data, len); // copy to RAM buffer, otherwise DMA would not work
spi_transaction_t t;
if (len==0) return ESP_OK; // no need to send anything
memset(&t, 0, sizeof(t)); // Zero out the transaction
t.length=len*8; // Len is in bytes, transaction length is in bits.
t.tx_buffer=ram_buff; // Data
t.user=(void*)1; // D/C needs to be set to 1
return spi_device_polling_transmit(LCD_SPI, &t); } // Transmit!
// This function is called (in irq context!) just before a transmission starts. It will
// set the D/C line to the value indicated in the user field.
static void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{ int DC=(int)t->user;
gpio_set_level(LCD_PIN_DC, DC);
// if(LCD_PIN_CS>GPIO_NUM_NC) gpio_set_level(LCD_PIN_CS, 0);
}
// static void lcd_spi_post_transfer_callback(spi_transaction_t *t)
// { if(LCD_PIN_CS>GPIO_NUM_NC) gpio_set_level(LCD_PIN_CS, 1); }
static ledc_timer_config_t LEDC_Timer =
{ speed_mode : LEDC_HIGH_SPEED_MODE, // timer mode
duty_resolution : LEDC_TIMER_8_BIT, // resolution of PWM duty: 0..255
timer_num : LEDC_TIMER_0, // timer index
freq_hz : 1000 // frequency of PWM signal
} ;
static ledc_channel_config_t LEDC_Channel =
{ gpio_num : LCD_PIN_BCKL,
speed_mode : LEDC_HIGH_SPEED_MODE,
channel : LEDC_CHANNEL_1,
intr_type : LEDC_INTR_DISABLE,
timer_sel : LEDC_TIMER_0,
duty : 0,
hpoint : 0
} ;
void LCD_SetBacklightLevel(uint8_t Level)
{ if(Level>8) Level=0;
uint8_t Duty=1; Duty<<=Level; Duty--;
if(LCD_PIN_BCKL>GPIO_NUM_NC)
{ ledc_set_duty(LEDC_Channel.speed_mode, LEDC_Channel.channel, Duty);
ledc_update_duty(LEDC_Channel.speed_mode, LEDC_Channel.channel); }
}
// Initialize the display
static void lcd_gpio_init(void)
{
if(LCD_PIN_BCKL>GPIO_NUM_NC)
{ LEDC_Channel.gpio_num = LCD_PIN_BCKL;
ledc_timer_config(&LEDC_Timer); // Set configuration of timer for high speed channels
ledc_channel_config(&LEDC_Channel);
LCD_SetBacklightOFF(); }
gpio_set_direction(LCD_PIN_DC, GPIO_MODE_OUTPUT);
if(LCD_PIN_RST>GPIO_NUM_NC)
{ gpio_set_direction(LCD_PIN_RST, GPIO_MODE_OUTPUT); }
// if(LCD_PIN_CS>GPIO_NUM_NC)
// { gpio_set_direction(LCD_PIN_CS, GPIO_MODE_OUTPUT);
// gpio_set_level(LCD_PIN_CS, 1); }
}
static void lcd_start(const uint8_t *cmd) // reset controller and send initial config commands
{
if(LCD_PIN_RST>GPIO_NUM_NC)
{ gpio_set_level(LCD_PIN_RST, 0);
vTaskDelay(100);
gpio_set_level(LCD_PIN_RST, 1);
vTaskDelay(140);
cmd+=2; } // skip Software Reset
else
{ } // include Software Reset
for( ; ; )
{ uint8_t Cmd = *cmd++;
uint8_t Len = *cmd++; if(Len==0xFF) break;
uint8_t Wait = Len>>4; Len&=0x0F;
lcd_cmd(Cmd);
lcd_data(cmd, Len);
if(Wait) vTaskDelay(10*Wait);
// else vTaskDelay(1);
cmd+=Len; }
}
// =============================================================================
// const int LCD_BUFF_SIZE = 12*320;
DRAM_ATTR static uint16_t lcd_buffer[LCD_BUFF_SIZE];
static int lcd_buffer_filled = 0; // buffer is prefilled up to this size with a fixed RGB565
static void lcd_buffer_fill(int size, uint16_t RGB565) // fill the buffer with given RGB565 up to the desired size
{ if(lcd_buffer[0]!=RGB565) lcd_buffer_filled=0; // if filled with a different RGB565 then assume not filled
if(lcd_buffer_filled>=size) return; // if filled up to the desired size then we are done
for(int x=lcd_buffer_filled; x<size; x++) // fill up to the desired size
{ lcd_buffer[x]=RGB565; }
lcd_buffer_filled=size; } // mark as filled till the requested size
static spi_transaction_t lcd_trans[6]; // six SPI transactions to transfer pixel data
static bool lcd_transaction_active = 0; // initially not active
static void lcd_trans_init(void) // initial setup of the transactions
{ for (int x=0; x<6; x++)
{ memset(&lcd_trans[x], 0, sizeof(spi_transaction_t));
if ((x&1)==0) // Even transfers are commands
{ lcd_trans[x].length = 8; // 8-bit = single byte command
lcd_trans[x].user=(void*)0; } // D/C = LOW for command
else // Odd transfers are data
{ lcd_trans[x].length = 8*4; // 4 byte = arguments (except for the last transaction)
lcd_trans[x].user=(void*)1; } // D/C = HIGH for data
lcd_trans[x].flags=SPI_TRANS_USE_TXDATA; }
lcd_transaction_active=0; }
static void lcd_trans_wait(void) // wait for the six SPI transaction to complete
{ if(!lcd_transaction_active) return;
spi_transaction_t *rtrans;
for (int x=0; x<6; x++)
{ esp_err_t ret=spi_device_get_trans_result(LCD_SPI, &rtrans, portMAX_DELAY); }
lcd_transaction_active=0; }
static void lcd_trans_start(void) // start the six SPI transactions to write data to given X/Y
{ if(lcd_transaction_active) lcd_trans_wait(); // if previous transaction set not finished, then wait for them
for (int x=0; x<6; x++) // start the six SPI transactions
{ esp_err_t ret=spi_device_queue_trans(LCD_SPI, &lcd_trans[x], portMAX_DELAY); }
lcd_transaction_active=1; } // mark the transactions as beig active
// positions here must fit into the screen, there is no check and no correction if they don't fit
static void lcd_trans_setup(int xpos, int ypos, int xsize, int ysize, uint16_t *data)
{
#ifdef LCD_FLIP
xpos+=LCD_XOFS;
#endif
if(lcd_transaction_active) lcd_trans_wait(); // if previous transaction set not finished, then wait for them
lcd_trans[0].tx_data[0] = 0x2A; // Column Address Set
lcd_trans[1].tx_data[0] = xpos>>8; // Start Col MSB
lcd_trans[1].tx_data[1] = xpos&0xFF; // Start Col LSB
xpos += xsize-1;
lcd_trans[1].tx_data[2] = xpos>>8; // End Col MSB
lcd_trans[1].tx_data[3] = xpos&0xFF; // End Col LSB
lcd_trans[2].tx_data[0] = 0x2B; // Page address set
lcd_trans[3].tx_data[0] = ypos>>8; // Start page MSB
lcd_trans[3].tx_data[1] = ypos&0xFF; // start page LSB
ypos += ysize-1;
lcd_trans[3].tx_data[2] = ypos>>8; // end page MSB
lcd_trans[3].tx_data[3] = ypos&0xFF; // end page LSB
lcd_trans[4].tx_data[0] = 0x2C; // memory write
lcd_trans[5].tx_buffer = data; // finally send the line data
lcd_trans[5].length = xsize*ysize*2*8; // Data length, in bits
lcd_trans[5].rxlength = 0; // need to set this, otherwise a previous value is taken and an error occurs
lcd_trans[5].flags=0; } // undo SPI_TRANS_USE_TXDATA flag
// ================================================================================
void LCD_DrawBox(int xpos, int ypos, int xsize, int ysize, uint16_t RGB565)
{ if(xsize==0) return;
if(ysize==0) return;
if(xpos>=LCD_WIDTH) return;
if(ypos>=LCD_HEIGHT) return;
if((xpos+xsize)<=0) return;
if((ypos+ysize)<=0) return;
if(xpos<0) { xsize+=xpos; xpos=0; }
if(ypos<0) { ysize+=ypos; ypos=0; }
if((xpos+xsize)>LCD_WIDTH) { xsize=LCD_WIDTH-xpos; }
if((ypos+ysize)>LCD_HEIGHT) { ysize=LCD_HEIGHT-ypos; }
if(lcd_transaction_active) lcd_trans_wait();
int lines_per_batch = LCD_BUFF_SIZE/xsize; // number of lines we can do per batch given the buffer size
if(lines_per_batch>ysize) lines_per_batch=ysize; // if bigger than we need to do then cut it down
int pixels_per_batch = lines_per_batch*xsize; // number of pixels per batch
lcd_buffer_fill(pixels_per_batch, RGB565); // fill the buffer with uniform color, if not filled already
for( ; ysize; ) // send given number of lines: do it in batches for speed
{ if(lines_per_batch>ysize) lines_per_batch=ysize;
lcd_trans_setup(xpos, ypos, xsize, lines_per_batch, lcd_buffer);
lcd_trans_start();
ypos+=lines_per_batch; ysize-=lines_per_batch; }
}
// void LCD_DrawHorLine(int xpos, int ypos, int xsize, uint16_t RGB565)
// { LCD_DrawBox(xpos, ypos, xsize, 1, RGB565); }
// void LCD_DrawVerLine(int xpos, int ypos, int ysize, uint16_t RGB565)
// { LCD_DrawBox(xpos, ypos, 1, ysize, RGB565); }
// void LCD_DrawPixel(int xpos, int ypos, uint16_t RGB565)
// { LCD_DrawBox(xpos, ypos, 1, 1, RGB565); }
// ================================================================================
static void swap(int &a, int &b) { int t = a; a = b; b = t; }
void LCD_DrawLine(int x0, int y0, int x1, int y1, uint16_t RGB565)
{
if (x0 == x1)
{ if (y0 <= y1) LCD_DrawVerLine(x0, y0, y1-y0+1, RGB565);
else LCD_DrawVerLine(x0, y1, y0-y1+1, RGB565);
return; }
if (y0 == y1)
{ if (x0 <= x1) LCD_DrawHorLine(x0, y0, x1-x0+1, RGB565);
else LCD_DrawHorLine(x1, y0, x0-x1+1, RGB565);
return; }
int steep = 0;
if (abs(y1-y0) > abs(x1-x0)) steep = 1;
if (steep) { swap(x0, y0); swap(x1, y1); }
if (x0>x1) { swap(x0, x1); swap(y0, y1); }
int dx = x1 - x0;
int dy = abs(y1 - y0);
int err = dx >> 1;
int ystep = -1;
int xs = x0;
int dlen = 0;
if (y0<y1) ystep = 1;
if (steep)
{ for ( ; x0<=x1; x0++)
{ dlen++;
err-=dy;
if(err<0)
{ err+=dx;
if (dlen==1) LCD_DrawPixel(y0, xs, RGB565);
else LCD_DrawVerLine(y0, xs, dlen, RGB565);
dlen=0; y0+=ystep; xs=x0+1;
}
}
if (dlen) LCD_DrawVerLine(y0, xs, dlen, RGB565);
}
else
{ for ( ; x0<=x1; x0++)
{ dlen++;
err-=dy;
if(err<0)
{ err+=dx;
if (dlen==1) LCD_DrawPixel(xs, y0, RGB565);
else LCD_DrawHorLine(xs, y0, dlen, RGB565);
dlen=0; y0+=ystep; xs=x0+1;
}
}
if (dlen) LCD_DrawHorLine(xs, y0, dlen, RGB565);
}
}
void LCD_DrawCircle(int x, int y, int radius, uint16_t RGB565)
{ int f = 1 - radius;
int ddF_x = 1;
int ddF_y = -2 * radius;
int x1 = 0;
int y1 = radius;
LCD_DrawPixel(x, y + radius, RGB565);
LCD_DrawPixel(x, y - radius, RGB565);
LCD_DrawPixel(x + radius, y, RGB565);
LCD_DrawPixel(x - radius, y, RGB565);
while(x1<y1)
{ if (f >= 0)
{ y1--;
ddF_y += 2;
f += ddF_y; }
x1++;
ddF_x += 2;
f += ddF_x;
LCD_DrawPixel(x + x1, y + y1, RGB565);
LCD_DrawPixel(x - x1, y + y1, RGB565);
LCD_DrawPixel(x + x1, y - y1, RGB565);
LCD_DrawPixel(x - x1, y - y1, RGB565);
LCD_DrawPixel(x + y1, y + x1, RGB565);
LCD_DrawPixel(x - y1, y + x1, RGB565);
LCD_DrawPixel(x + y1, y - x1, RGB565);
LCD_DrawPixel(x - y1, y - x1, RGB565);
}
}
// ================================================================================
// Proportional fonts for Arduino
class propChar
{ public:
uint8_t charCode; // for example 32 for ' '
uint8_t yOffset; // empty horizontal space on top
uint8_t width; // core width
uint8_t height; // core height
uint8_t xOffset; // empty vertical space on the left
uint8_t xDelta; // shift X-pos for the next character
uint8_t Data[]; //
public:
uint16_t CoreBits(void) const { return (uint16_t)width*height; }
uint8_t DataBytes(void) const { return (CoreBits()+7)/8; }
} ;
int LCD_FontHeight(const uint8_t *propFont) { return propFont[1]; } // height of the given font is in the 2nd byte of the font header
static const propChar *FindChar(char Char, const uint8_t *propFont) // find given character
{ propFont+=4; // skip the font header: 4 bytes
for( ; ; ) // loop over the characters
{ const propChar *Geom = (const propChar *)propFont; // pointer to the next character geometry
if(Geom->charCode==0xFF) break; // 0xFF is terminator: no more characters
if(Geom->charCode==(uint8_t)Char) return Geom; // if code matches: return the pointer
propFont += 6 + Geom->DataBytes(); } // if not: skip this character and go to the next
return 0; } // if went through all and not found: return NULL
// slow, because goes pixel-by-pixel, but simple to code, no big deal with out-of-screen positions
int LCD_DrawTranspChar(char Char, int xpos, int ypos, uint16_t RGB565, const uint8_t *propFont)
{ const propChar *Geom = FindChar(Char, propFont); if(Geom==0) return 0;
xpos += Geom->xOffset;
ypos += Geom->yOffset;
const uint8_t *Data = Geom->Data;
uint8_t Byte = 0x00;
uint8_t Mask = 0x00;
for(int dy=0; dy < Geom->height; dy++)
{ for(int dx=0; dx < Geom->width; dx++)
{ if(Mask==0) { Byte = *Data++; Mask=0x80; }
if(Byte&Mask) LCD_DrawPixel(xpos+dx, ypos+dy, RGB565);
Mask>>=1; }
}
return Geom->xDelta; } // return by how much move the cursor to draw the next character
int LCD_DrawTranspString(const char *String, int xpos, int ypos, uint16_t RGB565, const uint8_t *propFont)
{ int Len = 0;
for( ; ; )
{ char Char = *String++; if(Char==0) break;
Len += LCD_DrawTranspChar(Char, xpos+Len, ypos, RGB565, propFont); }
return Len; }
int LCD_DrawChar(char Char, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont)
{ const propChar *Geom = FindChar(Char, propFont); if(Geom==0) return 0;
int Width = Geom->xDelta; // the box for the character to be printed
int Height = LCD_FontHeight(propFont);
int CropLeft = 0; if(xpos<0) { CropLeft=(-xpos); xpos=0; Width -=CropLeft; }
int CropTop = 0; if(ypos<0) { CropTop =(-ypos); ypos=0; Height-=CropTop ; }
int CropRight = 0; if((xpos+Width)>LCD_WIDTH) { CropRight=xpos-LCD_WIDTH; Width-=CropRight; }
int CropBottom = 0; if((ypos+Height)>LCD_HEIGHT) { CropBottom=ypos-LCD_HEIGHT; Height-=CropBottom; }
if(Width<=0 || Height<=0) return Geom->xDelta;
int Pixels = Width*Height;
lcd_buffer_filled=0;
for(int Pix=0; Pix<Pixels; Pix++) lcd_buffer[Pix]=Back;
const uint8_t *Data = Geom->Data;
uint8_t Byte = 0x00;
uint8_t Mask = 0x00;
for(int dy=0; dy < Geom->height; dy++)
{ for(int dx=0; dx < Geom->width; dx++)
{ if(Mask==0) { Byte = *Data++; Mask=0x80; }
if(Byte&Mask)
{ int X = Geom->xOffset+dx-CropLeft;
int Y = Geom->yOffset+dy-CropTop;
if( X>=0 && X<Width && Y>=0 && Y<Height)
{ lcd_buffer[Y*Width+X] = Fore; }
}
Mask>>=1; }
}
lcd_trans_setup(xpos, ypos, Width, Height, lcd_buffer);
lcd_trans_start();
lcd_trans_wait();
return Width; }
int LCD_CharWidth(char Char, const uint8_t *propFont)
{ const propChar *Geom = FindChar(Char, propFont); if(Geom==0) return 0;
return Geom->xDelta; }
int LCD_StringWidth(const char *String, const uint8_t *propFont)
{ int Len = 0;
for( ; ; )
{ char Char = *String++; if(Char==0) break;
Len += LCD_CharWidth(Char, propFont); }
return Len; }
// draw opaque characters of a string
int LCD_DrawString(const char *String, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont)
{ int Len = 0;
for( ; ; )
{ char Char = *String++; if(Char==0) break;
Len += LCD_DrawChar(Char, xpos+Len, ypos, Fore, Back, propFont); }
return Len; }
// draw only those characters of a string which have changed to minimize the LCD transfer thus maximize the speed
int LCD_UpdateString(const char *String, const char *RefString, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont)
{ int Len = 0;
for( ; ; )
{ char Char = *String; if(Char) String++;
char RefChar = *RefString; if(RefChar) RefString++;
if(Char==0 && RefChar==0) break;
if(Char==RefChar)
{ Len += LCD_CharWidth(Char, propFont); }
else
{ if(Char) { Len += LCD_DrawChar(Char, xpos+Len, ypos, Fore, Back, propFont); }
else { int Width = LCD_CharWidth(RefChar, propFont);
LCD_DrawBox(xpos+Len, ypos, Width, LCD_FontHeight(propFont), Back);
Len+=LCD_CharWidth(RefChar, propFont); }
}
}
return Len; }
// =============================================================================
typedef struct
{ int16_t xpos; // top left point X position on the LCD
int16_t ypos; // top left point Y position on the LCD
const uint8_t * Input; // memory buffer containing the image
uint32_t InpSize; // size of the input image
uint32_t InpPtr; // input image current position
} JPGIODEV;
static UINT tjd_buf_input (
JDEC* jd, // Decompression object
BYTE* buff, // Pointer to the read buffer (NULL:skip)
UINT nd ) // Number of bytes to read/skip from input stream
{ JPGIODEV *dev = (JPGIODEV*)jd->device;
// CONS_UART_Write('i');
// Format_Hex(CONS_UART_Write, (uint32_t)(dev->Input));
// CONS_UART_Write(':');
// Format_Hex(CONS_UART_Write, (uint32_t)(buff));
// CONS_UART_Write(' ');
// return 0;
if(!dev->Input) return 0; // if Input pointer is NULL (can it be ?)
if(dev->InpPtr >= dev->InpSize) return 0; // if past the InpSize: end of stream
if( (dev->InpPtr+nd) > dev->InpSize) nd = dev->InpSize-dev->InpPtr;
if(buff) // if not NULL pointer then copy, otherwise just skip
{ memcpy(buff, dev->Input + dev->InpPtr, nd); } // copy the nd bytes of the input stream
dev->InpPtr += nd; // incremeant the input pointer
return nd; } // return the number of bytes copied or skipped
static UINT tjd_output (
JDEC* jd, // Decompression object of current session
void* bitmap, // Bitmap data to be output
JRECT* rect // Rectangular region to output
)
{ // Device identifier for the session (5th argument of jd_prepare function)
JPGIODEV *dev = (JPGIODEV*)jd->device;
// CONS_UART_Write('o');
int Lx0 = rect->left + dev->xpos; // coordinates on the screen
int Ly0 = rect->top + dev->ypos;
int Lx1 = rect->right+1 + dev->xpos; //
int Ly1 = rect->bottom+1 + dev->ypos;
int Jxs = Lx1-Lx0; // X-size of the JPEG rectagle
int Jys = Ly1-Ly0; // Y-size of the JPEG rectagle
int CropLeft = 0; if(Lx0<0) { CropLeft = (-Lx0); Lx0=0; } // how much crop the JPEG rectangle on the left
int CropTop = 0; if(Ly0<0) { CropTop = (-Ly0); Ly0=0; } // how much crop the JPEG rectangle on the right
int CropRight = 0; if(Lx1>LCD_WIDTH) { CropRight = LCD_WIDTH-Lx1; Lx1=LCD_WIDTH; } //
int CropBottom = 0; if(Ly1>LCD_HEIGHT) { CropBottom = LCD_HEIGHT-Ly1; Ly1=LCD_HEIGHT; } //
int Llines = Ly1-Ly0; // number of lines going to LCD
int Lrows = Lx1-Lx0; // number of pixels per line going to LCD
if(Llines<=0) return 1;
if(Lrows<=0) return 1;
uint16_t *Dst = lcd_buffer; // buffer to form RGB565 for transfer
uint8_t *Src = (uint8_t *)bitmap; // RGB from JPEG decoder
if(CropTop) Src += CropTop*3*Jxs; // Advance by the nuber of lines to skip
for(int Line=0; Line<Llines; Line++) // Loop over line to display
{ uint8_t *Ptr = Src + 3*CropLeft; // advance by the pixels to skip on the left
for(int Row=0; Row<Lrows; Row++) // loop over pixel in this line
{ Dst[Row] = RGB565(Ptr); Ptr+=3; } // convert to RGB565 and store in the buffer
Src+=3*Jxs; Dst+=Lrows; } // advance by the number of pixels
lcd_buffer_filled=0;
lcd_trans_setup(Lx0, Ly0, Llines, Lrows, lcd_buffer);
lcd_trans_start();
return 1; } // continue with decompression
void LCD_DrawJPEG(const uint8_t *JPEG, int JPEGsize, int xpos, int ypos, int Scale)
{ if(Scale<0) Scale=0;
else if(Scale>3) Scale=3;
JPGIODEV dev; // input/output device
JDEC decoder; // Decompression object (70 bytes)
// CONS_UART_Write('J');
const UINT WorkSize = 3800;
char *Work = (char *)malloc(WorkSize); // JPEG decode work space
if(Work==0) return;
// CONS_UART_Write('P');
dev.Input = JPEG;
dev.InpSize = JPEGsize;
dev.InpPtr = 0;
dev.xpos = xpos;
dev.ypos = ypos;
if(jd_prepare(&decoder, tjd_buf_input, (void *)Work, WorkSize, &dev)!=JDR_OK) { free(Work); return ; }
// CONS_UART_Write('E');
JRESULT rc = jd_decomp(&decoder, tjd_output, Scale);
// CONS_UART_Write('G');
free(Work); }
// ================================================================================
void LCD_Start(void)
{ // if(LCD_TYPE==1) lcd_start(ILI9341_init); // reset, send initial commands
// else lcd_start(ST7789_init); // reset, send initial commands
if(LCD_TYPE==1) lcd_start(ILI9341_init); // reset, send initial commands
else lcd_start(ST7789_init); // reset, send initial commands
lcd_trans_init(); // initialize SPI transactions
LCD_DrawBox(0, 0, LCD_WIDTH, LCD_HEIGHT, RGB565_WHITE); // screen all-white
}
void LCD_Init(spi_host_device_t LCD_SPI_HOST, uint8_t LCD_SPI_MODE, int LCD_SPI_SPEED) // Initialize SPI and LCD
{
spi_device_interface_config_t DevConfig = // specific device on the SPI bus
{
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.mode = LCD_SPI_MODE, // SPI mode 3, but cna be 0 for other displays
.duty_cycle_pos = 0,
.cs_ena_pretrans = 0,
.cs_ena_posttrans = 0,
.clock_speed_hz = LCD_SPI_SPEED,
.input_delay_ns = 0, // seems to help with the reliability
.spics_io_num = LCD_PIN_CS, // CS pin
.flags = 0,
.queue_size = 8, // We want to be able to queue 7 transactions at a time
.pre_cb = lcd_spi_pre_transfer_callback, // Specify pre-transfer callback to handle D/C line
.post_cb = 0 // lcd_spi_post_transfer_callback
};
esp_err_t ret = spi_bus_add_device(LCD_SPI_HOST, &DevConfig, &LCD_SPI); // Attach the LCD to the SPI bus
lcd_gpio_init(); // setup GPIO
// LCD_Start();
LCD_SetBacklightLevel(8); // max. backlight to signal power-on
// LCD_DrawJPEG(OGN_logo_jpg, OGN_logo_size, 0, 0); // draw logo
// // LCD_DrawJPEG(Club_logo_jpg, Club_logo_size, 0, 0);
/*
LCD_DrawBox(LCD_SPI, 0, 0, LCD_WIDTH, LCD_HEIGHT, RGB565_RED);
LCD_DrawBox(LCD_SPI, 32, 32, LCD_WIDTH-64, LCD_HEIGHT-64, RGB565_BLUE);
LCD_DrawBox(LCD_SPI, 64, 64, LCD_WIDTH-128, LCD_HEIGHT-128, RGB565_GREEN);
*/
/*
LCD_DrawLine( 0, 240, 240, 0, RGB565_MAGENTA);
LCD_DrawLine( 0, 180, 240, 60, RGB565_MAGENTA);
LCD_DrawLine( 0, 120, 240, 120, RGB565_MAGENTA);
LCD_DrawLine( 0, 60, 240, 180, RGB565_MAGENTA);
LCD_DrawLine( 0, 0, 240, 240, RGB565_MAGENTA);
LCD_DrawLine( 180, 0, 60, 240, RGB565_MAGENTA);
LCD_DrawLine( 120, 0, 120, 240, RGB565_MAGENTA);
LCD_DrawLine( 60, 0, 180, 240, RGB565_MAGENTA);
LCD_DrawCircle(120, 120, 25, RGB565_MAGENTA);
LCD_DrawCircle(120, 120, 50, RGB565_MAGENTA);
LCD_DrawCircle(120, 120, 75, RGB565_MAGENTA);
LCD_DrawCircle(120, 120, 100, RGB565_MAGENTA);
*/
// LCD_DrawString("OGN-Tracker", 40, 210, RGB565_BLUE, RGB565_PINK, tft_Dejavu24);
// LCD_DrawTranspString("OGN-Tracker", 40, 210, RGB565_BLUE, tft_Dejavu24);
// LCD_DrawTranspString("with T-Beam", 4, 20, RGB565_BLACK, tft_minya24);
// LCD_DrawString("Test Test Test Test Test Test Test Test Test", -10, -5, RGB565_BLUE, RGB565_PINK, tft_Dejavu24);
// LCD_clearDisplay(RGB565_GREEN);
}
void LCD_ClearDisplay(uint16_t RGB565)
{ LCD_DrawBox(0, 0, LCD_WIDTH, LCD_HEIGHT, RGB565); }
uint16_t RGB565(uint8_t Red, uint8_t Green, uint8_t Blue) // convert 8/8/8-bit RGB to 5/6/5-bit RGB
{ uint16_t RGB = (Red>>3);
RGB = (RGB<<5) | (Green>>2);
RGB = (RGB<<6) | (Blue>>3);
return (RGB>>8) | (RGB<<8); }
// uint16_t RGB565(uint8_t *RGB)
// { return RGB565(RGB[0], RGB[1], RGB[2]); }

102
main/st7789.h 100644
Wyświetl plik

@ -0,0 +1,102 @@
#ifndef __ST7789_H__
#define __ST7789_H__
#include <stdint.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
// some predefine colors Red Green Blue
const uint16_t RGB565_BLACK = 0x0000; // 0, 0, 0
const uint16_t RGB565_LIGHTGREY = 0x18C6; // 192, 192, 192
const uint16_t RGB565_DARKGREY = 0xEF7B; // 128, 128, 128
const uint16_t RGB565_WHITE = 0xFFFF; // 255, 255, 255
const uint16_t RGB565_NAVY = 0x0F00; // 0, 0, 128
const uint16_t RGB565_BLUE = 0x1F00; // 0, 0, 255
const uint16_t RGB565_LIGHTBLUE = 0xFF7B; // 127, 127, 255
// const uint16_t RGB565_DARKGREEN = 0x0004; // 0, 128, 0
const uint16_t RGB565_DARKGREEN = 0x0006; // 0, 192, 0
const uint16_t RGB565_GREEN = 0xE007; // 0, 255, 0
const uint16_t RGB565_DARKCYAN = 0xEF03; // 0, 128, 128
const uint16_t RGB565_CYAN = 0xFF07; // 0, 255, 255
const uint16_t RGB565_MAROON = 0x0078; // 128, 0, 0
const uint16_t RGB565_RED = 0x00F8; // 255, 0, 0
const uint16_t RGB565_LIGHTRED = 0xEFFB; // 255, 127, 127
const uint16_t RGB565_PURPLE = 0x0F78; // 128, 0, 128
const uint16_t RGB565_MAGENTA = 0x1FF8; // 255, 0, 255
const uint16_t RGB565_OLIVE = 0xE07B; // 128, 128, 0 ?
// const uint16_t RGB565_DARKYELLOW = 0x0084; // 128, 128, 0
const uint16_t RGB565_DARKYELLOW = 0x00C6; // 192, 192, 0
const uint16_t RGB565_YELLOW = 0xE0FF; // 255, 255, 0
const uint16_t RGB565_ORANGE = 0xA0FD; // 255, 180, 0
const uint16_t RGB565_DARKORANGE = 0xC082; // 128, 90, 0
const uint16_t RGB565_GREENYELLOW = 0xE0B7; // 180, 255, 0
const uint16_t RGB565_DARKGREENYELLOW = 0x005C; // 90, 128, 0
const uint16_t RGB565_PINK = 0x9FFC;
// python formula: "%04X" % ( ((Red>>3)<<11) | ((Green>>2)<<5) | (Blue>>3) )
// and then swap the MSB and LSB bytes
uint16_t RGB565(uint8_t Red, uint8_t Green, uint8_t Blue); // create RGB565 from RGB888
inline uint16_t RGB565(const uint8_t *RGB) { return RGB565(RGB[0], RGB[1], RGB[2]); }
// Embedded fonts
extern uint8_t tft_SmallFont[];
extern uint8_t tft_DefaultFont[];
extern uint8_t tft_Dejavu18[];
extern uint8_t tft_Dejavu24[];
extern uint8_t tft_Ubuntu16[];
extern uint8_t tft_Comic24[];
extern uint8_t tft_minya24[];
extern uint8_t tft_tooney32[];
extern uint8_t tft_def_small[];
// pins specific for this LCD
extern gpio_num_t LCD_PIN_CS; // connected to ground: constantly active
extern gpio_num_t LCD_PIN_DC; // D/C: Command = 0, Data = 1
extern gpio_num_t LCD_PIN_RST; // actually connected to system RESET
extern gpio_num_t LCD_PIN_BCKL; // back-light: HIGH active
extern int LCD_TYPE; // 0 = ST7789
extern int LCD_WIDTH; // [pixels]
extern int LCD_HEIGHT; // [pixels]
const int LCD_BUFF_SIZE = 12*320;
void LCD_Init(spi_host_device_t LCD_SPI_HOST, uint8_t LCD_SPI_MODE, int LCD_SPI_SPEED=10000000 /*, int LCD_TYPE=0 */ );
void LCD_Start(void);
void LCD_ClearDisplay(uint16_t RGB565);
void LCD_SetBacklightLevel(uint8_t Level);
inline void LCD_SetBacklightON(void) { LCD_SetBacklightLevel(8); }
inline void LCD_SetBacklightOFF(void) { LCD_SetBacklightLevel(0); }
void LCD_DrawBox(int xpos, int ypos, int xsize, int ysize, uint16_t RGB565);
void LCD_DrawLine(int x0, int y0, int x1, int y1, uint16_t RGB565);
inline void LCD_DrawPixel(int xpos, int ypos, uint16_t RGB565) { LCD_DrawBox(xpos, ypos, 1, 1, RGB565); }
inline void LCD_DrawHorLine(int xpos, int ypos, int xsize, uint16_t RGB565) { LCD_DrawBox(xpos, ypos, xsize, 1, RGB565); }
inline void LCD_DrawVerLine(int xpos, int ypos, int ysize, uint16_t RGB565) { LCD_DrawBox(xpos, ypos, 1, ysize, RGB565); }
void LCD_DrawCircle(int x, int y, int radius, uint16_t RGB565);
void LCD_DrawJPEG(const uint8_t *JPEG, int JPEGsize, int xpos=0, int ypos=0, int Scale=0);
int LCD_DrawTranspChar(char Char, int xpos, int ypos, uint16_t RGB565, const uint8_t *propFont = tft_Dejavu24);
int LCD_DrawTranspString(const char *String, int xpos, int ypos, uint16_t RGB565, const uint8_t *propFont = tft_Dejavu24);
int LCD_DrawChar(char Char, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont = tft_Dejavu24);
int LCD_DrawString(const char *String, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont = tft_Dejavu24);
int LCD_UpdateString(const char *String, const char *RefString, int xpos, int ypos, uint16_t Fore, uint16_t Back, const uint8_t *propFont = tft_Dejavu24);
int LCD_CharWidth(char Char, const uint8_t *propFont = tft_Dejavu24);
int LCD_StringWidth(const char *String, const uint8_t *propFont = tft_Dejavu24);
int LCD_FontHeight(const uint8_t *propFont = tft_Dejavu24);
#endif // __ST7789_H__

Wyświetl plik

@ -101,6 +101,12 @@ class UBX_NAV_TIMEUTC // 0x01 0x21
uint8_t valid; // bits: 0:ToW, 1:WN, 2:UTC
} ;
class UBX_RXM_PMREQ // 0x02 0x41
{ public:
uint32_t duration; // [ms]
uint32_t flags; // bit #1 = enter backup mode
} ;
class UBX_CFG_CFG
{ public:
uint32_t clearMask;