Persistent errors with RFM69 reception -- can't change bitrate

Started by e.coli, April 23, 2024, 05:29:57 PM

e.coli

I'm working with RFM69 modules and still in the learning stage. I am using the radios with a customized Arduino that has an RFID reader built in. The RFID reader uses interrupts, so I have to avoid interrupts when using the RFM69--Or at least I think I do. So I modified some code from an RFM69 library to enable minimal communications (see code below). Things seemed to work but then I noticed that I would get persistent errors (usually just the last bit of a few bytes) on some hardware setups. For example here's a message received from the same transmission on two circuit boards running the same code:

On circuit board 1:

-----GOT INCOMING MESSAGE-----
DEC    0 16 22 218 126 3 0 0 7 0 68 49 48 49
HEX    0 10 16 DA 7E 3 0 0 7 0 45 31 30 31
Check  0 17 39 1 127 130 130 130 137 138 207 0 48 97
Received checksum = 97

On circuit board 2:

-----GOT INCOMING MESSAGE-----
DEC    0 16 23 219 127 3 0 0 7 0 68 48 48 48
HEX    0 10 17 DB 7F 3 0 0 7 0 44 30 30 30
Check  0 16 39 2 129 132 132 132 139 139 207 255 47 95
Received checksum = 96

This is a 14 byte transmission that includes a simple checksum (just adding the bytes). The first set of numbers is correct. The second set has several errors. As far as I can tell the hardware is the same--about half the boards get the errors. Any idea why this happens?

I thought maybe the bitrate was set too high. so I tried to lower it by changing registers 0x03 and 0x04. I found I could go from 250kbps to 200kbps, but I could not get 150 kbps to work. Are there other settings (registers other than 3 and 4) that need to change to accommodate lower bitrates? I'm kind of groping in the dark because I don't understand many of the radio features (e.g. Guassan filtering). Thanks for any insights.

Code for receiving.... The main loop just polls for a high level on the radio GPIO-0 pin. When that happens it calls a function that reads the FIFO and prints the data. I've left a lot of the housekeeping code out.

void setup() {
     //most of this is omitted.
     radioSetup(nodeID, nodeID, 0xAA, 0xAA);
}

void loop() { // Main code is here, it loops forever:
     delay(5);
     if(digitalRead(Radio_Message_In)) {
         radioLogAndRelay(0);
     }
}


bool radioLogAndRelay(uint8_t motState) { //Called when message flag detected on radio pin.
  for(int ic=0; ic<20; ic++) {cArray1[ic] = 0;}
  if ((motState != 0) && motOn) {motAllStop(10);} //Stop motor if it is running (may need to move this up)
  uint16_t header = radioReadMessage(cArray1); //read in message - don't send ack
  uint8_t len = header >> 8;
  uint8_t rchecksum = header & 0x00FF;
  uint8_t checksum = 0;
  bool success = 0;
  Serial.println(); Serial.println("-----GOT INCOMING MESSAGE-----");   
  Serial.print("DEC    ");
  for(uint8_t ic = 0; ic < len; ic++) {Serial.print(cArray1[ic], DEC); Serial.print(" ");}
  Serial.println(); Serial.print("HEX    ");
  for(uint8_t ic2 = 0; ic2 < len; ic2++) {Serial.print(cArray1[ic2], HEX); Serial.print(" ");}
  Serial.println(); Serial.print("Check  ");
  for(uint8_t ic3 = 0; ic3 < len; ic3++) {
    checksum = checksum + cArray1[ic3];
    Serial.print(checksum, DEC); Serial.print(" ");
  }
  Serial.println(); Serial.print("Received checksum = "); Serial.println(rchecksum, DEC);
  Serial.println();
  return success;
}

uint16_t radioReadMessage(char buf_rx[]) { 
  //reads a waiting message from the radio buffer
  //In variable length mode the first byte received after the sync word is interpreted as the length of the received packet. The
  //internal length counter is initialized to this received length. The PayloadLength register is set to a value which is greater
  //than the maximum expected length of the received packet. If the received length is greater than the maximum length stored
  //in PayloadLength register the packet is discarded otherwise the complete packet is received. 
  //Note that the length byte itself is not included in its calculation
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));  // gain control of SPI bus
  digitalWrite(Radio_CS, LOW);
  SPI.transfer((0 % 0x7));  //SPI.transfer((RFM69_FIFO % RFM69_READ_REG_MASK));
  uint8_t len = SPI.transfer(0); //first byte is length of message
  len = len-1; //subtract 1, checksum does not count as message
 
  for (uint8_t i = 0; i < len; i++) { // read in message
    buf_rx[i] = SPI.transfer(0); //read in each byte of message
    //Serial.print(buf_rx[i]); Serial.println(" ");  
  }
  uint8_t cs = SPI.transfer(0); //read in checksum
  Serial.print("received len "); Serial.print(len);
  Serial.print(". checksum "); Serial.println(cs);
  digitalWrite(Radio_CS, HIGH);
  SPI.endTransaction();    // release the SPI bus
  return ((len<<8)+cs);  //Length byte and checksum returned as 16 bit integer.
}

