jgromes / RadioLib

Universal wireless communication library for embedded devices
https://jgromes.github.io/RadioLib/
MIT License
1.54k stars 384 forks source link

First Byte Missing in FSK OOK Receive with SX1276 #174

Closed hansgaensbauer closed 4 years ago

hansgaensbauer commented 4 years ago

I am using the SX1276RF1KAS and an arduino uno to receive FSK OOK packets, with the following code:

//Modified version of the SX127x_FSK_Modem sketch

// include the library
#include <RadioLib.h>

// SX1276 has the following connections:
// NSS pin:   10
// DIO0 pin:  2
// RESET pin: 9
// DIO1 pin:  3
SX1276 radio = new Module(10, 2, 9, 3);
uint8_t* data = new uint8_t[21];

void setup() {
  Serial.begin(9600);
  Serial.println("Arduino Reset");
  // initialize SX1278 FSK modem with default settings
  Serial.print(F("[SX1278] Initializing ... "));
  int state = radio.beginFSK();
  if (state == ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
  state = radio.setFrequency(915);
  state = radio.setBitRate(4.8);
  state = radio.setFrequencyDeviation(5);
  state = radio.setRxBandwidth(5);
  state = radio.setOutputPower(10.0);
  state = radio.setCurrentLimit(100);
  state = radio.disableAddressFiltering(); 
  state = radio.setPreambleLength(5);
  uint8_t syncWord[] = {0x69, 0x81, 0x7E, 0x96};
  state = radio.setSyncWord(syncWord, 4);
  if (state != ERR_NONE) {
    Serial.print(F("Unable to set configuration, code "));
    Serial.println(state);
    while (true);
  }
  state = radio.setOOK(true);
  state = radio.fixedPacketLengthMode(8);
  state = radio.setCRC(false);
  state = radio.setEncoding(RADIOLIB_ENCODING_NRZ);
  state = radio.setDataShapingOOK(1);
  if (state != ERR_NONE) {
    Serial.print(F("Unable to change modulation, code "));
    Serial.println(state);
    while (true);
  }
}

void loop() {

  // receive FSK packet
  int state = radio.startReceive(20, SX127X_RX);
  //wait for packet reception
  while(!digitalRead(2));
  //read the received data
  state = radio.readData(data, 20);
  //print the hex data to the serial monitor
  for(int i = 0; i<20; i++){
    Serial.print(data[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
  //wait 100ms
  delay(100);
}

When I transmit 8 bytes, I receive them all except for the first byte. However, if I set the sync word length to 3 instead of 4 (while still transmitting a 4 byte sync word), the byte is received correctly. Looking at the SX1276 datasheet, I have to guess that the byte right after the sync word is being interpreted as an address, even though I am not using address filtering. Is that correct, or have I just made a mistake somewhere else (extremely possible)? If so, is there a way to change this?

Originally, I was using the following from the SX127x_FSK_Modem sketch to receive the message "hi there":

// receive FSK packet
String str;
state = radio.receive(str);
Serial.println(str)

The serial monitor would print "i there" until I changed the sync word length to 3.

It seems like everything else is working fine. Thank you so much!

jgromes commented 4 years ago

Couple of things to check out before I get to try this myself:

  1. You're not checking state correctly - when you're changing configuration/modulation, you overwrite the result of the previous request, so when you do:
  state = radio.setFrequency(915);
  state = radio.setBitRate(4.8);
  state = radio.setFrequencyDeviation(5);
  state = radio.setRxBandwidth(5);
  state = radio.setOutputPower(10.0);
  state = radio.setCurrentLimit(100);
  state = radio.disableAddressFiltering(); 
  state = radio.setPreambleLength(5);
  uint8_t syncWord[] = {0x69, 0x81, 0x7E, 0x96};
  state = radio.setSyncWord(syncWord, 4);
  if (state != ERR_NONE) {
    Serial.print(F("Unable to set configuration, code "));
    Serial.println(state);
    while (true);
  }

you're only checking the result of radio.setSyncWord(syncWord, 4);. Any of the previous methods may have failed, and you wouldn't know it. It's not likely to be the cause, just to make sure.

  1. Next, is it only cause by sync word length, or some other parameter? Try to remove all the extra configuration, and leave only the changed sync word. If the issue persists, we can be pretty sure it's caused by the sync word. If not, then it's something else in combination with the sync word. My guess would be the other parameters that change the packet structure - fixed/variable packet length mode, address filtering and preamble length.
hansgaensbauer commented 4 years ago

Thank you for your response! I've tried your suggestions, here are the results. First, I pasted the state check if statement after every configuration/modulation change, none of them failed. I tested various combinations of the transmitter and receiver packet length mode. I made a sort of truth table describing the combinations I tried and the results they gave:

Receiver Packet Length Mode Transmitted Packet Length Mode Result
Fixed Fixed Missing 1st Byte
Fixed Variable Correctly Received
Variable Fixed Not Received
Variable Variable Correctly Received

I also tested the other parameters (address filtering, data encoding, CRC calculation, and preamble length) but they didn't make a difference so I didn't include them in the table.

This is similar to the behavior that originally lead me to believe that the first byte was being interpreted as an address, but this would suggest that the missing byte is being interpreted as a packet length instead. Also, when the packet is "Not Received", it doesn't show an error, nothing is printed to the serial monitor. I'm using the following code:

void loop() {

  // receive FSK packet
  int state = radio.startReceive(20, SX127X_RX);
  if (state != ERR_NONE) {
    Serial.print(F("Error, code "));
    Serial.println(state);
  }
  //wait for packet reception
  while(!digitalRead(2));
  //read the received data
  state = radio.readData(data, 20);
  if (state != ERR_NONE) {
    Serial.print(F("Error, code "));
    Serial.println(state);
  }
  //print the hex data to the serial monitor
  for(int i = 0; i<20; i++){
    Serial.print(data[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
  //wait 100ms
  delay(100);
}

Also, I often get Error -16 while receiving, even if the majority of the time the correct output is printed to the serial monitor. I'm not sure if this is related or just a problem with the logic level shifter and mess of jumper wires I'm using to connect the two. The part is rated for a much higher bitrate than the SPI is using. The serial monitor looks like this (this is variable length packet sent, variable packet length mode):

68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
Error, code -16
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
Error, code -16
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
68 69 69 69 69 6D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

Thanks again!

jgromes commented 4 years ago

I think I know what the issue is - it seems like the packet mode isn't checked in SX127x::getPacketLength() method, and is always treated as variable length, that's why the first byte is missing.

I hvane't had the time to test this with hardware, but could you go to RadioLib/src/modules/SX127x/SX127x.ccp and do the following changes:

line 456: change

_mod->SPIwriteRegister(SX127X_REG_FIFO, len);

to

if (_packetLengthConfig == SX127X_PACKET_VARIABLE) {
  _mod->SPIwriteRegister(SX127X_REG_FIFO, len);
}

and line 868, change

_packetLength = _mod->SPIreadRegister(SX127X_REG_FIFO);

to

if (_packetLengthConfig == SX127X_PACKET_VARIABLE) {
  _packetLength = _mod->SPIreadRegister(SX127X_REG_FIFO);
} else {
  _packetLength = _mod->SPIreadRegister(SX127X_REG_PAYLOAD_LENGTH_FSK);
}
jgromes commented 4 years ago

Checking with two SX1272 modules, it does appear that was indeed the issue, it should be fixed now.