jgromes / RadioLib

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

SX127x Missing packets in FSK mode #505

Closed skywodd closed 1 year ago

skywodd commented 2 years ago

Describe the bug When using an SX1276 module in FSK mode with all defaults settings (only frequency is set in beginFSK()), the DIO0 interrupt will only trigger half the time when a packet is sent.

The hardware itself seem to be working properly, by replacing beginFSK(868.3) with begin(868.3) in both TX and RX sketch, everything work as expected in LoRa mode.

To Reproduce

Test procedure:

Observed behavior:

TX sketch :

#include <RadioLib.h>

SX1276 radio = new Module(10, 3, 9, 4);

void setup() {
  Serial.begin(9600);

  int state = radio.beginFSK(868.3);
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Begin failed, code "));
    Serial.println(state);
    while (true);
  }
}

void loop() {
  byte byteArr[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
  int state = radio.transmit(byteArr, 8);
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Transmit failed, code "));
    Serial.println(state);
  }

  delay(3000);
}

RX Sketch :

#include <RadioLib.h>

SX1276 radio = new Module(10, 3, 9, 4);

volatile bool receivedFlag = false;
volatile bool enableInterrupt = true;

void setup() {
  Serial.begin(9600);

  int state = radio.beginFSK(868.3);
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Begin failed, code "));
    Serial.println(state);
    while (true);
  }

  radio.setDio0Action(setFlag);

  state = radio.startReceive();
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Receive failed, code "));
    Serial.println(state);
    while (true);
  }
}

void setFlag(void) {
  if(!enableInterrupt)
    return;

  receivedFlag = true;
}

void loop() {
  if(receivedFlag) {
    enableInterrupt = false;
    receivedFlag = false;

    byte byteArr[8];
    int state = radio.readData(byteArr, 8);
    if (state == RADIOLIB_ERR_NONE) {
      Serial.println(F("Received packet!"));

    } else {
      Serial.print(F("Read failed, code "));
      Serial.println(state);
    }

    radio.startReceive();
    enableInterrupt = true;
  }
}

Expected behavior An interrupt on DIO0 should be triggered after each packet is sent/received in FSK mode.

Additional info:

Debug mode output

Click to expand! ``` R 42 12 M SX127x R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R D 8 W D F R D F R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R 2 1A W 2 1A R 2 1A R 3 B W 3 A R 3 A R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R 4 0 W 4 0 R 4 0 R 5 52 W 5 51 R 5 51 R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R 13 B W 13 2 R 13 2 R 1 9 R D F W D F R D F R 1 9 R D F W D 1F R D 1F R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R 12 15 W 12 2 R 12 2 R 1 9 R 1 9 W 1 9 R 1 9 R B 2B W B 23 R B 23 R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 25 0 W 25 0 R 25 0 R 26 3 W 26 2 R 26 2 R 1 9 R 27 93 W 27 93 R 27 93 R 27 93 W 27 91 R 27 91 W 28 12 AD R 1 9 R 30 90 W 30 90 R 30 90 R 33 0 W 33 0 R 33 0 R 34 0 W 34 0 R 34 0 R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R E 2 W E 2 R E 2 R E 2 W E 2 R E 2 R 1 9 R 30 90 W 30 90 R 30 90 R 1 9 R 30 90 W 30 90 R 30 90 R 32 40 W 32 40 R 32 40 R 10 FF W 10 FF R 10 FF W 3F 10 R 30 90 W 30 90 R 30 90 R 31 40 W 31 40 R 31 40 R 27 91 W 27 B1 R 27 B1 R 35 F W 35 8F R 35 8F R 35 8F W 35 8F R 35 8F R 20 0 W 20 0 R 20 0 R 21 0 W 21 0 R 21 0 R 22 0 W 22 0 R 22 0 R 1F 40 W 1F AA R 1F AA R 44 2D W 44 AD R 44 AD R 24 5 R 6 6C W 6 D9 R 6 D9 R 7 80 W 7 13 R 7 13 R 8 0 W 8 33 R 8 33 R 1 9 R 1 9 W 1 9 R 1 9 R 9 4F W 9 CF R 9 CF R 9 CF W 9 F8 R 9 F8 R 4D 84 W 4D 84 R 4D 84 R 1 9 R 1 9 R 1 9 W 1 9 R 1 9 R A 9 W A 9 R A 9 R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 40 0 W 40 0 R 40 0 R 1 9 W 3E FF W 3F FF R 1 9 R 1 9 W 1 D R 1 C R 1 D R 1 D R 1 D W 1 9 R 1 9 R 1 9 R 0 8 R 30 90 R 0 1 23 45 67 89 AB CD EF R 1 9 W 3E FF W 3F FF Received packet! R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 40 0 W 40 0 R 40 0 R 1 9 W 3E FF W 3F FF R 1 9 R 1 9 W 1 D R 1 C R 1 D R 1 D R 1 D W 1 9 R 1 9 R 1 9 R 0 8 R 30 90 R 0 1 23 45 67 89 AB CD EF R 1 9 W 3E FF W 3F FF Received packet! R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 40 0 W 40 0 R 40 0 R 1 9 W 3E FF W 3F FF R 1 9 R 1 9 W 1 D R 1 C R 1 D R 1 D R 1 D W 1 9 R 1 9 R 1 9 R 0 8 R 30 90 R 0 1 23 45 67 89 AB CD EF R 1 9 W 3E FF W 3F FF Received packet! R 1 9 R 1 9 W 1 9 R 1 9 R 1 9 R 40 0 W 40 0 R 40 0 R 1 9 W 3E FF W 3F FF R 1 9 R 1 9 W 1 D R 1 C R 1 D R 1 D R 1 D W 1 9 R 1 9 R 1 9 R 0 8 R 30 90 R 0 1 23 45 67 89 AB CD EF R 1 9 W 3E FF W 3F FF ```
jgromes commented 2 years ago