void radioSetup(uint8_t sync1, uint8_t sync2, uint8_t sync3, uint8_t sync4) {
  radioReset(Radio_Reset); // send the RFM69 a hard-reset.
  //settings rendered by rfm.setRecommended(), with modifications
  radioWriteRegister(0x18, 0x08); //Set regLNA to turn on LnaZin and have  gain set by the internal AGC loop
  radioWriteRegister(0x29, 0x64); //setRSSIThreshold (trial & error)
  radioWriteRegister(0x2D, 0x04); //4 LSB for preamble bytes. default = 3; MSB defaults to 0
  radioWriteRegister(0x2E, B10011000);  //Sync on; FifoFillCondition = 0;  3 sync bytes (or maybe 4??), no tolerance
  radioWriteRegister(0x6F, 0x30); //setContinuousDagc                   0               0
  radioWriteRegister(0x02, 0x01); //set DataModul to one to turn on guassan filter
  radioWriteRegister(0x3C, 0x7F & 3 ); //FIFO threshold; first bit zero -> TX when fifo over threshold. other bits = threshold (need to experiment)
  radioWriteRegister(0x19, 0xE0);  //Channel filtering?? DccFreq = B010; RxBwMant = B10, RxBwExp = B101
  radioWriteRegister(0x1A, 0xE0);  //Channel filtering?? DccFreqAfc = B111, RxBwMantAfc = B01, RxBwExpAfc = B000
  radioWriteRegister(0x13, 0x1A); //setPALevel...
  radioWriteRegister(0x11, 0x7F); //setPALevel: power almost at max
  radioWriteRegister(0x07, 0x6C);  //Frequency MSB for 434,000,000 Mhz
  radioWriteRegister(0x08, 0x80);  //Frequency MidSB for 434,000,000 Mhz
  radioWriteRegister(0x09, 0x00);  //Frequency LSB for 434,000,000 Mhz

  //Settings from radioHeand and trial & error.
  radioWriteRegister(3, 0x00);       //RegBitrateMsb 0x1A Bit Rate setting, Most Significant Bits
  radioWriteRegister(4, 0xA0);    //RegBitrateLsb 0x0B Bit Rate setting, Least Significant Bits
  radioWriteRegister(5, 0x10);    //RegFdevMsb 0x00 Frequency Deviation setting, Most Significant Bits
  radioWriteRegister(6, 0);       //RegFdevLsb 0x52 Frequency Deviation setting, Least Significant Bits
  radioWriteRegister(0x23, 0x0);  //RegRssiConfig 0x02 RSSI-related settings
  radioWriteRegister(0x24, 0xBE); //RegRssiValue 0xFF RSSI value in dBm
  radioWriteRegister(0x25, 0x40); //RegDioMapping1 0x00 Mapping of pins DIO0 to DIO3
  radioWriteRegister(0x27, 0xD8); //0x27 RegIrqFlags1 0x80 Status register: PLL Lock state, Timeout, RSSI > Threshold...
  radioWriteRegister(0x2F, sync1); //registers 0x2F-0x36 Sync values - change these to define network
  radioWriteRegister(0x30, sync2);  //Sync
  radioWriteRegister(0x31, sync3);  //Sync
  radioWriteRegister(0x32, sync4);  //Sync
  radioWriteRegister(0x37, 0xD0); //packet config, Variable length, whitening on, CRC on
  radioWriteRegister(0x38, 0x20); //RFM69_PAYLOAD_LENGTH

  //Final settings
  radioWriteRegister(0x3D, 0); //no interpacket delay, no auto RX restart, no AES (encryption)
  radioWriteRegister(0x3C, 0x01); //minimum package threshold
  radioWriteRegister(0x3B, B00000000);  //Automode setting - turn it off
  radioWriteRegister(0x5A, 0x55); // RFM69_TEST_PA1: Normal mode and Rx mode
  radioWriteRegister(0x5C, 0x70); //RFM69_TEST_PA2: Normal mode and Rx mode

  radioWriteRegister(0x01, B00010000); //set to receiver mode

  delay(5); //let setup take effect....
}

void radioReset(uint8_t pin) { // function to send the RFM69 a hardware reset.
  pinMode(pin, OUTPUT);
  digitalWrite(pin, HIGH);
  delayMicroseconds(150); // pull high for >100 uSec
  pinMode(pin, INPUT); // release
  delay(10); //  wait 10 milliseconds before SPI is possible.
}




Felix

But why mess with SPI transactions when there's a library with working examples that will give you a head start?
Try the node and gateway examples, they will work with a good hardware setup (and antenna!), just match the settings for the radio module type and frequency.
Then once you have those you can worry about the rest of the hardware and narrow it down easier when you run into any issues.

e.coli

Thanks Felix.

I did start with the libraries. I tried three of them including low power labs. And they did give me a head start. I learned the basics through the examples and the initial settings for the radio came from one of the libraries.

My problem is that the RFID reading part of the code needs interrupts and I can't have the Radio using them as well. From what I can tell, all of the libraries set up interrupts for incoming messages, and I didn't see a simple way to turn them off without modifying the library. Also I have to share this code with non-programmers, and if I can avoid having them download and modify libraries it makes my life easier.

e.coli

One thing I found re the transmission errors, is that they may be linked to having and SD card on the same SPI bus as the radio. That is to say, if I remove the SD card the errors go away. So maybe transmission is not the problem. It's the SD card compromising the SPI bus. I've rad about this issue elsewhere.... Anyway, I don't need the SD card most of the time, so I can just leave it out. I'm still baffled about chaning bitrates though.

Felix

Bitrates are changed through SPI as well. So ensure you have an unobstructed SPI bus before you do anything else.