Improve LoRaWAN code and other minor improvements

pull/46/head
Pawel Jalocha 2022-01-16 17:37:18 +00:00
rodzic d9502ab856
commit 48063e376b
7 zmienionych plików z 300 dodań i 167 usunięć

Wyświetl plik

@ -464,17 +464,18 @@ void OLED_DrawBaro(u8g2_t *OLED, GPS_Position *GPS)
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; }
static int8_t BattCapacity(uint16_t mVolt) // deduce battery capacity from its voltage
{ if(mVolt>=4100) return 100; // if 4.1V or more then full
if(mVolt<=1000) return -1; // if below 1.0V then no-battery
if(mVolt<=3600) return 0; // if below 3.6V then empty
return (mVolt-3600+2)/5; } // otherwise a linear function from 3.6V to 4.1V
void OLED_DrawBattery(u8g2_t *OLED, GPS_Position *GPS) // draw battery status page
{
#ifdef WITH_MAVLINK
uint8_t Cap=MAVLINK_BattCap; // [%] from the drone's telemetry
int8_t Cap=MAVLINK_BattCap; // [%] from the drone's telemetry
#else
uint8_t Cap=BattCapacity(BatteryVoltage>>8); // [%] est. battery capacity based on the voltage readout
int8_t Cap=BattCapacity(BatteryVoltage>>8); // [%] est. battery capacity based on the voltage readout
#endif
// u8g2_SetFont(OLED, u8g2_font_battery19_tn);
// u8g2_DrawGlyph(OLED, 120, 60, '0'+(Cap+10)/20);
@ -486,13 +487,14 @@ void OLED_DrawBattery(u8g2_t *OLED, GPS_Position *GPS) // draw battery status pa
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); // print battery est. capacity
u8g2_DrawFrame(OLED, 12, 20, 42, 14); // draw battery empty box around it
u8g2_DrawBox (OLED, 8, 23, 4, 8); // and the battery tip
if(Cap>=0)
{ strcpy(Line, " %");
if(Cap>=100) Format_UnsDec(Line, (uint8_t)Cap, 3);
else if(Cap>=10) Format_UnsDec(Line+1, (uint8_t)Cap, 2);
else Line[2]='0'+Cap;
u8g2_DrawStr (OLED, 16, 32, Line); // print battery est. capacity
u8g2_DrawFrame(OLED, 12, 20, 42, 14); // draw battery empty box around it
u8g2_DrawBox (OLED, 8, 23, 4, 8); } // and the battery tip
strcpy(Line, " . V");
#ifdef WITH_MAVLINK
@ -555,9 +557,9 @@ void OLED_DrawBattery(u8g2_t *OLED, GPS_Position *GPS) // draw battery status pa
void OLED_DrawStatusBar(u8g2_t *OLED, GPS_Position *GPS) // status bar on top of the OLED
{ static bool Odd=0;
#ifdef WITH_MAVLINK
uint8_t Cap = MAVLINK_BattCap; // [%]
int8_t Cap = MAVLINK_BattCap; // [%]
#else
uint8_t Cap = BattCapacity(BatteryVoltage>>8); // [%] est. battery capacity
int8_t Cap = BattCapacity(BatteryVoltage>>8); // [%] est. battery capacity
#endif
uint8_t BattLev = (Cap+10)/20; // [0..5] convert to display scale
uint8_t Charging = 0; // charging or not changing ?
@ -578,14 +580,15 @@ void OLED_DrawStatusBar(u8g2_t *OLED, GPS_Position *GPS) // status bar on top
#else
uint8_t &DispLev = BattLev;
#endif
if(BattLev==0 && !Charging && Odd) // when battery is empty, then flash it at 0.5Hz
{ } // thus here avoid printing the battery symbol for flashing effect
else // print the battery symbol with DispLev
{ u8g2_SetFont(OLED, u8g2_font_battery19_tn);
u8g2_SetFontDirection(OLED, 3);
u8g2_DrawGlyph(OLED, 20, 10, '0'+DispLev);
u8g2_SetFontDirection(OLED, 0); }
Odd=!Odd;
if(Cap>=0)
{ if(BattLev==0 && !Charging && Odd) // when battery is empty, then flash it at 0.5Hz
{ } // thus here avoid printing the battery symbol for flashing effect
else // print the battery symbol with DispLev
{ u8g2_SetFont(OLED, u8g2_font_battery19_tn);
u8g2_SetFontDirection(OLED, 3);
u8g2_DrawGlyph(OLED, 20, 10, '0'+DispLev);
u8g2_SetFontDirection(OLED, 0); }
Odd=!Odd; }
#ifdef WITH_SD
if(SD_isMounted())
{ u8g2_SetFont(OLED, u8g2_font_twelvedings_t_all);

Wyświetl plik

@ -47,11 +47,11 @@ class FANET_Packet
if(Pref==0x08 || Pref==0x11 || Pref==0x20 || Pref==0xDD || Pref==0xDE || Pref==0xDF) return 2;
return 3; }
void setAddress(uint32_t Addr) { setAddrPref(Addr>>16); setAddrLow(Addr); }
void setAddrPref(uint8_t Prefix) { Byte[1]=Prefix; }
void setAddrLow(uint16_t Addr ) { Byte[2]=Addr; Byte[3]=Addr>>8; }
void setAddress(uint32_t Addr) { setAddrPref(Addr>>16); setAddrLow(Addr); } // full 24-bit address
void setAddrPref(uint8_t Prefix) { Byte[1]=Prefix; } // address prefix
void setAddrLow(uint16_t Addr ) { Byte[2]=Addr; Byte[3]=Addr>>8; } // lower 16-bits of the address
void setHeader(uint8_t Type) { Byte[0] = 0x40 | (Type&0x3F); }
void setType(uint8_t Type) { Byte[0] = (Byte[0]&0xC0) | (Type&0x3F); }
void setType(uint8_t Type) { Byte[0] = (Byte[0]&0xC0) | (Type&0x3F); } // packet-type: 1=air-position
uint8_t ExtHeaderLen(void) const // length ot the extended header (zero in most cases)
{ if(!ExtHeader()) return 0;
@ -62,7 +62,8 @@ class FANET_Packet
uint8_t MsgOfs(void) const { return 4+ExtHeaderLen(); } // offset to the actual message (past the header and ext. header)
uint8_t MsgLen(void) const { return Len-4-ExtHeaderLen(); } // length of the actual message
const uint8_t *Msg(void) const { return Byte+MsgOfs(); }
const uint8_t *Msg(void) const { return Byte+MsgOfs(); } // pointer to the message, past the header
uint8_t *Msg(void) { return Byte+MsgOfs(); }
void setName(const char *Name)
{ setHeader(2);
@ -325,7 +326,7 @@ class FANET_RxPacket: public FANET_Packet
printf("%s CR%c%c%c %3.1fdB/%de %+3.1fkHz ", HHMMSS, '0'+CR, hasCRC?'c':'_', badCRC?'-':'+', 0.25*SNR, BitErr, 1e-2*FreqOfs);
FANET_Packet::Print(Name); }
int WriteJSON(char *JSON) const
int WriteStxJSON(char *JSON) const
{ int Len=0;
Len+=Format_String(JSON+Len, "\"addr\":\"");
Len+=Format_Hex(JSON+Len, Byte[1]);
@ -378,7 +379,29 @@ class FANET_RxPacket: public FANET_Packet
Len+=Format_String(JSON+Len, ",\"lat_deg\":");
Len+=Format_SignDec(JSON+Len, CoordUBX(Lat), 8, 7, 1);
Len+=Format_String(JSON+Len, ",\"lon_deg\":");
Len+=Format_SignDec(JSON+Len, CoordUBX(Lon), 8, 7, 1); }
Len+=Format_SignDec(JSON+Len, CoordUBX(Lon), 8, 7, 1);
int Idx=7;
if(Service&0x40)
{ Len+=Format_String(JSON+Len, ",\"temp_deg\":");
Len+=Format_SignDec(JSON+Len, (int16_t)5*((int8_t)Msg[Idx++]), 2, 1, 1); }
if(Service&0x20)
{ uint16_t Dir = Msg[Idx++]; // [cordic]
Len+=Format_String(JSON+Len, ",\"wind_deg\":");
Len+=Format_UnsDec(JSON+Len, (45*Dir+16)>>5, 2, 1);
uint16_t Wind = getSpeed(Msg[Idx++]); // [0.2km/h]
Len+=Format_String(JSON+Len, ",\"wind_kmh\":");
Len+=Format_UnsDec(JSON+Len, 2*Wind, 2, 1);
uint16_t Gust = getSpeed(Msg[Idx++]);
Len+=Format_String(JSON+Len, ",\"gust_kmh\":");
Len+=Format_UnsDec(JSON+Len, 2*Gust, 2, 1); }
if(Service&0x10)
{ Len+=Format_String(JSON+Len, ",\"hum_perc\":");
Len+=Format_UnsDec(JSON+Len, (uint16_t)4*Msg[Idx++], 2, 1); }
if(Service&0x08)
{ Len+=Format_String(JSON+Len, ",\"press_hpa\":");
Len+=Format_UnsDec(JSON+Len, getPressure(Msg+Idx), 2, 1);
Idx+=2; }
}
if(Type==1 || Type==7) // airborne or ground position
{ int32_t Lat = getLat(Msg); // [cordic] decode the latitude
int32_t Lon = getLon(Msg+3); // [cordic] decode the longitude
@ -568,7 +591,7 @@ class FANET_RxPacket: public FANET_Packet
}
if(SNR>0)
{ Out[Len++]=' ';
Len+=Format_UnsDec(Out+Len, ((uint16_t)SNR*10+2)/4, 2, 1);
Len+=Format_SignDec(Out+Len, ((int16_t)SNR*10+2-843)/4, 2, 1, 1);
Out[Len++]='d'; Out[Len++]='B'; }
Out[Len++]=' ';
Len+=Format_SignDec(Out+Len, FreqOfs/10, 2, 1);

Wyświetl plik

@ -13,68 +13,100 @@
class LoRaWANnode
{ public:
static const uint8_t Chans=8;
uint64_t AppEUI; // from application registration: Application identification
uint8_t AppKey[16]; // from device registration: application encryption/decryption key
uint64_t DevEUI; // Device identification (MAC)
uint32_t DevNonce; // unique counter kept by the device for Join-Requests
uint8_t NetSesKey[16]; // from Join-Accept: Network Session Key
uint8_t AppSesKey[16]; // from Join-Accept: App Session Key
uint32_t JoinNonce; // from Join-Accept: unique must not be reused
uint32_t HomeNetID; // from Join-Accept: Home Network ID
uint32_t DevAddr; // from Join-Accept: Device Address
uint8_t DLsetting; // from Join-Accept: DownLink configuration: OptNeg:1 | RX1 data rate offset:3 | RX2 data rate:4
uint8_t RxDelay; // from Join-Accept: RFU:4 | Del:4 Del=1..15s for the RX1, RX2 delay is Del+1
uint8_t State; // 0:disconencted, 1:join-request sent, 2:join-accept received, 3:uplink-packet sent
uint8_t Chan; // [0..7] Current channel being used
uint32_t UpCount; // [seq] Uplink frame counter: reset when joining the network
uint32_t DnCount; // [seq] Downlink frame counter: reset when joining the network
uint32_t TxCount; // [packets] transmitted to the network
uint32_t LastTx; // [sec] last transmission
uint32_t RxCount; // [packets] received from the network
uint32_t LastRx; // [sec] when last heard from the network
int8_t RxSNR; // [0.25dB] SNR on receive
int8_t RxRSSI; // [dBm] signal strength
union
{ uint8_t Flags;
struct
{ bool RxACK :1; // received ACK
bool TxACK :1; // ACK to be transmitted
bool RxPend:1; // more frames pending for reception
} ;
} ;
uint8_t Spare;
uint8_t Packet[40]; // generic packet for storage/processing
static const uint8_t Chans = 8;
static const size_t UsedBytes = 112; // [bytes] actually used
static const size_t SaveBytes = 128; // [bytes] round up and including CRC32
static const size_t SaveWords = SaveBytes/4;
union
{ uint8_t Byte[SaveBytes];
uint32_t Word[SaveWords];
struct
{ uint64_t AppEUI; // from application registration: Application identification
uint8_t AppKey[16]; // from device registration: application encryption/decryption key
uint64_t DevEUI; // Device identification (MAC)
uint32_t DevNonce; // unique counter kept by the device for Join-Requests
// the four items above need to be kept stored permanently per each device
// the other elements below are obtained when join-accept is received from the LoRaWAN network
uint8_t NetSesKey[16]; // from Join-Accept: Network Session Key
uint8_t AppSesKey[16]; // from Join-Accept: App Session Key
uint32_t JoinNonce; // from Join-Accept: unique must not be reused
uint32_t HomeNetID; // from Join-Accept: Home Network ID
uint32_t DevAddr; // from Join-Accept: Device Address
uint8_t DLsetting; // from Join-Accept: DownLink configuration: OptNeg:1 | RX1 data rate offset:3 | RX2 data rate:4
uint8_t RxDelay; // from Join-Accept: RFU:4 | Del:4 Del=1..15s for the RX1, RX2 delay is Del+1
uint8_t State; // 0:disconencted, 1:join-request sent, 2:join-accept received, 3:uplink-packet sent
uint8_t Chan; // [0..7] Current channel being used
uint32_t UpCount; // [seq] Uplink frame counter: reset when joining the network
uint32_t DnCount; // [seq] Downlink frame counter: reset when joining the network
uint32_t TxCount; // [packets] transmitted to the network
uint32_t LastTx; // [sec] last transmission
uint32_t RxCount; // [packets] received from the network
uint32_t LastRx; // [sec] when last heard from the network
int8_t RxSNR; // [0.25dB] SNR on receive (can be negative)
int8_t RxRSSI; // [dBm] signal strength
union
{ uint8_t Flags;
struct
{ bool RxACK :1; // received ACK
bool TxACK :1; // ACK to be transmitted
bool RxPend:1; // more frames pending for reception
bool Enable:1; // Enable/disable operation
bool SaveReq:1; // Request to save the node state
} ;
} ;
uint8_t Spare; // 112 bytes up to this point
uint32_t LastSaved; // [sec] when saved to EEPROM or other permament storage
uint8_t Dummy[8]; // just to fill up the space, could be used later
uint32_t CRC32; // 128 bytes up to here: fits into 1kbit EEPROM
} ;
} ;
// uint8_t TxMAC[16];
// uint8_t TxMACs;
static const size_t MaxPacketSize = 64; // [bytes]
uint8_t Packet[MaxPacketSize]; // generic packet for storage/processing
public:
LoRaWANnode() { Reset(); }
void Reset(void)
{ State=0; DevNonce=0; JoinNonce=0;
LastTx=0; TxCount=0; LastRx=0; RxCount=0; Flags=0; }
LastTx=0; TxCount=0; LastRx=0; RxCount=0; Flags=0; LastSaved=0;
setCRC(); }
void Reset(uint64_t MAC, uint8_t *AppKey=0)
{ AppEUI=0x70B3D57ED0035895;
DevEUI=MAC;
if(AppKey) memcpy(this->AppKey, AppKey, 16);
Reset(); }
{ AppEUI=0x70B3D57ED0035895; // set OGN application
DevEUI=MAC; // set DevEUI from MAC
if(AppKey) memcpy(this->AppKey, AppKey, 16); // set the AppKey
Reset(); } // reset to not-joined state
void Disconnect(void)
{ State=0; }
uint8_t incrChan(uint8_t Step=1) { Chan+=Step; if(Chan>=Chans) Chan-=Chans; return Chan; }
uint32_t calcCRC(void) const
{ uint32_t Sum=0x87654321; // start the sum with some magic
for(size_t Idx=0; Idx<SaveWords; Idx++)
Sum+=Word[Idx]; // sum all the Words
return Sum; } // return the Sum
int Save(FILE *File) { return fwrite(this, sizeof(LoRaWANnode), 1, File); }
int Save(const char *FileName)
bool goodCRC(void) const { return calcCRC()==0; }
void setCRC(void) { CRC32=0; CRC32 = CRC32-calcCRC(); }
uint8_t incrChan(uint8_t Step=1)
{ Chan+=Step; if(Chan>=Chans) Chan-=Chans; return Chan; }
int Save(FILE *File) { return fwrite(this, sizeof(LoRaWANnode), 1, File); } // save to a file
int Save(const char *FileName) // save to a file
{ FILE *File=fopen(FileName, "wb"); if(File==0) return 0;
int Written=Save(File); fclose(File); return Written; }
int Restore(FILE *File) { return fread(this, sizeof(LoRaWANnode), 1, File); }
int Restore(const char *FileName)
{ FILE *File=fopen(FileName, "rb"); if(File==0) return 0;
int Read=Restore(File); fclose(File); return Read; }
// int Restore(FILE *File) { return fread(this, sizeof(LoRaWANnode), 1, File); }
// int Restore(const char *FileName)
// { FILE *File=fopen(FileName, "rb"); if(File==0) return 0;
// int Read=Restore(File); fclose(File); return Read; }
static int ReadHex(uint8_t *Data, int Len, const char *Inp)
static int ReadHex(uint8_t *Data, int Len, const char *Inp) // read Len bytes from a hex string
{ int Bytes=0;
for( ; Bytes<Len; )
{ int8_t H = Read_Hex1(*Inp++); if(H<0) break;
@ -83,7 +115,7 @@ class LoRaWANnode
return Bytes; }
// int readAppEUI(const char *Inp) { return ReadHex(&AppEUI, 8, Inp); }
int readAppKey(const char *Inp) { return ReadHex(AppKey,16, Inp); }
int readAppKey(const char *Inp) { return ReadHex(AppKey,16, Inp); } // read key from the hex string
// int readDevEUI(const char *Inp) { return ReadHex(&DevEUI, 8, Inp); }
template<class Type>
@ -168,6 +200,9 @@ class LoRaWANnode
LoRaMacPayloadEncrypt(Data, DataLen, AppSesKey, DevAddr, 0, UpCount, Packet+PktLen); PktLen+=DataLen; // copy+encrypt user data
uint32_t MIC=0;
LoRaMacComputeMic(Packet, PktLen, NetSesKey, DevAddr, 0x00, UpCount, &MIC); // calc. MIC
// uint8_t MIC2[4];
// Tiny.Calculate_MIC(Packet, MIC2, PktLen, UpCount, 0x00);
// printf("Data packet MIC: %08X <=> %02X%02X%02X%02X\n", MIC, MIC2[3], MIC2[2], MIC2[1], MIC2[0]);
memcpy(Packet+PktLen, &MIC, 4); PktLen+=4; // append MIC
UpCount++; State=3; return PktLen; } // return the packet size
@ -175,8 +210,8 @@ class LoRaWANnode
{ int Len=getDataPacket(Packet, Data, DataLen, Port, Confirm); *Pkt = Packet; return Len; }
int procRxData(const RFM_LoRa_RxPacket &RxPacket)
{ int Ret = procRxData(RxPacket.Byte, RxPacket.Len); if(Ret<0) return Ret;
RxSNR += (RxPacket.SNR-RxSNR+1)/2; // if good packet then update the signal statistics
{ int Ret=procRxData(RxPacket.Byte, RxPacket.Len); if(Ret<0) return Ret;
RxSNR += (RxPacket.SNR-RxSNR+1)/2;
RxRSSI += (RxPacket.RSSI-RxRSSI+1)/2;
return Ret; }
@ -191,6 +226,7 @@ class LoRaWANnode
if(CountDiff<=0) return -1; // attempt to reuse the counter: drop this packet
uint32_t MIC=0;
LoRaMacComputeMic(PktData, PktLen-4, NetSesKey, Addr, 0x01, Count, &MIC);
// printf("RxData: %08X\n", MIC);
if(memcmp(PktData+PktLen-4, &MIC, 4)) return -1; // give up if MIC does not match
uint8_t OptLen = Ctrl&0x0F; // Options: how many bytes
uint8_t DataOfs = 8 + OptLen; // where the port byte should be
@ -224,12 +260,22 @@ class LoRaWANnode
// Format_Hex(CONS_UART_Write, Opt[Idx]);
// Format_String(CONS_UART_Write, "\n"); }
int WriteToFile(const char *Name)
{ FILE *File = fopen(Name, "wb"); if(File==0) return -1;
int Written = fwrite(this, 1, SaveBytes, File);
fclose(File); return Written; }
int ReadFromFile(const char *Name)
{ FILE *File = fopen(Name, "rb"); if(File==0) return -1;
int Read = fread(this, 1, SaveBytes, File);
fclose(File); return Read; }
#ifdef WITH_ESP32
esp_err_t WriteToNVS(const char *Name="LoRaWAN", const char *NameSpace="TRACKER")
{ nvs_handle Handle;
esp_err_t Err = nvs_open(NameSpace, NVS_READWRITE, &Handle);
if(Err!=ESP_OK) return Err;
Err = nvs_set_blob(Handle, Name, this, sizeof(LoRaWANnode)-40);
Err = nvs_set_blob(Handle, Name, this, SaveBytes);
if(Err==ESP_OK) Err = nvs_commit(Handle);
nvs_close(Handle);
return Err; }
@ -240,12 +286,22 @@ class LoRaWANnode
if(Err!=ESP_OK) return Err;
size_t Size=0;
Err = nvs_get_blob(Handle, Name, 0, &Size); // get the Size of the blob in the Flash
if( (Err==ESP_OK) && (Size<=(sizeof(LoRaWANnode)-40)) )
if( (Err==ESP_OK) && (Size<=SaveBytes) )
Err = nvs_get_blob(Handle, Name, this, &Size); // read the Blob from the Flash
nvs_close(Handle);
return Err; }
#endif // WITH_ESP32
#ifdef RTLSDR_API
int Read(RTLSDR &SDR) { return SDR.readEEPROM(Byte, 0x80, SaveBytes); }
int Write(RTLSDR &SDR) { return SDR.writeEEPROM(Byte, 0x80, SaveBytes); }
#endif
bool isFF(void)
{ for(size_t Idx=0; Idx<SaveBytes; Idx++)
{ if(Byte[Idx]!=0xFF) return 0; }
return 1; }
} ;
#endif // __LORAWAN_H__