I was able to confirm this behavior, using Arduino Uno and 2 SX1278 transceivers. Rolling back to 4.3.0, everything started working correctly again, so the issue was introduced somewhere between 4.3.0 and 4.4.0. Tracking further, I narrowed it down to changes in #282. Finally, I got it down to the call to setAFCAGCTrigger() in beginFSK.

There are 4 possible AFC/AGC trigger configurations, I tried them all in 4.4.0 and here are the results:

I'm not sure what exactly is the root cause here. I tried using SX127X_RX_TRIGGER_RSSI_INTERRUPT to fix the primary issue, and that worked in 4.4.0, but stopped working at all in 5.0.0. I think that's because the bitrate/freqeuncy deviation changed in 5.0.0 from 48k/50kHz to 4k8/5kHz, while the Rx bandwidth remained the same at 125 kHz. To get thinkgs working reliably, I had to both disable AFC and use the RSSI interrupt trigger (which is honestly pretty weird).

I'll use SX127X_RX_TRIGGER_RSSI_INTERRUPT with setAFC(false) to fix the primary issue, but it would be good to know why are some AFC triggers only working once per every two packets. It's worth mentioning that the delay between packets doesn't matter, it looks like the first packet received triggers something that prevents the SX127x from receiving the next one.

@yeckel since you were the one to implement this feature (and the SX127X_RX_TRIGGER_BOTH was used in your PR), do you have any idea what might be causing this behavior?

skywodd commented 2 years ago

Thanks for the workaround, it's working perfectly.

Just out of curiosity, I've read the AFC chapter of the data sheet (and errata), but I don't see anything which can explain a (perfect) 50% packet lost. It's a very strange (hardware?) bug.

jgromes commented 2 years ago

@skywodd I'm guessing one could try asking around on Semtech dev forums, if this is a known limitation or some misunderstanding of the AFC functionality on our part.

I will keep the workaround (i.e. disabled AFC, as it was pre-4.4.0) until we can figure out what's going on here.

parlotto commented 2 years ago

I confirm the same behavior with lora esp32 board. Only one packet other two is received with the code above. I have analyzed the signal received by an rtlsdr with urh (universal radio hacker) and it seems every packet is good but that the checksum is alternating correct/incorrect. When resetting the device between to transmission every packet has the correct checksum and is received.
transmitted data : ascii "ABCD" and 5th is an incrementing counter f2,f3,f4,...
image image image

jgromes commented 2 years ago

@parlotto that's an interesting observation - looks like the last bit of the packet got flipped, from 1 to 0 - or perhaps the packet was terminated prematurely?

Is this happening with the latest release, or did you do some changes to the AFC/AGC triggers?

parlotto commented 2 years ago

@jgromes I use the code out of the box downloaded from github a few weeks ago. I will try again with the official latest release.

parlotto commented 2 years ago

@jgromes now tested with 5.2.0 release : same behavior. SX1278 chip ver 18 I suspect the problem is on the tx side. After every reset of the tx the first packet (byte[4]=0) is always received here is my code :

