#ifndef __PARAMETERS_H__ #define __PARAMETERS_H__ #include #include #if defined(WITH_STM32) || defined(WITH_ESP32) #include "hal.h" #endif #include "ogn.h" #ifdef WITH_ESP32 #include "nvs.h" #endif #ifdef WITH_SAMD21 #include "flashsize.h" #endif #ifdef WITH_STM32 #include "stm32f10x_flash.h" #include "flashsize.h" #endif #include "nmea.h" #include "format.h" // Parameters stored in Flash class FlashParameters { public: union { 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; // 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 } ; } ; union { uint32_t RFchip; struct { int16_t RFchipFreqCorr: 12; // [0.1ppm] frequency correction for crystal frequency offset int8_t RFchipTempCorr: 4; // [degC] correction to the temperature measured in the RF chip int8_t TxPower: 6; // [dBm] highest bit set => HW module (up to +20dBm Tx power) bool RFchipTypeHW: 1; // is this RFM69HW (Tx power up to +20dBm) ? uint8_t FreqPlan: 3; // 0=default or force given frequency hopping plan } ; } ; // int16_t RFchipFreqCorr; // [0.1ppm] frequency correction for crystal frequency offset // int8_t RFchipTxPower; // [dBm] highest bit set => HW module (up to +20dBm Tx power) // int8_t RFchipTempCorr; // [degC] correction to the temperature measured in the RF chip union { uint32_t Console; struct { uint32_t CONbaud:24; // [bps] Console baud rate uint8_t CONprot: 8; // [bit-mask] Console protocol mask: 0=minGPS, 1=allGPS, 2=Baro, 3=UBX, 4=OGN, 5=FLARM, 6=GDL90, 7=$PGAV5 } ; } ; int16_t PressCorr; // [0.25Pa] pressure correction for the baro union { 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 packets uint8_t NavMode:3; // GPS navigation mode/model uint8_t Verbose:2; // uint8_t NavRate:3; // [Hz] GPS position report rate int8_t TimeCorr:3; // [sec] it appears for ArduPilot you need to correct time by 3 seconds which is likley the leap-second issue } ; } ; // 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 uint8_t PPSdelay; // [ms] delay between the PPS and the data burst starts on the GPS UART (used when PPS failed or is not there) union { uint8_t GNSS; struct { bool EnableGPS :1; // 1 bool EnableSBAS:1; // 1 bool EnableGAL :1; // 1 bool EnableBEI :1; // 0 bool EnableIMES:1; // 0 bool EnableQZSS:1; // 1 bool EnableGLO :1; // 1 // } ; } ; static const uint8_t InfoParmLen = 16; // [char] max. size of an infp-parameter static const uint8_t InfoParmNum = 15; // [int] number of info-parameters char *InfoParmValue(uint8_t Idx) { return Idx>16)); Format_Hex(Call+5, (uint16_t)Address); Call[9]=0; return 9; } public: void setDefault(void) { setDefault(getUniqueAddress()); } void setDefault(uint32_t UniqueAddr) { AcftID = ((uint32_t)DEFAULT_AcftType<<26) | 0x03000000 | (UniqueAddr&0x00FFFFFF); RFchip = 0; // this clears FreqCorr and other // RFchipFreqCorr = 0; // [0.1ppm] // RFchipTempCorr = 0; // [degC] #ifdef WITH_RFM69W TxPower = 13; // [dBm] for RFM69W RFchipTypeHW = 0; #else TxPower = 14; // [dBm] for RFM69HW RFchipTypeHW = 1; #endif Flags = 0; #ifdef WITH_GPS_UBX NavMode = 6; // 6 = Avionic mode 1g for UBX #endif #ifdef WITH_GPS_MTK NavMode = 2; // 2 = Avionic mode for MTK #endif #ifdef WITH_STRATUX NavRate = 5; // [Hz] Stratux prefers higher rate for AHRS operation #else NavRate = 0; // [Hz] 0 = do not attempt to change the navigation rate #endif GNSS = 0x67; // enable GPS, SBAS, GLONASS and GALILEO, but not BeiDou GeoidSepar = 10*DEFAULT_GeoidSepar; // [0.1m] Verbose = 1; RFchipTempCorr = 0; // [degC] CONbaud = DEFAULT_CONbaud; // [bps] CONprot = 0xFF; PressCorr = 0; // [0.25Pa] TimeCorr = 0; // [sec] FreqPlan = DEFAULT_FreqPlan; // [0..5] PPSdelay = DEFAULT_PPSdelay; // [ms] PageMask = 0xFFFF; InitialPage = 0; AltitudeUnit = 0; // meter SpeedUnit = 0; // km/h VarioUnit = 0; // m/s for(uint8_t Idx=0; IdxADDR.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) && (IdxCTRLA.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>32)); Len+=Format_Hex(Line+Len, (uint32_t)ID); } // uint32_t DefaultAddr=getUniqueAddress(); // if(Address!=DefaultAddr) // { Line[Len++]='/'; Len+=Format_Hex(Line+Len, DefaultAddr, 6); } #ifdef WITH_RFM69 Len+=Format_String(Line+Len, " RFM69"); if(RFchipTypeHW) 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 #ifdef WITH_SX1262 Len+=Format_String(Line+Len, " SX1262"); #endif Line[Len++]='/'; Len+=Format_SignDec(Line+Len, (int16_t)TxPower); Len+=Format_String(Line+Len, "dBm"); 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"); Line[Len]=0; return Len; } uint8_t WritePOGNS(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,CPU=0x"); uint64_t CPU=getUniqueID(); Len+=Format_Hex(Line+Len, (uint32_t)(CPU>>32)); Len+=Format_Hex(Line+Len, (uint32_t)CPU); Len+=Format_String(Line+Len, ",Address=0x"); Len+=Format_Hex(Line+Len, Address, 6); Len+=Format_String(Line+Len, ",AddrType="); Line[Len++]='0'+AddrType; Len+=Format_String(Line+Len, ",Stealth="); Line[Len++]='0'+Stealth; Len+=Format_String(Line+Len, ",AcftType=0x"); Line[Len++]=HexDigit(AcftType); Len+=Format_String(Line+Len, ",FreqPlan="); Line[Len++]='0'+FreqPlan; Len+=Format_String(Line+Len, ",TxPower="); Len+=Format_SignDec(Line+Len, (int16_t)TxPower); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } uint8_t WritePOGNS_Pilot(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,Pilot="); Len+=Format_String(Line+Len, Pilot); Len+=Format_String(Line+Len, ",Crew="); Len+=Format_String(Line+Len, Crew); Len+=Format_String(Line+Len, ",Reg="); Len+=Format_String(Line+Len, Reg); Len+=Format_String(Line+Len, ",Base="); Len+=Format_String(Line+Len, Base); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } uint8_t WritePOGNS_Acft(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,Manuf="); Len+=Format_String(Line+Len, Manuf); Len+=Format_String(Line+Len, ",Model="); Len+=Format_String(Line+Len, Model); Len+=Format_String(Line+Len, ",Type="); Len+=Format_String(Line+Len, Type); Len+=Format_String(Line+Len, ",SN="); Len+=Format_String(Line+Len, SN); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } uint8_t WritePOGNS_Comp(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,Class="); Len+=Format_String(Line+Len, Class); Len+=Format_String(Line+Len, ",ID="); Len+=Format_String(Line+Len, ID); Len+=Format_String(Line+Len, ",Task="); Len+=Format_String(Line+Len, Task); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } #ifdef WITH_AP uint8_t WritePOGNS_AP(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,APname="); Len+=Format_String(Line+Len, APname); Len+=Format_String(Line+Len, ",APass="); Len+=Format_String(Line+Len, APpass); Len+=Format_String(Line+Len, ",APport="); Len+=Format_UnsDec(Line+Len, APport); Len+=Format_String(Line+Len, ",APtxPwr="); Len+=Format_SignDec(Line+Len, ((int16_t)10*APtxPwr+2)>>2, 2, 1); Len+=Format_String(Line+Len, ",APminSig="); Len+=Format_SignDec(Line+Len, (int16_t)APminSig); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } #endif #ifdef WITH_STRATUX uint8_t WritePOGNS_Stratux(char *Line) { uint8_t Len=0; Len+=Format_String(Line+Len, "$POGNS,StratuxWIFI="); Len+=Format_String(Line+Len, StratuxWIFI); Len+=Format_String(Line+Len, ",StratuxPass="); Len+=Format_String(Line+Len, StratuxPass); Len+=Format_String(Line+Len, ",StratuxHost="); Len+=Format_String(Line+Len, StratuxHost); Len+=Format_String(Line+Len, ",StratuxPort="); Len+=Format_UnsDec(Line+Len, StratuxPort); Len+=Format_String(Line+Len, ",StratuxTxPwr="); Len+=Format_SignDec(Line+Len, ((int16_t)10*StratuxTxPwr+2)>>2, 2, 1); Len+=Format_String(Line+Len, ",StratuxMinSig="); Len+=Format_SignDec(Line+Len, (int16_t)StratuxMinSig); Len+=NMEA_AppendCheckCRNL(Line, Len); Line[Len]=0; return Len; } #endif int ReadPOGNS(NMEA_RxMsg &NMEA) { int Count=0; for(uint8_t Idx=0; ; Idx++) { char *Parm = (char *)NMEA.ParmPtr(Idx); if(Parm==0) break; if(ReadLine(Parm)) Count++; } return Count; } static bool isStringChar (char ch) // characters accepted as part of the string values { if( (ch>='0') && (ch<='9') ) return 1; // numbers if( (ch>='A') && (ch<='Z') ) return 1; // uppercase letters if( (ch>='a') && (ch<='z') ) return 1; // lowercase letters if(strchr(".@-+_/#", ch)) return 1; // any of the listed special characters return 0; } static int8_t Read_String(char *Value, const char *Inp, uint8_t MaxLen) { const char *Val = SkipBlanks(Inp); uint8_t Idx; for(Idx=0; Idx' ') break; Inp++; } return Inp; } bool ReadParam(const char *Name, const char *Value) // interprete "Name = Value" line { if(strcmp(Name, "Address")==0) { uint32_t Addr=0; if(Read_Int(Addr, Value)<=0) return 0; Address=Addr; return 1; } if(strcmp(Name, "AddrType")==0) { uint32_t Type=0; if(Read_Int(Type, Value)<=0) return 0; AddrType=Type; return 1; } if(strcmp(Name, "Stealth")==0) { uint32_t Type=0; if(Read_Int(Type, Value)<=0) return 0; Stealth=Type; return 1; } if(strcmp(Name, "AcftType")==0) { uint32_t Type=0; if(Read_Int(Type, Value)<=0) return 0; AcftType=Type; return 1; } if(strcmp(Name, "CONbaud")==0) { uint32_t Baud=0; if(Read_Int(Baud, Value)<=0) return 0; CONbaud=Baud; return 1; } if(strcmp(Name, "CONprot")==0) { uint32_t Prot=0; if(Read_Int(Prot, Value)<=0) return 0; CONprot=Prot; return 1; } if(strcmp(Name, "TxHW")==0) { int32_t HW=1; if(Read_Int(HW, Value)<=0) return 0; RFchipTypeHW=HW; } if(strcmp(Name, "TxPower")==0) { int32_t Power=0; if(Read_Int(Power, Value)<=0) return 0; if(Power<(-32)) Power=(-32); else if(Power>31) Power=31; TxPower=Power; return 1; } if(strcmp(Name, "PPSdelay")==0) { uint32_t Delay=0; if(Read_Int(Delay, Value)<=0) return 0; if(Delay>0xFF) Delay=0xFF; PPSdelay=Delay; return 1; } if(strcmp(Name, "FreqPlan")==0) { uint32_t Plan=0; if(Read_Int(Plan, Value)<=0) return 0; if(Plan>5) Plan=5; FreqPlan=Plan; return 1; } if(strcmp(Name, "FreqCorr")==0) { int32_t Corr=0; if(Read_Float1(Corr, Value)<=0) return 0; RFchipFreqCorr=Corr; return 1; } if(strcmp(Name, "PressCorr")==0) { int32_t Corr=0; if(Read_Float1(Corr, Value)<=0) return 0; PressCorr=4*Corr/10; return 1; } if(strcmp(Name, "TimeCorr")==0) { int32_t Corr=0; if(Read_Int(Corr, Value)<=0) return 0; TimeCorr=Corr; return 1; } 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; 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 Rate=0; if(Read_Int(Rate, Value)<=0) return 0; if(Rate<0) Rate=0; NavRate=Rate; return 1; } if(strcmp(Name, "GNSS")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; GNSS=Mode; return 1; } if(strcmp(Name, "PageMask")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; PageMask=Mode; return 1; } if(strcmp(Name, "InitialPage")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; InitialPage=Mode; return 1; } if(strcmp(Name, "AltitudeUnit")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; AltitudeUnit=Mode; return 1; } if(strcmp(Name, "SpeedUnit")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; SpeedUnit=Mode; return 1; } if(strcmp(Name, "VarioUnit")==0) { int32_t Mode=0; if(Read_Int(Mode, Value)<=0) return 0; VarioUnit=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_LORAWAN if(strcmp(Name, "AppKey")==0) { if(Value[0]=='0' && Value[1]=='x') Value+=2; // skip initial 0x if present for(uint8_t Idx=0; Idx<16; Idx++) // read 16 hex bytes { uint8_t Byte; uint8_t Len=Read_Hex(Byte, Value); if(Len!=2) break; AppKey[Idx]=Byte; Value+=2; } return 1; } #endif #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; // if (bton==2) //WAR: disable usart1 in order to be able to configure BT over 2nd USB // { USART1_Disable(); // bton=1; } BT_ON=bton; return 1; } #endif for(uint8_t Idx=0; Idx0xFFFF) Port=30011; APport=Port; return 1; } if(strcmp(Name, "APtxPwr")==0) { int32_t TxPwr; if(Read_Float1(TxPwr, Value)<=0) return 0; TxPwr=(TxPwr*4+5)/10; if(TxPwr<=0) TxPwr=0; if(TxPwr>=80) TxPwr=80; APtxPwr=TxPwr; return 1; } #endif #ifdef WITH_STRATUX if(strcmp(Name, "StratuxWIFI")==0) return Read_String(StratuxWIFI, Value, 32)<=0; if(strcmp(Name, "StratuxPass")==0) return Read_String(StratuxPass, Value, 32)<=0; if(strcmp(Name, "StratuxHost")==0) return Read_String(StratuxHost, Value, 32)<=0; if(strcmp(Name, "StratuxPort")==0) { int32_t Port; if(Read_Int(Port, Value)<=0) return 0; if(Port<=0 || Port>0xFFFF) Port=30011; StratuxPort=Port; return 1; } if(strcmp(Name, "StratuxMinSig")==0) { int32_t MinSig; if(Read_Int(MinSig, Value)<=0) return 0; if(MinSig<=(-90)) MinSig=(-90); if(MinSig>=0) MinSig=0; StratuxMinSig=MinSig; return 1; } if(strcmp(Name, "StratuxTxPwr")==0) { int32_t TxPwr; if(Read_Float1(TxPwr, Value)<=0) return 0; TxPwr=(TxPwr*4+5)/10; if(TxPwr<=0) TxPwr=0; if(TxPwr>=80) TxPwr=80; StratuxTxPwr=TxPwr; return 1; } #endif #ifdef WITH_APRS if(strcmp(Name, "WIFIname")==0) return Read_String(WIFIname[0], Value, WIFInameLen)<=0; if(strcmp(Name, "WIFIpass")==0) return Read_String(WIFIpass[0], Value, WIFIpassLen)<=0; if( (memcmp(Name, "WIFIname", 8)==0) && (strlen(Name)==9) ) { int Idx=Name[8]-'0'; if( (Idx>=0) && (Idx=0) && (Idx