Wyświetl plik

@ -585,6 +585,12 @@ template<class OGNx_Packet, uint8_t Size=8>
uint8_t getNew(void) // get (index of) a free or lowest rank packet
{ Sum-=Packet[LowIdx].Rank; Packet[LowIdx].Rank=0; Low=0; return LowIdx; } // remove old packet from the rank sum
uint8_t size(void)
{ uint8_t Count=0;
for(uint8_t Idx=0; Idx<Size; Idx++)
{ if(Packet[Idx].Rank) Count++; }
return Count; }
OGN_RxPacket<OGNx_Packet> *addNew(uint8_t NewIdx) // add the new packet to the queue
{ OGN_RxPacket<OGNx_Packet> *Prev = 0;
uint32_t AddressAndType = Packet[NewIdx].Packet.getAddressAndType(); // get ID of this packet: ID is address-type and address (2+24 = 26 bits)
@ -784,15 +790,20 @@ class GPS_Time
Prev=Hour;
Hour=Read_Dec2(Value); if(Hour<0) return -1; // read hour (two digits), return when invalid
if(Prev!=Hour) Same=0;
Value+=2;
if(Value[0]==':') Value++;
Prev=Min;
Min=Read_Dec2(Value+2); if(Min<0) return -1; // read minute (two digits), return when invalid
Min=Read_Dec2(Value); if(Min<0) return -1; // read minute (two digits), return when invalid
if(Prev!=Min) Same=0;
Value+=2;
if(Value[0]==':') Value++;
Prev=Sec;
Sec=Read_Dec2(Value+4); if(Sec<0) return -1; // read second (two digits), return when invalid
Sec=Read_Dec2(Value); if(Sec<0) return -1; // read second (two digits), return when invalid
Value+=2;
if(Prev!=Sec) Same=0;
int16_t mPrev = mSec;
if(Value[6]=='.') // is there a fraction
{ uint16_t Frac=0; int8_t Len=Read_UnsDec(Frac, Value+7); if(Len<1) return -1; // read the fraction, return when invalid
if(Value[0]=='.') // is there a fraction
{ uint16_t Frac=0; int8_t Len=Read_UnsDec(Frac, Value+1); if(Len<1) return -1; // read the fraction, return when invalid
if(Len==1) mSec = Frac*100;
else if(Len==2) mSec = Frac*10;
else if(Len==3) mSec = Frac;
@ -802,9 +813,13 @@ class GPS_Time
return Same; } // return 1 when time did not change (both RMC and GGA were for same time)
int8_t ReadDate(const char *Param) // read the field DDMMYY
{ Day=Read_Dec2(Param); if(Day<0) return -1; // read calendar day
Month=Read_Dec2(Param+2); if(Month<0) return -1; // read calendar month
Year=Read_Dec2(Param+4); if(Year<0) return -1; // read calendar year (two digits - thus need to be extended to four)
{ Day=Read_Dec2(Param); if(Day<0) return -1; // read calendar year (two digits - thus need to be extended to four)
Param+=2;
if(Param[0]=='/') Param++;
Month=Read_Dec2(Param); if(Month<0) return -1; // read calendar month
Param+=2;
if(Param[0]=='/') Param++;
Year=Read_Dec2(Param); if(Year<0) return -1; // read calendar day
return 0; } // return 0 when field valid and was read correctly
private:
@ -918,6 +933,12 @@ class GPS_Position: public GPS_Time
if(Satellites<=0) return 0; // if number of satellites none or invalid
return 1; }
void copyDOP(GPS_Position &RefPos)
{ HDOP = RefPos.HDOP;
VDOP = RefPos.VDOP;
PDOP = RefPos.PDOP;
FixMode = RefPos.FixMode; }
void copyBaro(GPS_Position &RefPos, int16_t dTime=0)
{ if(!RefPos.hasBaro) { hasBaro=0; return; }
StdAltitude = RefPos.StdAltitude;
@ -927,7 +948,7 @@ class GPS_Position: public GPS_Time
if(dTime)
{ int32_t dAlt = calcAltitudeExtrapolation(dTime); // [0.1m]
StdAltitude += dAlt; // [0.1m]
Pressure += 4000*dAlt/Atmosphere::PressureLapseRate(Pressure/4, Temperature); } // [0.25Pa] ([Pa], [0.1degC])
if(Pressure) Pressure += 4000*dAlt/Atmosphere::PressureLapseRate(Pressure/4, Temperature); } // [0.25Pa] ([Pa], [0.1degC])
hasBaro=1; }
#ifndef __AVR__ // there is not printf() with AVR
@ -1189,18 +1210,23 @@ class GPS_Position: public GPS_Time
else if(TimeDiff>=180000) TimeDiff-=360000;
return TimeDiff; } // [0.01s]
*/
int16_t calcDifferentials(GPS_Position &RefPos) // calculate climb rate and turn rate with an earlier reference position
int16_t calcDifferentials(GPS_Position &RefPos, bool useBaro=1) // 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); // [ms]
if(TimeDiff<10) return 0;
TurnRate = Heading-RefPos.Heading;
if(TurnRate>1800) TurnRate-=3600; else if(TurnRate<(-1800)) TurnRate+=3600;
ClimbRate = Altitude-RefPos.Altitude;
if(hasBaro && RefPos.hasBaro && (abs(Altitude-StdAltitude)<2500) )
{ ClimbRate = StdAltitude-RefPos.StdAltitude; }
Accel = Speed-RefPos.Speed;
if(TimeDiff==200)
if(RefPos.FixQuality==0) return 0; // give up if no fix on the reference position
int16_t TimeDiff = calcTimeDiff(RefPos); // [ms] time difference between positions
if(TimeDiff<10) return 0; // [ms] give up if smaller than 10ms (as well when negative)
TurnRate = Heading-RefPos.Heading; // [0.1deg/s] turn rate
if(TurnRate>1800) TurnRate-=3600; else if(TurnRate<(-1800)) TurnRate+=3600; // wrap-around
ClimbRate = Altitude-RefPos.Altitude; // [0.1m/s] climb rate as altitude difference
if(useBaro && hasBaro && RefPos.hasBaro && (abs(Altitude-StdAltitude)<2500) ) // if there is baro data then
{ ClimbRate += StdAltitude-RefPos.StdAltitude; // [0.1m/s] on pressure altitude
ClimbRate = (ClimbRate+1)>>1; }
Accel = Speed-RefPos.Speed; // longitual acceleration
if(TimeDiff==100) // [ms] if 0.1sec difference
{ ClimbRate*=10;
TurnRate *=10;
Accel *=10; }
if(TimeDiff==200) // [ms] if 0.2sec difference
{ ClimbRate*=5;
TurnRate *=5;
Accel *=5; }
@ -1551,26 +1577,26 @@ class GPS_Position: public GPS_Time
Format_UnsDec(Out+4, Sec , 2);
return 6; }
int WriteIGC(char *Out) const
int WriteIGC(char *Out) const // write IGC B-record
{ // if(!isValid()) return 0;
int Len=0;
Out[Len++] = 'B';
if(isTimeValid()) Len+=WriteHHMMSS(Out+Len);
else Len+=Format_String(Out+Len, " ");
if(isValid())
{ Len+=WriteIGCcoord(Out+Len, Latitude, 2, "NS");
Len+=WriteIGCcoord(Out+Len, Longitude, 3, "EW");
Out[Len++] = FixMode>2 ? 'A':'V'; }
else Len+=Format_String(Out+Len, " ");
if(hasBaro)
{ int32_t Alt = StdAltitude/10; // [m]
if(Alt<0) { Alt = (-Alt); Out[Len++] = '-'; Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 4); }
else { Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 5); }
if(isTimeValid()) Len+=WriteHHMMSS(Out+Len); // if time is valid
else Len+=Format_String(Out+Len, " "); // or leave empty
if(isValid()) // if position valid
{ Len+=WriteIGCcoord(Out+Len, Latitude, 2, "NS"); // DDMM.MMM latitude
Len+=WriteIGCcoord(Out+Len, Longitude, 3, "EW"); // DDDMM.MMM longitude
Out[Len++] = FixMode>2 ? 'A':'V'; } // fix mode
else Len+=Format_String(Out+Len, " "); // is position not valid then leave empty
if(hasBaro) // if pressure data is there
{ int32_t Alt = StdAltitude/10; // [m] pressure altitude
if(Alt<0) { Alt = (-Alt); Out[Len++] = '-'; Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 4); } // -AAAA (when negative)
else { Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 5); } // AAAAA
} else Len+=Format_String(Out+Len, " ");
if(isValid())
{ int32_t Alt = (Altitude+GeoidSeparation)/10; // [m]
if(Alt<0) { Alt = (-Alt); Out[Len++] = '-'; Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 4); }
else { Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 5); }
if(isValid()) // if position is valid
{ int32_t Alt = (Altitude+GeoidSeparation)/10; // [m] HAE GPS altitude
if(Alt<0) { Alt = (-Alt); Out[Len++] = '-'; Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 4); } // -AAAA (when negative)
else { Len+=Format_UnsDec(Out+Len, (uint32_t)Alt, 5); } // AAAAA
} else Len+=Format_String(Out+Len, " ");
Out[Len++]='\n'; Out[Len]=0; return Len; }