#include <RadioLib.h>
//TX

// SX1278 has the following connections:
// NSS pin:  18
// DIO0 pin: 26
// RESET pin: 23
// DIO1 pin: 33
SX1278 radio = new Module(18, 26, 23, 33);
volatile float freq = 434.0; //MHz
volatile unsigned int counter = 0;

void initFSK() {

  // initialize SX1278 with default settings
  Serial.print(F("[SX1278] Initializing ... "));
  //                         freq, br,  fDev, rxBw,  power, preambLen, ook
  int state = radio.beginFSK(freq, 2.4, 75.0,  125.0, 20,    64,         false);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

  Serial.print("SX1278 ver: ");
  Serial.println(radio.getChipVersion());
  uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67};
  state = radio.setSyncWord(syncWord, 4);
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Unable to set configuration, code "));
    Serial.println(state);
    while (true);
  }
  radio.setDataShaping(RADIOLIB_SHAPING_0_5);
}

void setup() {
  Serial.begin(115200);
  initFSK();

}
//                     A     B     C    D
byte byteArr[5] = {0x41, 0x42, 0x43, 0x44, 0x00};

void loop() {

  int state = radio.transmit(byteArr, sizeof(byteArr));

  Serial.print(byteArr[4]);
  byteArr[4]++;

  if (state == RADIOLIB_ERR_NONE) {
    // the packet was successfully transmitted
    Serial.println(F(", success!"));

  } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) {
    // the supplied packet was longer than 256 bytes
    Serial.println(F("too long!"));

  } else if (state == RADIOLIB_ERR_TX_TIMEOUT) {
    // timeout occurred while transmitting packet
    Serial.println(F("timeout!"));

  } else {
    // some other error occurred
    Serial.print(F("failed, code "));
    Serial.println(state);

  }

  // wait for a second before transmitting again
  delay(1000);
}
#include <RadioLib.h>
//RX

// SX1278 has the following connections:
// NSS pin:  18
// DIO0 pin: 26
// RESET pin: 23
// DIO1 pin: 33
SX1278 radio = new Module(18, 26, 23, 33);

volatile float freq = 434.0 ;//MHz
volatile bool receivedFlag = false;
volatile bool enableInterrupt = true;

#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void IRQ_flag(void) {

  // we got a packet, set the flag
  receivedFlag = true;
}

void initFSK() {

  // initialize SX1278 with default settings
  Serial.print(F("[SX1278] Initializing ... "));
  //                         freq, br,  fDev, rxBw,  power, preambLen, ook
  int state = radio.beginFSK(freq, 2.4, 75.0,  125.0, 20,    64,         false);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

  Serial.print("SX1278 ver: ");
  Serial.println(radio.getChipVersion());
  uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67};
  state = radio.setSyncWord(syncWord, 4);
  if (state != RADIOLIB_ERR_NONE) {
    Serial.print(F("Unable to set configuration, code "));
    Serial.println(state);
    while (true);
  }
  radio.setDataShaping(RADIOLIB_SHAPING_0_5);
}
void setup() {
  Serial.begin(115200);
  initFSK();
  // set the function that will be called
  // when new packet is received
  radio.setDio0Action(IRQ_flag);

  int  state = radio.startReceive();
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("StartReceive success!"));
  } else {
    Serial.print(F("StartReceive failed, code "));
    Serial.println(state);
    while (true);
  }
}

void loop() {
  // check if the flag is set
  if (receivedFlag) {
    // reset flag
    receivedFlag = false;

#define ARR_SIZE 5
    // you can also read received data as byte array
    byte byteArr[ARR_SIZE];
    int len = 0; //= radio.getPacketLength(false);
    int state = radio.readData(byteArr, ARR_SIZE);

    if (state == RADIOLIB_ERR_NONE) {
      // packet was successfully received
      //Serial.println(F("[SX1278] Received packet!"));

      // print data of the packet
      Serial.print("[SX1278] Data(");
      Serial.print(len);
      Serial.print("):");
      //Serial.print(str);
      for (int i = 0; i < sizeof(byteArr); i++) {
        Serial.print(byteArr[i], HEX);
        Serial.print(" ");
      }

      // print RSSI (Received Signal Strength Indicator)
      Serial.print(F(", RSSI:"));
      Serial.print(radio.getRSSI());
      Serial.print(F(" dBm"));

      // print SNR (Signal-to-Noise Ratio)
      Serial.print(F(", SNR:"));
      Serial.print(radio.getSNR());
      Serial.print(F(" dB"));

      // print frequency error
      Serial.print(F(", Frequency error:"));
      Serial.print(radio.getFrequencyError());
      //Serial.print(radio.getAFCError());
      Serial.print(F(" Hz"));

    } else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
      // packet was received, but is malformed
      Serial.print(F(", CRC error!"));

    } else {
      // some other error occurred
      Serial.print(F(" Failed, code "));
      Serial.println(state);

    }
    Serial.println();

    // put module back to listen mode
    radio.startReceive();

  }
}
jgromes commented 2 years ago

