@ -8,7 +8,7 @@
//#include <TTGO_T-Beam_LoRa_APRS_config.h> // to config user parameters
# include <Arduino.h>
# include <SPI.h>
# include < BG_RF95.h> // library from OE1ACM
# include < RadioLib.h>
# include <math.h>
# include <driver/adc.h>
# include <Wire.h>
@ -45,10 +45,20 @@ String wifi_info; // saving wifi info (CLI|AP|dis) for Oled. If W
# define SSD1306_ADDRESS 0x3C
// SPI config
# define SPI_sck 5
# define SPI_miso 19
# define SPI_mosi 27
# define SPI_ss 18
# ifdef ESP32_DEV_V1
# define SPI_sck 18
# define SPI_miso 19
# define SPI_mosi 23
# define SPI_ss 5
# else
# define SPI_sck 5
# define SPI_miso 19
# define SPI_mosi 27
# define SPI_ss 18
# define DIO0 26
# define RESET 14
# define DIO1 35
# endif
// IO config
# ifdef T_BEAM_V1_0
@ -116,6 +126,18 @@ String wifi_info; // saving wifi info (CLI|AP|dis) for Oled. If W
double lora_freq = 433.775 ;
# endif
// flag to indicate transmission or reception state
bool transmit_flag = false ;
// flag to indicate that a packet was received
volatile bool received_flag = false ;
// disable interrupt when it's not needed
volatile bool enable_interrupt = true ;
// flag to indicate that a packet was sent
volatile bool transmitted_flag = false ;
ulong lora_speed_cross_digi = 1200 ;
double lora_freq_cross_digi = 433.900 ;
@ -402,7 +424,7 @@ uint32_t time_lora_automaic_cr_adoption_rx_measurement_window = 0L;
uint16_t lora_automaic_cr_adoption_rf_transmissions_heard_in_timeslot = 0 ;
uint16_t lora_packets_received_in_timeslot_on_main_freq = 0 ;
uint16_t lora_packets_received_in_timeslot_on_secondary_freq = 0 ;
char lora_TXBUFF_for_digipeating [ BG_RF95 _MAX_MESSAGE_LEN+ 1 ] = " " ; // buffer for digipeating
char lora_TXBUFF_for_digipeating [ RADIOLIB_SX127X _MAX_MESSAGE_LEN+ 1 ] = " " ; // buffer for digipeating
time_t time_lora_TXBUFF_for_digipeating_was_filled = 0L ;
boolean sendpacket_was_called_twice = false ;
// bits for sendpacket()
@ -425,10 +447,12 @@ static const adc_unit_t unit = ADC_UNIT_1;
AXP20X_Class axp ;
# endif
// Singleton instance of the radio driver
BG_RF95 rf95 ( 18 , 26 ) ; // TTGO T-Beam has NSS @ Pin 18 and Interrupt IO @ Pin26
# ifdef ESP32_DEV_V1
SX1278 rf95 = new Module ( SPI_ss , DIO0 , RESET , DIO1 ) ; // For custom ESP32 and LoRa module
# else
SX1278 rf95 = new Module ( SPI_ss , DIO0 , RESET , DIO1 ) ;
# endif
char blacklist_calls [ 256 ] = " " ;
@ -436,14 +460,6 @@ char blacklist_calls[256] = "";
# define OLED_RESET 16 // not used
Adafruit_SSD1306 display ( 128 , 64 , & Wire , OLED_RESET ) ;
# ifdef IF_SEMAS_WOULD_WORK
xSemaphoreHandle sema_lora_chip ;
# else
volatile boolean sema_lora_chip = false ;
# endif
// + FUNCTIONS-----------------------------------------------------------+//
void do_serial_println ( const String & msg )
@ -674,23 +690,65 @@ void buzzer(int* melody, int array_size){
void lora_set_speed ( ulong lora_speed ) {
if ( lora_speed = = 1200 ) {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr47Sf512 ) ;
if ( lora_speed = = 21000 ) {
rf95 . begin ( lora_freq , 250.0 , 12 , 6 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 6000 ) {
rf95 . begin ( lora_freq , 125.0 , 7 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 1200 ) {
rf95 . begin ( lora_freq , 125.0 , 9 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 610 ) {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr48Sf1024 ) ;
rf95 . begin ( lora_freq , 125.0 , 10 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 180 ) {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr48Sf4096 ) ;
rf95 . begin ( lora_freq , 125.0 , 12 , 8 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 210 ) {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr47Sf4096 ) ;
rf95 . begin ( lora_freq , 125.0 , 12 , 7 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 240 ) {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr46Sf4096 ) ;
rf95 . begin ( lora_freq , 125.0 , 12 , 6 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 1367 ) {
rf95 . begin ( lora_freq , 31.25 , 7 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else if ( lora_speed = = 439 ) {
rf95 . begin ( lora_freq , 31.25 , 9 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
else {
rf95 . setModemConfig ( BG_RF95 : : Bw125Cr45Sf4096 ) ;
rf95 . begin ( lora_freq , 125.0 , 12 , 5 , 0x12 , txPower ) ;
rf95 . setCRC ( 1 ) ;
rf95 . setDio0Action ( setFlag ) ;
rf95 . startReceive ( ) ;
}
}
@ -739,78 +797,29 @@ void loraSend(byte lora_LTXPower, float lora_FREQ, ulong lora_SPEED, const Strin
if ( ! lora_tx_enabled )
return ;
// kind of csma/cd. TODO: better approach: add to a tx-queue
// in SF12: preamble + lora header = 663.552 -> we wait 1300ms for check if lora-chip is in decoding
// See https://www.rfwireless-world.com/calculators/LoRaWAN-Airtime-calculator.html
// In detail: At our supported speed "names":
// SF7: 28.928ms
// SF8: 57.856ms
// SF9: 115.712ms 1200
// SF10: 231.424ms 610
// SF11: 331.776ms
// SF12: 663.552ms 300,240, 210, 180
uint32_t wait_for_signal = 700 ;
if ( lora_speed = = 610 ) wait_for_signal = 250 ;
else if ( lora_speed = = 1200 ) wait_for_signal = 125 ;
// sema lock for lora chip operations
# ifdef IF_SEMAS_WOULD_WORK
while ( xSemaphoreTake ( sema_lora_chip , 10 ) ! = pdTRUE )
esp_task_wdt_reset ( ) ;
# else
for ( n = 0 ; sema_lora_chip ; n + + ) {
delay ( 10 ) ;
if ( ! ( n % 100 ) )
esp_task_wdt_reset ( ) ;
}
sema_lora_chip = true ;
# endif
randomSeed ( millis ( ) ) ;
for ( n = 0 ; n < 30 ; n + + ) {
esp_task_wdt_reset ( ) ;
delay ( wait_for_signal ) ;
if ( rf95 . SignalDetected ( ) ) {
continue ;
}
delay ( 100 ) ;
if ( ! rf95 . SignalDetected ( ) & & random ( 256 ) < 64 ) {
break ;
}
}
esp_task_wdt_reset ( ) ;
# ifdef T_BEAM_V1_0
axp . setPowerOutPut ( AXP192_LDO2 , AXP202_ON ) ; // switch LoRa chip on
# endif
//byte array
byte lora_TXBUFF [ BG_RF95 _MAX_MESSAGE_LEN] ; //buffer for packet to send
byte lora_TXBUFF [ RADIOLIB_SX127X_MAX_MESSAGE_LEN ] ; //buffer for packet to send
int messageSize = min ( message . length ( ) , sizeof ( lora_TXBUFF ) - 1 ) ;
message . toCharArray ( ( char * ) lora_TXBUFF , messageSize + 1 , 0 ) ;
lora_set_speed ( lora_SPEED ) ;
rf95 . setFrequency ( lora_FREQ ) ;
rf95 . setTxPower ( lora_LTXPower ) ;
enable_interrupt = false ;
rf95 . transmit ( lora_TXBUFF , message . length ( ) ) ;
enable_interrupt = true ;
# ifdef ENABLE_LED_SIGNALING
digitalWrite ( TXLED , LOW ) ;
# endif
lastTX = millis ( ) ;
rf95 . sendAPRS ( lora_TXBUFF , messageSize ) ;
rf95 . waitPacketSent ( ) ;
# ifdef ENABLE_LED_SIGNALING
digitalWrite ( TXLED , HIGH ) ;
# endif
// cross-digipeating may have altered our RX-frequency. Revert frequency change needed for this transmission.
if ( lora_FREQ ! = lora_freq_rx_curr ) {
rf95 . setFrequency ( lora_freq_rx_curr ) ;
// flush cache. just to be sure, so that no cross-digi-qrg packet comes in the input-buffer of the main qrg.
// With no buffer / length called, recvAPRS directly calls clearRxBuf()
rf95 . recvAPRS ( 0 , 0 ) ;
}
if ( lora_SPEED ! = lora_speed_rx_curr )
lora_set_speed ( lora_speed_rx_curr ) ;
rf95 . startReceive ( ) ;
# ifdef T_BEAM_V1_0
// if lora_rx is disabled, but ONLY if lora_digipeating_mode == 0 AND no SerialBT.hasClient is connected,
// we can savely go to sleep
@ -821,13 +830,6 @@ void loraSend(byte lora_LTXPower, float lora_FREQ, ulong lora_SPEED, const Strin
) )
axp . setPowerOutPut ( AXP192_LDO2 , AXP202_OFF ) ; // switch LoRa chip off
# endif
// release lock
# ifdef IF_SEMAS_WOULD_WORK
xSemaphoreGive ( sema_lora_chip ) ;
# else
sema_lora_chip = false ;
# endif
}
@ -2169,12 +2171,6 @@ static boolean preferences_cfg_valid = false;
batt_read ( ) ;
writedisplaytext ( " LoRa-APRS " , " " , " Init: " , " ADC OK! " , " BAT: " + String ( BattVolts , 2 ) , " " ) ;
delay ( 500 ) ;
if ( ! rf95 . init ( ) ) {
writedisplaytext ( " LoRa-APRS " , " " , " Init: " , " RF95 FAILED! " , " :-( " , " " ) ;
for ( ; ; ) ; // Don't proceed, loop forever
}
writedisplaytext ( " LoRa-APRS " , " " , " Init: " , " RF95 OK! " , " " , " " ) ;
// if we are fill-in or wide2 digi, we listen only on configured main frequency
@ -2184,22 +2180,12 @@ static boolean preferences_cfg_valid = false;
Serial . printf ( " LoRa Speed: \t %lu \r \n " , lora_speed_rx_curr ) ;
lora_freq_rx_curr = ( rx_on_frequencies ! = 2 | | lora_digipeating_mode > 1 ) ? lora_freq : lora_freq_cross_digi ;
rf95 . setFrequency ( lora_freq_rx_curr ) ;
//rf95.setFrequency(lora_freq_rx_curr);
Serial . printf ( " LoRa FREQ: \t %f \r \n " , lora_freq_rx_curr ) ;
// we tx on main and/or secondary frequency. For tx, loraSend is called (and always has desired txpower as argument)
rf95 . setTxPower ( ( lora_digipeating_mode < 2 | | lora_cross_digipeating_mode < 1 ) ? txPower : txPower_cross_digi ) ;
delay ( 500 ) ;
// Avoid concurrent access of processes to lora our chip
# ifdef IF_SEMAS_WOULD_WORK
sema_lora_chip = xSemaphoreCreateBinary ( ) ;
# else
sema_lora_chip = false ;
# endif
//rf95.setTxPower((lora_digipeating_mode < 2 || lora_cross_digipeating_mode < 1) ? txPower : txPower_cross_digi);
//delay(500);
// LORA32_21: bug in hardware. cannot run bluetooth and wifi concurrently.
// We wait for a bt-client connecting, up to 60s. If none connected,
@ -2365,6 +2351,16 @@ static boolean preferences_cfg_valid = false;
digitalWrite ( TXLED , HIGH ) ;
}
void setFlag ( void ) {
// check if the interrupt is enabled
if ( ! enable_interrupt ) {
return ;
}
// we got a packet, set the flag
received_flag = true ;
}
void enableOled ( ) {
if ( ! enabled_oled )
return ;
@ -2528,11 +2524,11 @@ int bg_rf95snr_to_snr(uint8_t snr)
int bg_rf95rssi_to_rssi ( int rssi )
{
// We use an old BG_RF95 library from 2001. RadioHead/RH_RF95.cpp implements _lastSNR and _lastRssi correctly accordingg to the specs
int _lastSNR = bg_rf95snr_to_snr ( rf95 . las tSNR( ) ) ;
int _lastSNR = bg_rf95snr_to_snr ( rf95 . ge tSNR( ) ) ;
boolean _usingHFport = ( lora_freq > = 779.0 ) ;
// bg_rf95 library: _lastRssi = spiRead(BG_RF95_REG_1A_PKT_RSSI_VALUE) - 137. First, undo -137 operation
int _lastRssi = rf95 . lastRssi ( ) + 137 ;
int _lastRssi = rf95 . getRSSI ( ) + 137 ;
if ( _lastSNR < 0 )
_lastRssi = _lastRssi + _lastSNR ;
@ -2554,8 +2550,8 @@ char *encode_snr_rssi_in_path()
// https://github.com/Lora-net/LoRaMac-node/issues/275
// https://github.com/mayeranalytics/pySX127x/blob/master/SX127x/LoRa.py
// rf95snr_to_snr returns values in range -32 to 31. The lowest two bits are RFU
int snr = bg_rf95snr_to_snr ( rf95 . las tSNR( ) ) ;
int rssi = bg_rf95rssi_to_rssi ( rf95 . lastRssi ( ) ) ;
int snr = bg_rf95snr_to_snr ( rf95 . ge tSNR( ) ) ;
int rssi = bg_rf95rssi_to_rssi ( rf95 . getRSSI ( ) ) ;
if ( snr > 99 ) snr = 99 ; // snr will not go upper 31
else if ( snr < - 99 ) snr = - 99 ; // snr will not go below -32
if ( rssi > 0 ) rssi = 0 ; // rssi is always negative
@ -2581,7 +2577,7 @@ char *encode_snr_rssi_in_path()
char * add_element_to_path ( const char * data , const char * element )
{
static char buf [ BG_RF95 _MAX_MESSAGE_LEN+ 1 ] ;
static char buf [ RADIOLIB_SX127X _MAX_MESSAGE_LEN+ 1 ] ;
if ( strlen ( data ) + 1 /* ',' */ + strlen ( element ) + 1 /* '*' */ > sizeof ( buf ) - 1 )
return 0 ;
char * p = strchr ( data , ' > ' ) ;
@ -2630,7 +2626,7 @@ char *add_element_to_path(const char *data, const char *element)
// append element to path, regardless if it will exceed max digipeaters. It's for snr encoding for aprs-is. We don't use
// add_element_to_path, because we will append at the last position, and do not change digipeated bit.
char * append_element_to_path ( const char * data , const char * element ) {
static char buf [ BG_RF95 _MAX_MESSAGE_LEN+ 10 + 1 ] ;
static char buf [ RADIOLIB_SX127X _MAX_MESSAGE_LEN+ 10 + 1 ] ;
if ( strlen ( data ) + 1 /* ',' */ + strlen ( element ) > sizeof ( buf ) - 1 )
return 0 ;
char * p = strchr ( data , ' > ' ) ;
@ -2666,7 +2662,7 @@ struct ax25_frame {
struct ax25_frame * tnc_format_to_ax25_frame ( const char * s )
{
static char data [ BG_RF95 _MAX_MESSAGE_LEN+ 1 ] ;
static char data [ RADIOLIB_SX127X _MAX_MESSAGE_LEN+ 1 ] ;
static struct ax25_frame frame ;
char * p ;
char * q ;
@ -3822,14 +3818,17 @@ out:
}
# endif // KISS_PROTOCOL
// sema lock for lora chip operations
# ifdef IF_SEMAS_WOULD_WORK
if ( xSemaphoreTake ( sema_lora_chip , 100 ) = = pdTRUE ) {
# else
if ( ! sema_lora_chip ) {
sema_lora_chip = true ;
# endif
if ( rf95 . waitAvailableTimeout ( 100 ) ) {
if ( received_flag = = true ) {
// disable the interrupt service routine while
// processing the data
enable_interrupt = false ;
// reset flag
received_flag = false ;
//get current packet size
int packetSize = rf95 . getPacketLength ( ) ;
# ifdef T_BEAM_V1_0
# ifdef ENABLE_LED_SIGNALING
axp . setChgLEDMode ( AXP20X_LED_LOW_LEVEL ) ;
@ -3842,16 +3841,11 @@ out:
// we need to read the received packt, even if rx is set to disable. else rf95.waitAvailableTimeout(100) will always show, data is available
//byte array
byte lora_RXBUFF [ BG_RF95_MAX_MESSAGE_LEN ] ; //buffer for packet to send
uint8_t loraReceivedLength = sizeof ( lora_RXBUFF ) ; // (implicit ) reset max length before receiving!
boolean lora_rx_data_available = rf95 . recvAPRS ( lora_RXBUFF , & loraReceivedLength ) ;
// release lock here. We read the data from the lora chip. And we may call later loraSend (which should not be blocked by ourself)
# ifdef IF_SEMAS_WOULD_WORK
xSemaphoreGive ( sema_lora_chip ) ;
# else
sema_lora_chip = false ;
# endif
byte lora_RXBUFF [ RADIOLIB_SX127X_MAX_MESSAGE_LEN ] ; //buffer for packet to send
//uint8_t loraReceivedLength = sizeof(lora_RXBUFF); // (implicit ) reset max length before receiving!
//boolean lora_rx_data_available = rf95.readData(lora_RXBUFF, loraReceivedLength);
const char * rssi_for_path = encode_snr_rssi_in_path ( ) ;
// always needed (even if rx is disabled)
@ -3862,7 +3856,9 @@ out:
lora_packets_received_in_timeslot_on_secondary_freq + + ;
}
if ( lora_rx_enabled & & lora_rx_data_available ) {
if ( lora_rx_enabled ) {
if ( packetSize > 0 ) {
rf95 . readData ( lora_RXBUFF , packetSize ) ;
String loraReceivedFrameString ; //data on buff is copied to this string. raw
# if defined(ENABLE_SYSLOG)
String loraReceivedFrameString_for_syslog ; //data on buff is copied to this string. Non-printable characters are shown as <0xnn>. Even valid EOL \r. Syslog is for analyzing.
@ -3870,7 +3866,7 @@ out:
String loraReceivedFrameString_for_weblist ; //data on buff is copied to this string. Bad characters like \n and \0 are replaced by ' ' (also valid \r - aprs does not use two line messages ;); \r, \n or \0 at the end of the string are removed.
char * s = 0 ;
for ( int i = 0 ; i < loraReceivedLength ; i + + ) {
for ( int i = 3 ; i < packetSize ; i + + ) {
loraReceivedFrameString + = ( char ) lora_RXBUFF [ i ] ;
# if defined(ENABLE_WIFI) // || defined(ENABLE_SYSLOG)
if ( lora_RXBUFF [ i ] > = 0x20 ) {
@ -3968,11 +3964,11 @@ out:
writedisplaytext ( " ((RX)) " , " " , loraReceivedFrameString , " " , " " , " " ) ;
time_to_refresh = millis ( ) + showRXTime ;
# ifdef ENABLE_WIFI
sendToWebList ( loraReceivedFrameString_for_weblist , bg_rf95rssi_to_rssi ( rf95 . lastRssi ( ) ) , bg_rf95snr_to_snr ( rf95 . las tSNR( ) ) ) ;
sendToWebList ( loraReceivedFrameString_for_weblist , bg_rf95rssi_to_rssi ( rf95 . getRSSI ( ) ) , bg_rf95snr_to_snr ( rf95 . ge tSNR( ) ) ) ;
# endif
# endif
# if defined(ENABLE_SYSLOG) && defined(ENABLE_WIFI) // unfortunately, on this plattform we only have IP if we have WIFI
syslog_log ( LOG_INFO , String ( " LoRa-RX: ' " ) + loraReceivedFrameString_for_syslog + " ', RSSI: " + bg_rf95rssi_to_rssi ( rf95 . lastRssi ( ) ) + " , SNR: " + bg_rf95snr_to_snr ( rf95 . las tSNR( ) ) ) ;
syslog_log ( LOG_INFO , String ( " LoRa-RX: ' " ) + loraReceivedFrameString_for_syslog + " ', RSSI: " + bg_rf95rssi_to_rssi ( rf95 . getRSSI ( ) ) + " , SNR: " + bg_rf95snr_to_snr ( rf95 . ge tSNR( ) ) ) ;
# endif
# ifdef KISS_PROTOCOL
s = 0 ;
@ -4031,14 +4027,10 @@ invalid_packet:
# else
; // make compiler happy
# endif
} else {
# ifdef IF_SEMAS_WOULD_WORK
xSemaphoreGive ( sema_lora_chip ) ;
# else
sema_lora_chip = false ;
# endif
}
rf95 . startReceive ( ) ;
enable_interrupt = true ;
}
}
if ( lora_rx_enabled & & rx_on_frequencies = = 3 & & lora_digipeating_mode < 2 ) {
static uint8_t slot_table [ 9 ] [ 10 ] = {
@ -4082,7 +4074,7 @@ invalid_packet:
// avoid calling rf95.setFrequency() and lora_set_speed() if previos *p_curr_slot_table was the same freq/speed
if ( * p_curr_slot_table ! = ( ( p_curr_slot_table > curr_slot_table ) ? p_curr_slot_table [ - 1 ] : curr_slot_table [ 9 ] ) ) {
lora_freq_rx_curr = ( * p_curr_slot_table ) ? lora_freq_cross_digi : lora_freq ;
rf95 . setFrequency ( lora_freq_rx_curr ) ;
//rf95.setFrequency(lora_freq_rx_curr);
lora_speed_rx_curr = ( * p_curr_slot_table ) ? lora_speed_cross_digi : lora_speed ;
lora_set_speed ( lora_speed_rx_curr ) ;
}