Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
9 years, 1 month ago.
Continuous setup works with LoRa, not with FSK
Hi
The SX1276PingPong program is working flawlessly with the FRDM-K64F and the LoRa mbed shield. Wonderful! Now, I would like to operate in continuous mode (having one machine only as TX and the other as RX) using the same state machine approach. What I've come up with only works when using LoRa (Great!), but fails with FSK. I want to be able to switch between FSK and LoRa. Any idea why this doesn't work with both FSK and LoRa?
My code is as follows (I only made minor terminal output changes, adding of LEDs for the K64F board and then a rewritten statemachine, compared to the PingPong program)
#include "mbed.h" #include "main.h" #include "sx1276-hal.h" #include "debug.h" /* Set this flag to '1' to display debug messages on the console */ #define DEBUG_MESSAGE 1 /* Set this flag to '1' to use the LoRa modulation or to '0' to use FSK modulation */ #define USE_MODEM_LORA 0 #define USE_MODEM_FSK !USE_MODEM_LORA #define RF_FREQUENCY 868000000 // Hz #define TX_OUTPUT_POWER 14 // 14 dBm #if USE_MODEM_LORA == 1 #define LORA_BANDWIDTH 2 // [0: 125 kHz, // 1: 250 kHz, // 2: 500 kHz, // 3: Reserved] #define LORA_SPREADING_FACTOR 7 // [SF7..SF12] #define LORA_CODINGRATE 1 // [1: 4/5, // 2: 4/6, // 3: 4/7, // 4: 4/8] #define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx #define LORA_SYMBOL_TIMEOUT 5 // Symbols #define LORA_FIX_LENGTH_PAYLOAD_ON false #define LORA_FHSS_ENABLED false #define LORA_NB_SYMB_HOP 4 #define LORA_IQ_INVERSION_ON false #define LORA_CRC_ENABLED true #elif USE_MODEM_FSK == 1 #define FSK_FDEV 25000 // Hz #define FSK_DATARATE 19200 // bps #define FSK_BANDWIDTH 50000 // Hz #define FSK_AFC_BANDWIDTH 83333 // Hz #define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx #define FSK_FIX_LENGTH_PAYLOAD_ON false #define FSK_CRC_ENABLED true #else #error "Please define a modem in the compiler options." #endif #define RX_TIMEOUT_VALUE 3500000 // in us #define BUFFER_SIZE 32 // Define the payload size here DigitalOut led_R(LED_RED); // PTB22 = Red pin DigitalOut led_G(LED_GREEN); // PTE26 = Green pin DigitalOut led_B(LED_BLUE); // PTB21 = Blue pin Timer t; Timer sys_t; Serial pc(USBTX, USBRX); /* * Global variables declarations */ typedef RadioState States_t; volatile States_t State = LOWPOWER; SX1276MB1xAS Radio( OnTxDone, OnTxTimeout, OnRxDone, OnRxTimeout, OnRxError, NULL, NULL ); const uint8_t PingMsg[] = "PING"; const uint8_t PongMsg[] = "PONG"; uint16_t BufferSize = BUFFER_SIZE; uint8_t Buffer[BUFFER_SIZE]; int16_t RssiValue = 0.0; int16_t i_rssi = 0; int8_t SnrValue = 0.0; bool txRxSwitch = true; //true for TX, false for RX int main() { uint8_t _i; // turn off LEDs led_R = 1; led_G = 1; led_B = 1; debug( "\n\n\r SX1276 Ping Pong Demo Application \n\n\r" ); // verify the connection with the board while( Radio.Read( REG_VERSION ) == 0x00 ) { debug( "Radio could not be detected!\n\r", NULL ); wait( 1 ); } debug_if( ( DEBUG_MESSAGE & ( Radio.DetectBoardType( ) == SX1276MB1LAS ) ) , "\n\r > Board Type: SX1276MB1LAS < \n\r" ); debug_if( ( DEBUG_MESSAGE & ( Radio.DetectBoardType( ) == SX1276MB1MAS ) ) , "\n\r > Board Type: SX1276MB1MAS < \n\r" ); Radio.SetChannel( RF_FREQUENCY ); #if USE_MODEM_LORA == 1 debug_if( LORA_FHSS_ENABLED, "\n\n\r > LORA FHSS Mode < \n\n\r"); debug_if( !LORA_FHSS_ENABLED, "\n\n\r > LORA Mode < \n\n\r"); Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, LORA_IQ_INVERSION_ON, 2000000 ); Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, LORA_IQ_INVERSION_ON, true ); #elif USE_MODEM_FSK == 1 debug("\n\n\r > FSK Mode < \n\n\r"); Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, FSK_DATARATE, 0, FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, FSK_CRC_ENABLED, 0, 0, 0, 2000000 ); Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, 0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, 0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, FSK_CRC_ENABLED, 0, 0, false, true ); #else #error "Please define a modem in the compiler options." #endif debug_if( DEBUG_MESSAGE, "Starting Ping-Pong loop\r\n" ); // Clear screen pc.printf( "\033[2J" ); led_R = 0; t.start(); sys_t.start(); if( txRxSwitch == true ) // transmit TX { Radio.Tx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); pc.printf( "\033[12;30H\033[K\033[12;30H" ); pc.printf( "TX" ); } else // receive RX { Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); pc.printf( "\033[12;30H\033[K\033[12;30H" ); pc.printf( "RX" ); } while( 1 ) { switch( State ) { case TX: // TX mode // Send the next PING frame. // Copy PingMsg content into Buffer strcpy( ( char* )Buffer, ( char* )PingMsg ); // We fill the rest of buffer with numbers for the payload for( _i = 4; _i < BufferSize; _i++ ) { Buffer[_i] = _i - 4; } wait_ms( 10 ); Radio.Send( Buffer, BufferSize ); pc.printf( "\033[18;30H\033[K\033[18;30H" ); pc.printf( "%s", Buffer ); State = LOWPOWER; break; case RX: // RX mode if( BufferSize > 0 ) { if (strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0) { pc.printf( "\033[18;30H" ); pc.printf( "%s", Buffer ); strcpy( ( char* )Buffer, ( char* )PongMsg ); // Make Buffer invalid so as to only count packages when new received } } State = LOWPOWER; break; case RX_TIMEOUT: Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); State = LOWPOWER; break; case RX_ERROR: Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); State = LOWPOWER; break; case LOWPOWER: /*! * Update screen with new values every ~0.5s */ if ( txRxSwitch == true ) // TX { if( t.read() > 0.5 ) { // light LED led_B = !led_B; t.reset(); } Radio.Tx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); } else // RX { if( t.read() > 0.5 ) { // light LED led_G = !led_G; RssiValue = 0.0; i_rssi = 0; t.reset(); } Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); } break; default: State = LOWPOWER; break; } }// while( 1 ) }// main() void OnTxDone( void ) { Radio.Sleep( ); State = TX; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnTxDone" ); } void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { Radio.Sleep( ); BufferSize = size; memcpy( Buffer, payload, BufferSize ); RssiValue = ( rssi + i_rssi * RssiValue ) / ( i_rssi + 1 ); // Calculate cumulative moving average RSSI i_rssi++; SnrValue = snr; State = RX; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRxDone" ); } void OnTxTimeout( void ) { Radio.Sleep( ); State = TX_TIMEOUT; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnTxTimeout" ); } void OnRxTimeout( void ) { Radio.Sleep( ); Buffer[ BufferSize ] = 0; State = RX_TIMEOUT; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRxTimeout" ); } void OnRxError( void ) { Radio.Sleep( ); State = RX_ERROR; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRx_Error" ); }
Question relating to:
2 Answers
9 years, 1 month ago.
I've modified the code to deal with RX and TX timeout issues. I've successfully tested this code in both Lora and FSK mode.
Updated to fix RX/TX timeout issues
#include "mbed.h" #include "main.h" #include "sx1276-hal.h" #include "debug.h" /* Set this flag to '1' to display debug messages on the console */ #define DEBUG_MESSAGE 1 /* Set this flag to '1' to use the LoRa modulation or to '0' to use FSK modulation */ #define USE_MODEM_LORA 1 #define USE_MODEM_FSK !USE_MODEM_LORA #define RF_FREQUENCY 868000000 // Hz #define TX_OUTPUT_POWER 14 // 14 dBm #if USE_MODEM_LORA == 1 #define LORA_BANDWIDTH 1 // [0: 125 kHz, // 1: 250 kHz, // 2: 500 kHz, // 3: Reserved] #define LORA_SPREADING_FACTOR 7 // [SF7..SF12] #define LORA_CODINGRATE 1 // [1: 4/5, // 2: 4/6, // 3: 4/7, // 4: 4/8] #define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx #define LORA_SYMBOL_TIMEOUT 5 // Symbols #define LORA_FIX_LENGTH_PAYLOAD_ON false #define LORA_FHSS_ENABLED false #define LORA_NB_SYMB_HOP 4 #define LORA_IQ_INVERSION_ON false #define LORA_CRC_ENABLED true #elif USE_MODEM_FSK == 1 #define FSK_FDEV 25000 // Hz #define FSK_DATARATE 19200 // bps #define FSK_BANDWIDTH 50000 // Hz #define FSK_AFC_BANDWIDTH 83333 // Hz #define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx #define FSK_FIX_LENGTH_PAYLOAD_ON false #define FSK_CRC_ENABLED true #else #error "Please define a modem in the compiler options." #endif #define RX_TIMEOUT_VALUE 3500000 // in us #define BUFFER_SIZE 32 // Define the payload size here DigitalOut led_R(LED_RED); // PTB22 = Red pin DigitalOut led_G(LED_GREEN); // PTE26 = Green pin DigitalOut led_B(LED_BLUE); // PTB21 = Blue pin Timer t; Timer sys_t; Serial pc(USBTX, USBRX); /* * Global variables declarations */ typedef RadioState States_t; volatile States_t State = LOWPOWER; SX1276MB1xAS Radio( OnTxDone, OnTxTimeout, OnRxDone, OnRxTimeout, OnRxError, NULL, NULL ); const uint8_t PingMsg[] = "PING"; const uint8_t PongMsg[] = "PONG"; uint16_t BufferSize = BUFFER_SIZE; uint8_t Buffer[BUFFER_SIZE]; int16_t RssiValue = 0.0; int16_t RssiInst = 0; int16_t i_rssi = 0; int8_t SnrValue = 0.0; float Frequency; bool txRxSwitch = false; //true for TX, false for RX int main() { uint8_t _i; Frequency = RF_FREQUENCY; // turn off LEDs led_R = 1; led_G = 1; led_B = 1; debug( "\n\n\r SX1276 Ping Pong Demo Application \n\n\r" ); // verify the connection with the board while( Radio.Read( REG_VERSION ) == 0x00 ) { debug( "Radio could not be detected!\n\r", NULL ); wait( 1 ); } debug_if( ( DEBUG_MESSAGE & ( Radio.DetectBoardType( ) == SX1276MB1LAS ) ) , "\n\r > Board Type: SX1276MB1LAS < \n\r" ); debug_if( ( DEBUG_MESSAGE & ( Radio.DetectBoardType( ) == SX1276MB1MAS ) ) , "\n\r > Board Type: SX1276MB1MAS < \n\r" ); Radio.SetChannel( RF_FREQUENCY ); #if USE_MODEM_LORA == 1 debug_if( LORA_FHSS_ENABLED, "\n\n\r > LORA FHSS Mode < \n\n\r"); debug_if( !LORA_FHSS_ENABLED, "\n\n\r > LORA Mode < \n\n\r"); Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, LORA_IQ_INVERSION_ON, 2000000 ); Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, LORA_IQ_INVERSION_ON, true ); #elif USE_MODEM_FSK == 1 debug("\n\n\r > FSK Mode < \n\n\r"); Radio.SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, FSK_DATARATE, 0, FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, FSK_CRC_ENABLED, 0, 0, 0, 2000000 ); Radio.SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, 0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, 0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, FSK_CRC_ENABLED, 0, 0, false, true ); #else #error "Please define a modem in the compiler options." #endif debug_if( DEBUG_MESSAGE, "Starting Ping-Pong loop\r\n" ); // Clear screen pc.printf( "\033[2J" ); led_R = 0; t.start(); sys_t.start(); if( txRxSwitch == true ) // transmit TX { Radio.Tx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); pc.printf( "\033[12;30H\033[K\033[12;30H" ); pc.printf( "TX" ); } else // receive RX { Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); pc.printf( "\033[12;30H\033[K\033[12;30H" ); pc.printf( "RX" ); } while( 1 ) { switch( State ) { case TX: case TX_TIMEOUT: // TX mode // Send the next PING frame. // Copy PingMsg content into Buffer strcpy( ( char* )Buffer, ( char* )PingMsg ); // We fill the rest of buffer with numbers for the payload for( _i = 4; _i < BufferSize; _i++ ) { Buffer[_i] = _i - 4; } wait_ms( 10 ); Radio.Send( Buffer, BufferSize ); //pc.printf( "\033[18;30H\033[K\033[18;30H" ); pc.printf( "\033[18;30H" ); pc.printf( "%s", Buffer ); Radio.Tx( RX_TIMEOUT_VALUE ); State = LOWPOWER; //wait(1); break; case RX: // RX mode if( BufferSize > 0 ) { if (strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0) { pc.printf( "\033[18;30H" ); pc.printf( "%s", Buffer); strcpy( ( char* )Buffer, ( char* )PongMsg ); // Make Buffer invalid so as to only count packages when new received } } Radio.Rx( RX_TIMEOUT_VALUE ); State = LOWPOWER; break; case RX_TIMEOUT: Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); State = LOWPOWER; break; case RX_ERROR: Radio.Rx( RX_TIMEOUT_VALUE ); wait_ms( 20 ); State = LOWPOWER; break; case LOWPOWER: /*! * Update screen with new values every ~0.5s */ if ( txRxSwitch == true ) // TX { if( t.read() > 0.5 ) { // light LED led_B = !led_B; t.reset(); } wait_ms( 20 ); } else // RX { if( t.read() > 4.5 ) { // light LED led_G = !led_G; RssiValue = 0.0; i_rssi = 0; t.reset(); } wait_ms( 20 ); } break; default: State = LOWPOWER; break; } }// while( 1 ) }// main() void OnTxDone( void ) { Radio.Sleep( ); State = TX; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnTxDone" ); } void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { Radio.Sleep( ); BufferSize = size; memcpy( Buffer, payload, BufferSize ); RssiInst = rssi; RssiValue = ( rssi + i_rssi * RssiValue ) / ( i_rssi + 1 ); // Calculate cumulative moving average RSSI i_rssi++; SnrValue = snr; State = RX; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRxDone" ); } void OnTxTimeout( void ) { Radio.Sleep( ); State = TX_TIMEOUT; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnTxTimeout" ); } void OnRxTimeout( void ) { Radio.Sleep( ); Buffer[ BufferSize-1 ] = 0; State = RX_TIMEOUT; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRxTimeout" ); } void OnRxError( void ) { Radio.Sleep( ); State = RX_ERROR; pc.printf( "\033[20;0H\033[K\033[20;0H" ); pc.printf( "> OnRx_Error" ); }
I think you might have a couple of issues with your code, for both lora and fsk modes: 1) I don't think that you'll ever see an RX timeout when in RX mode. When the device is in low power mode, every half second the rx timeout is reset (line 243). Since the timeout value is 3.5 seconds, the affect of resetting it every half second is the disabling of the RX timeout. In my version of your code, I moved setting of the RX timeout to just after line 203 (the case statement handling the RX). In this way, the RX timeout should be set after all possible RX cases. 2) In the OnRxTimeout function, running line 288 crashes on my board. The current code ( Buffer[ BufferSize ] = 0;) is probably trying to write past the end of the array. I think you want Buffer[ BufferSize-1 ] = 0;
This fixed problems I had running in lora mode. When I tried in fsk mode, the TX side never sent the initial packet. I think similar changes are needed for the TX timeout (remove line 230 and add a case statement for TX_TIMEOUT)
posted by Frank Agius 30 Sep 2015Hi Frank
Thanks for your help!
Though, there are some things I'm still not so sure about (I am very new to both mbed, cpp and radio communication systems).
Unfortunately, my program still doesn't work when implementing your suggestions and Gregory's. My problem is that I don't quite understand exactly when the OnTxDone, OnRxDone etc. get called. Say, I want to transmit continuously, what should be done when OnTxTimeout gets called? (this is where the program gets stuck)
Should the program then call the TX state again, or go to the timeout (and then go directly to TX again?) Or some other thing?
Thank you!
posted by Mathias Johnsen 30 Sep 2015The functions OnTxDone, OnRXDone, OnRxTimeout, OnTxTimeout are set as the callback routines in line 67 of your code. When an interrupt occurs, the interrupt handling routines in sx1276.cpp will call the appropriate function. For example, in the case when the board is set up as the transmitter, when the program first starts the tx timeout value is set to 3.5 seconds. Initially, your code will not send a packet, so 3.5 seconds after it starts, there should be a tx timeout interrupt. This interrupt should cause OnTxTimeout to be called. In OnTxTimeout, the State variable is set to TX_TIMEOUT. When OnTxTimeout returns, you'll be in your while (1) loop. In order to send the first packet, you will need to handle in the switch the case for TX_TIMEOUT. You will want to do the same for both TX and TX_TIMEOUT, so you can change "case TX:" to "case TX: case TX_TIMEOUT:". This will allow for the first call to Radio.Send and the first PING packet should be sent. After this first packet is sent, there should be a tx interrupt, which causes OnTxDone to be called. The State variable is set to TX, so when the program gets back to the while loop, the next packet should be sent. At this point, the program should continuously send out packets. I will post my modified code in the answer section. It is working for me in both lora and fsk mode.
posted by Frank Agius 01 Oct 2015Thank you for the thorough explanation. Really helpful!
posted by Mathias Johnsen 01 Oct 2015