@parlotto what a rabbit hole you sent me down :)

I can confirm your code does indeed behave like this. Your point about the CRC got me interested - turns out that when CRC is disabled, the receiver gets the packet every time and every other packet seems to be a repeat of the previous one:

15:47:12.412 -> [SX1278] Data(0):41 42 43 44 9A , RSSI:-31.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
15:47:13.474 -> [SX1278] Data(0):41 42 43 44 9A , RSSI:-32.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
15:47:14.536 -> [SX1278] Data(0):41 42 43 44 9C , RSSI:-32.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
15:47:15.596 -> [SX1278] Data(0):41 42 43 44 9C , RSSI:-31.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
15:47:16.660 -> [SX1278] Data(0):41 42 43 44 9E , RSSI:-32.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
15:47:17.723 -> [SX1278] Data(0):41 42 43 44 9E , RSSI:-31.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz

However, when I replaced the last byte in the packet sent by your code by a random(0xFF) | 0x1, (which will add pseudo-random bytes and always set the last bit in the packet) something different started happening:

16:22:12.669 -> 3F, success!
16:22:13.729 -> 75, success!
16:22:14.792 -> 67, success!
16:22:15.852 -> 1B, success!

(...)

16:22:12.761 -> [SX1278] Data(0):41 42 43 44 3E , RSSI:-29.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
16:22:13.820 -> [SX1278] Data(0):41 42 43 44 74 , RSSI:-30.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
16:22:14.884 -> [SX1278] Data(0):41 42 43 44 66 , RSSI:-29.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
16:22:15.944 -> [SX1278] Data(0):41 42 43 44 1A , RSSI:-29.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz

From this, it seems like the last bit is getting flipped to 0 - that's why it seems that in the previous example, even-numbered packets are being "received" twice. This also explaines why with CRC enabled, it seems like only every other packet is received - every CRC ending with a 1 bit will mismatch. By default, FIFO is cleared automatically when a CRC mismatch is detected, hence no packet.

So the ultimate question is why is the last bit not being sent or read correctly by the SX1278. It should be noted that this effect is random - sometimes it does see the correct bit at thte end of the packet. I then noticed you're using in your code quite strange bitrate/frequency deviation combination of 2400 bps with 75 kHz. Crunching the numbers, that gives a modulation index of 2*75/2.4 = 62.5, which is huge. IIRC from my signal processing class in university, higher modulation index means lower spectral effectivity - the most effective option, minimum shift keying, having a modulation index of 0.5.

So, I returned the values you had to the RadioLib defaults (4.8 kbps, 5.0 kHz), which happen to have modulation index of 0.48 and everything started working reliably, including the CRC:

17:04:47.085 -> [SX1278] Initializing ... success!
17:04:47.131 -> SX1278 ver: 18
17:04:47.131 -> 0, success!
17:04:48.193 -> 1, success!
17:04:49.209 -> 2, success!
17:04:50.274 -> 3, success!
17:04:51.288 -> 4, success!
17:04:52.349 -> 5, success!
17:04:53.364 -> 6, success!
17:04:54.380 -> 7, success!
17:04:55.443 -> 8, success!

(...)

17:04:47.162 -> [SX1278] Data(0):41 42 43 44 0 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:48.178 -> [SX1278] Data(0):41 42 43 44 1 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:49.240 -> [SX1278] Data(0):41 42 43 44 2 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:50.258 -> [SX1278] Data(0):41 42 43 44 3 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:51.319 -> [SX1278] Data(0):41 42 43 44 4 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:52.333 -> [SX1278] Data(0):41 42 43 44 5 , RSSI:-42.50 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:53.395 -> [SX1278] Data(0):41 42 43 44 6 , RSSI:-40.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:54.410 -> [SX1278] Data(0):41 42 43 44 7 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz
17:04:55.428 -> [SX1278] Data(0):41 42 43 44 8 , RSSI:-41.00 dBm, SNR:0.00 dB, Frequency error:0.00 Hz