Wyświetl plik

@ -5,6 +5,7 @@
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "ognconv.h"
@ -282,7 +283,7 @@ class OGN1_Packet // Packet structure for the OGN tracker
printf("\n");
}
int WriteJSON(char *JSON) const
int WriteStxJSON(char *JSON) const // Stratux JSON message
{ int Len=0;
Len+=Format_String(JSON+Len, "\"addr\":\"");
Len+=Format_Hex(JSON+Len, (uint8_t) (Header.Address>>16));
@ -552,13 +553,13 @@ class OGN1_Packet // Packet structure for the OGN tracker
if(Header.NonPos) // status and info packets
{ if(Status.ReportType==0) Len+=WriteStatus(Msg+Len);
else Len+=WriteDeviceInfo(Msg+Len);
Msg[Len++]='\n'; Msg[Len]=0; return Len; }
/* Msg[Len++]='\n'; */ Msg[Len]=0; return Len; }
if(Header.Encrypted) // encrypted packets
{ Msg[Len++]=' ';
for(int Idx=0; Idx<4; Idx++)
{ Len+=EncodeAscii85(Msg+Len, Data[Idx]); }
Msg[Len++]='\n'; Msg[Len]=0; return Len; }
/* Msg[Len++]='\n'; */ Msg[Len]=0; return Len; }
const char *Icon = getAprsIcon(Position.AcftType);
@ -616,7 +617,9 @@ class OGN1_Packet // Packet structure for the OGN tracker
Msg[Len++] = ' '; Msg[Len++] = 'g'; Msg[Len++] = 'p'; Msg[Len++] = 's';
Len+=Format_UnsDec(Msg+Len, HorPrec); Msg[Len++] = 'x'; Len+=Format_UnsDec(Msg+Len, VerPrec);
Msg[Len++]='\n'; Msg[Len]=0; return Len; }
// Msg[Len++]='\n';
Msg[Len]=0;
return Len; }
#endif // __AVR__