So in conclusion, if you want to send a 2400 bps signal, don't spread it over 150 kHz. I tried it with 4.8 kHz deviation and it works fine.

parlotto commented 2 years ago

@jgromes Thank you for investigating this problem. Yes I admit 75kHz deviation is a way to much for 2400 b/s. I set this value to clearly distinguish the two symbols in URH. Then I forget to reset to the normal value. I have tried with (4.8 kbps, 5.0 kHz) and (2.4 kbps, 5.0 kHz) and I still have the same behavior : One packet received, and very often the next not. I was not able to find a value of FDEV that could give consistent result for 2400b/s baudrate for my devices. I have played a bit with FDEV et BAUDRATE and it start to work reliably at (9.6 kbps, 5.0 kHz) : every packet received. In the documentation : "No constraint applies to the modulation index of the transmitter, but the frequency deviation must be set between 600 Hz and 200 kHz." I think index should more than 1 to have reliable demodulation, the more the better (most reliable) at the price of the passband. (0.5 is a special case for GMSK coherent demodulation if I remember but I was able to have constant result at i=0.4 : (40kb/s ; 8 kHz)). POCSAG at 466MHz for example use large index to be very robust i=17,6 for 512b/s and the most common is i=7.5 at 1200b/s.

What is still strange to me is that I can't have good result at low speed whatever the index is.

EDIT : the documentation states "It is most efficient when the modulation index of the signal is greater than 0.5 and below 10" so 0.5 should be fine.

jgromes commented 2 years ago

@parlotto unfortunately it seems like there is some issue with the last byte that is sent. In URH, I can see for low data rates that the last bit (if present) is a lot shorter than it should be, so I'm not sure if anything can be done on the library side. This definitely warrants further exploration. Couple of thoughts on areas that may be related:

parlotto commented 2 years ago

@jgromes I have tested with version 4.3 :
To make my code work

I confirm the same behavior at low speed. The problem arise under about 8 kb/s (at FDEV=5kHz). Changing FDEV seems no have no effect on the problem.

My main interest to be able to send at low speed is to be compatible with POCSAG 1200b/s.

jgromes commented 2 years ago

@parlotto thanks for checking it out - then it seems like my original assesment was wrong and the AFC/AGC has nothing to do with the issue, or it only contributes to it and is not the root cause. In any case, this will require some digging.

I'm mainly curiuous now if the effect is gradual, or starts suddenly. For example, if there's a 100% success rate at 8 kbps and 50% at 7.9, then that would suggest some issue in bit rate configuration. If the success rate drops off gradually with decreasing bit rate, that would point more to something in the RF/demodulator chain not working correctly. I will try to get to the bottom of this (since I'm pretty curious myslef at this point), but can't guarantee I will have enough time to work exclusively on this.

Velocet commented 1 year ago

@parlotto Have you tried the SX1276 Starter Kit software from Semtech? Maybe they implemented something that everyone is missing?

Use the GUI to set everything up and then export the pre configured registers. Normally this would only work for dev kits, but you can get around this: sx1276ska.exe -test. Use the following key combinations to unlock features:

STRG + ALT + 
  N - New View
  D = Debug
  T = Test Form
  P - LoRa related (dunno as i'm only interested in FSK...)
  B = Firmware Update (only found on RFMxx)

You can also use this on every other software from Semtech (like those for the RFMxx...). And since it's a .NET app you can easily dissect everything with dotPeek.


I know you traced the problem back to the low freq, but there is an errata for all SX12xx which mentions a broken CRC implementation: image

dernasherbrezon commented 1 year ago

if there's a 100% success rate at 8 kbps and 50% at 7.9

Can it be fixed by putting fractional part into register 0x5d?

jgromes commented 1 year ago

@dernasherbrezon good idea - however, it seems to have no effect. The difference is actually very small (e.g., 2.40000375001 with fractional part, compared to 2.4000600015 without. which is a difference of 23 ppm). I implemented it just to scratch it off the TODO list.

The only way I can reliably trigger this problem is by using the bad AFC/AGC trigger. Without it, I can observe no significant packet loss, even with slightly different bitrates. So unless this issue can be confirmed by someone in new library releases, I will consider this resolved.

dernasherbrezon commented 2 months ago

Bit flip can be caused by high SPI frequency. I did some analysis here.