Wyświetl plik

@ -41,7 +41,7 @@ static uint32_t RF_SlotTime; // [sec] UTC time which belongs to t
FIFO<FANET_Packet, 4> FNT_TxFIFO;
#endif
int32_t TX_Credit = 0; // [ms] counts transmitter time avoid using more than 1%
int32_t TX_Credit = 0; // [ms] counts transmitter time avoid using more than 1% or air time
uint8_t RX_OGN_Packets=0; // [packets] counts received packets
static LowPass2<uint32_t, 4,2,4> RX_RSSI; // low pass filter to average the RX noise
@ -80,7 +80,8 @@ static void SetTxChannel(uint8_t TxChan=RX_Channel) // default channel t
static void SetRxChannel(uint8_t RxChan=RX_Channel)
{
#ifdef WITH_SX1262
// TRX.setChannel(RxChan&0x7F);
TRX.setChannel(RxChan&0x7F);
TRX.FSK_WriteSYNC(7, 7, OGN_SYNC); // Shorter SYNC for RX
#else
TRX.WriteTxPowerMin(); // setup for RX
TRX.setChannel(RxChan&0x7F);
@ -102,7 +103,7 @@ static uint8_t ReceivePacket(void) // see if a pack
RxPkt->Channel = RX_Channel; // store reception channel
RxPkt->RSSI = RxRSSI; // store signal strength
TRX.OGN_ReadPacket(RxPkt->Data, RxPkt->Err); // get the packet data from the FIFO
RxPkt->Print(CONS_UART_Write); // for debug
// RxPkt->Print(CONS_UART_Write); // for debug
RF_RxFIFO.Write(); // complete the write to the receiver FIFO
TRX.setModeRX(); // back to receive (but we already have AutoRxRestart)
@ -116,6 +117,15 @@ static uint32_t ReceiveUntil(TickType_t End)
int32_t Left = End-xTaskGetTickCount();
if(Left<=0) break;
vTaskDelay(1); }
#ifdef WITH_SX1262
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "ReceiveUntil() => 0x");
Format_Hex(CONS_UART_Write, TRX.ReadIrqFlags());
Format_String(CONS_UART_Write, "\n");
xSemaphoreGive(CONS_Mutex);
#endif
#endif
return Count; }
// static uint32_t ReceiveFor(TickType_t Ticks) // keep receiving packets for given period of time
@ -171,7 +181,7 @@ static uint8_t Transmit(uint8_t TxChan, const uint8_t *PacketByte, uint8_t Thres
Format_String(CONS_UART_Write, "ms\n");
xSemaphoreGive(CONS_Mutex);
#endif
#else
#else // of WITH_SX1262
TRX.ClearIrqFlags();
TRX.OGN_WritePacket(PacketByte); // write packet into FIFO
TRX.setModeTX(); // transmit
@ -188,6 +198,15 @@ static uint8_t Transmit(uint8_t TxChan, const uint8_t *PacketByte, uint8_t Thres
SetRxChannel();
TRX.setModeRX(); // back to receive mode
TRX.ClearIrqFlags();
#ifdef WITH_SX1262
#ifdef DEBUG_PRINT
xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "setModeRX() => 0x");
Format_Hex(CONS_UART_Write, TRX.getStatus());
Format_String(CONS_UART_Write, "\n");
xSemaphoreGive(CONS_Mutex);
#endif
#endif
return 1; }
// make a time-slot: listen for packets and transmit given PacketByte$
static void TimeSlot(uint8_t TxChan, uint32_t SlotLen, const uint8_t *PacketByte, uint8_t Rx_RSSI, uint8_t MaxWait=8, uint32_t TxTime=0)
@ -428,7 +447,7 @@ extern "C"
TRX.WAN_Configure(); // LoRa for WAN config.
TRX.setChannel(WANdev.Chan); // set the channel
TRX.LoRa_InvertIQ(1); TRX.LoRa_setCRC(0); TRX.LoRa_setIRQ(0); // setup for WAN RX
TRX.setModeLoRaRXsingle(); // wait for a single packet
TRX.setModeLoRaRXsingle(); // wait for a single packet
int Wait=WAN_RespLeft+100; // 100ms timeout after the expected reception
for( ; Wait>0; Wait--)
{ vTaskDelay(1);
@ -446,15 +465,15 @@ extern "C"
if(WANdev.State==1) WANdev.procJoinAccept(WAN_RxPacket); // if join-request state then expect a join-accept packet
else if(WANdev.State==3) RxLen=WANdev.procRxData(WAN_RxPacket); // if data send then respect ACK and/or downlink data packet
}
else WANdev.State--; // if no packet received then retreat the State
TRX.setFSK(); // back to FSK
SetFreqPlanOGN(); // OGN frequency plan
TRX.OGN_Configure(0, OGN_SYNC); // OGN config
else WANdev.State--; // if no packet received then retreat the State
TRX.setFSK(); // back to FSK
SetFreqPlanOGN(); // OGN frequency plan
TRX.OGN_Configure(0, OGN_SYNC); // OGN config
SetRxChannel();
TRX.setModeRX(); // switch to receive mode
TRX.setModeRX(); // switch to receive mode
TRX.ClearIrqFlags();
WANdev.WriteToNVS(); // store new WAN state in flash
if(RxLen>0) // if Downlink data received
WANdev.WriteToNVS(); // store new WAN state in flash
if(RxLen>0) // if Downlink data received
{ xSemaphoreTake(CONS_Mutex, portMAX_DELAY);
Format_String(CONS_UART_Write, "LoRaWAN Msg: ");
// Format_UnsDec(CONS_UART_Write, (uint16_t)RxLen);
@ -542,7 +561,7 @@ extern "C"
} while(TimeSync_msTime()<350); // keep going until 400 ms after PPS
RX_RSSI.Process(RxRssiSum/RxRssiCount); // [-0.5dBm] average noise on channel
TX_Credit+=10; if(TX_Credit>3600000) TX_Credit=3600000; // [ms] count the transmission credit
TX_Credit+=10; if(TX_Credit>3600000) TX_Credit=600000; // [ms] count the transmission credit
XorShift32(RX_Random);
uint32_t TxTime = (RX_Random&0x3F)+1; TxTime*=6; TxTime+=50; // random transmission time: (1..64)*6+50 [ms]
@ -630,22 +649,22 @@ extern "C"
#ifdef WITH_PAW
static uint8_t PAWtxBackOff = 4;
#ifdef WITH_LORAWAN
if(!WANtx && TxPkt0)
if(!WANtx && TxPkt0 && WANdev.State!=1 && WANdev.State!=3) // if no WAN transmission/reception scheduled
#else
if(TxPkt0)
#endif
{ PAW_Packet Packet; Packet.Clear();
OGN1_Packet TxPkt = TxPkt0->Packet;
TxPkt.Dewhiten();
XorShift32(RX_Random);
TxPkt.Dewhiten(); // de-whiten the OGN packet so it can be converted to PAW format
XorShift32(RX_Random); // convert PAW to OGN
if(PAWtxBackOff==0 && Parameters.TxPower!=(-32) && !TxPkt.Header.Relay && Packet.Copy(TxPkt) && TxPkt.Position.Time<60)
{ TRX.setModeStandby();
TRX.PAW_Configure(PAW_SYNC);
TRX.WriteTxPower(Parameters.TxPower+6);
vTaskDelay(RX_Random&0x3F);
TRX.ClearIrqFlags();
TRX.PAW_WritePacket(Packet.Byte, 24);
TRX.setModeTX();
TRX.PAW_WritePacket(Packet.Byte, 24); //
TRX.setModeTX(); //
vTaskDelay(8); // wait 8ms (about the PAW packet time)
#ifdef WITH_SX1262
TRX.WaitWhileBusy_ms(2);

Wyświetl plik

@ -299,9 +299,9 @@ class RFM_TRX
uint16_t WaitWhileBusy(uint16_t Loops=100) // 50 seems to be still too short on RPI
{ for( ; Loops; Loops--)
{ if(!readBusy()) break; }
return Loops; }
return Loops; } // return number of looks left or zero (=> still busy)
uint16_t WaitWhileBusy_ms(uint16_t ms=10)
uint16_t WaitWhileBusy_ms(uint16_t ms=100)
{ WaitWhileBusy(50);
for( ; ms; ms--)
{ if(!readBusy()) break;
@ -699,36 +699,23 @@ class RFM_TRX
uint8_t getModulation(void) { return Cmd_Read(CMD_GETPACKETTYPE, 1)[0]; }
uint8_t getStatus(void) { return Cmd_Read(CMD_GETSTATUS, 0)[-1]; } // RMMM SSSR MMM: 2=STBY_RC, 3=STBY_XOSC, 4:FS, 5:RX, 6:TX p.95
bool isModeRX(void) { uint8_t Mode=getStatus(); Mode = (Mode>>4)&7; return Mode==5; }
void WriteTxPower(int8_t TxPower)
void WriteTxPower(int8_t TxPower, uint8_t TxRamp=0x04) // 0x04 = 200us ramp time
{ if(TxPower>22) TxPower=22; // for high power PA
else if(TxPower<(-9)) TxPower=(-9);
else if(TxPower<(-3)) TxPower=(-3);
uint8_t PAparm[4] = { 0x04, 0x07, 0x00, 0x01 } ; // for high power PA: paDutyCycle, hpMax, deviceSel, paLut
Cmd_Write(CMD_SETPACONFIG, PAparm, 4); // Power Amplifier Configuration
uint8_t TxParm[2] = { (uint8_t)TxPower, 0x04 } ; // RampTime = 200us
uint8_t MaxCurr = 0x38; // 160mA max. total current
Regs_Write(REG_OCPCONFIG, &MaxCurr, 1);
uint8_t TxParm[2] = { (uint8_t)TxPower, TxRamp } ; //
Cmd_Write(CMD_SETTXPARAMS, TxParm, 2); } // 0x8E, Power, RampTime
void WriteTxPowerMin(void) { WriteTxPower(-8); }
void WriteTxPowerMin(void) { WriteTxPower(-3); }
void Calibrate(void) // Calibrate receiver image rejection
{ uint8_t CalParm[2] = { 0xD7, 0xDB }; // for 868MHz // { 0xE1, 0xE9 } for 915MHz
Cmd_Write(CMD_SETTXPARAMS, CalParm, 2); }
void FSK_WriteSYNC(uint8_t WriteSize, uint8_t SyncTol, const uint8_t *SyncData)
{ if(SyncTol>7) SyncTol=7;
if(WriteSize>8) WriteSize=8;
uint8_t Param[12];
Param[0] = 0; //
Param[1] = 4; // [bits] preamble length
Param[2] = 0x04; // preamble detect: 0x00:OFF, 0x04:8bits, 0x05:16bits, 0x06=24bits, 0x07=32bits
Param[3] = WriteSize*8; // [bits] SYNC word length, write word at 0x06C0
Param[4] = 0x00; // address filtering: OFF
Param[5] = 0x00; // fixed packet size
Param[6] = 2*26; // 26 bytes, software Manchester
Param[7] = 0x01; // no CRC
Param[8] = 0x00; // no whitening
Cmd_Write(CMD_SETPACKETPARAMS, Param, 9); // 0x8C, PacketParam
Regs_Write(REG_SYNCWORD0, SyncData+(8-WriteSize), WriteSize); } // Write the SYNC word
static void Pack3bytes(uint8_t *Byte, uint32_t Value) { Byte[0]=Value>>16; Byte[1]=Value>>8; Byte[2]=Value; }
void FNT_Configure(uint8_t CR=1) // configure for FANET/LoRa
@ -756,6 +743,22 @@ class RFM_TRX
Param[1] = (CFG.SYNC<<4) | 0x04;
Regs_Write(REG_LORASYNCWORD, Param, 2); }
void FSK_WriteSYNC(uint8_t WriteSize, uint8_t SyncTol, const uint8_t *SyncData)
{ if(SyncTol>7) SyncTol=7;
if(WriteSize>8) WriteSize=8;
uint8_t Param[12];
Param[0] = 0; //
Param[1] = 4; // [bits] preamble length
Param[2] = 0x04; // preamble detect: 0x00:OFF, 0x04:8bits, 0x05:16bits, 0x06=24bits, 0x07=32bits
Param[3] = WriteSize*8; // [bits] SYNC word length, write word at 0x06C0
Param[4] = 0x00; // address filtering: OFF
Param[5] = 0x00; // fixed packet size
Param[6] = 2*26; // 26 bytes, software Manchester
Param[7] = 0x01; // no CRC
Param[8] = 0x00; // no whitening
Cmd_Write(CMD_SETPACKETPARAMS, Param, 9); // 0x8C, PacketParam
Regs_Write(REG_SYNCWORD0, SyncData+(8-WriteSize), WriteSize); } // Write the SYNC word
void OGN_Configure(int16_t Channel, const uint8_t *SyncData)
{ setChannel(Channel);
uint8_t Param[12];
@ -764,9 +767,9 @@ class RFM_TRX
Param[4] = 0x0A; // DSB RX bandwidth: 0x0A=232.3kHz, 0x19=312.2kHz, 0x1B=78.2kHz, 0x13=117.3kHz
Pack3bytes(Param+5, 52429); // FSK deviation: 50e3*2^25/Xtal for OGN +/-50kHz
Cmd_Write(CMD_SETMODULATIONPARAMS, Param, 8); // 0x8B, ModParam
Param[0] = 0; //
Param[1] = 4; // [bits] preamble length
Param[2] = 0x00; // preamble detect: 0x00:OFF, 0x04:8bits, 0x05:16bits, 0x06=24bits, 0x07=32bits
Param[0] = 0; // [bits] MSB
Param[1] = 8; // [bits] LSB preamble length
Param[2] = 0x04; // preamble detect: 0x00:OFF, 0x04:8bits, 0x05:16bits, 0x06=24bits, 0x07=32bits
Param[3] = 8*8; // [bits] SYNC word length, write word at 0x06C0
Param[4] = 0x00; // address filtering: OFF
Param[5] = 0x00; // fixed packet size
@ -834,7 +837,7 @@ class RFM_TRX
void setModeTX(void) { WriteMode(RF_OPMODE_TRANSMITTER); } // FSK transmit
bool isModeTX(void) { return ReadMode()==RF_OPMODE_TRANSMITTER; } // in transmitter mode ?
void setModeRX(void) { WriteMode(RF_OPMODE_RECEIVER); } // FSK receive
bool isModeRX(void) { return ReadMode()==RF_OPMODE_RECEIVER; } // in receiver mode ? ?
bool isModeRX(void) { return ReadMode()==RF_OPMODE_RECEIVER; } // in receiver mode ?
#if defined(WITH_RFM95) || defined(WITH_SX1272)
void setModeLoRaStandby(void) { WriteMode(RF_OPMODE_LORA_STANDBY); } // LoRa standby
void setModeLoRaRXcont(void) { WriteMode(RF_OPMODE_LORA_RX_CONT); } // Lora continues recieve
@ -970,10 +973,10 @@ class RFM_TRX
RFM_LoRa_Config CFG = RFM_FNTcfg; CFG.CR=CR;
LoRa_Configure(CFG, FANET_Packet::MaxBytes); }
void WAN_Configure(uint8_t CR=1) // configure for LoRaWAN
void WAN_Configure(RFM_LoRa_Config CFG = RFM_WANcfg, uint8_t MaxPktSize=40) // configure for LoRaWAN
{ WriteTxPower(0);
RFM_LoRa_Config CFG = RFM_WANcfg; CFG.CR=CR;
LoRa_Configure(CFG, 40); }
// RFM_LoRa_Config CFG = RFM_WANcfg; CFG.CR=CR;
LoRa_Configure(CFG, MaxPktSize); }
void LoRa_setIRQ(uint8_t Mode=0) // 0:on RX, 1:on TX, 2: on CAD
{ WriteByte(Mode<<6, REG_DIOMAPPING1); }