jgromes / RadioLib

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

Hellschreiber issues with E22-400M30S module #659

Closed Pacjunk closed 1 year ago

Pacjunk commented 1 year ago

Describe the bug I am having some strange issue with Hellschreiber mode on this module (SX1268 based). The standard 122.5 baud rate works OK, but anything faster is garbled. I generally use it in the X5 mode and my calculated value is 620 (based on the arduino board I'm using). The module works fine with CW and LORA, just has issues with Hellschreiber. In X9 mode, you can sort of see what it is trying to do, but is still garbled.

The same code works fine with a SX1278 module. I had a look at the library code and it seems straightfoward. The only things I can think of are either some strange setting, or a timing issue. Maybe it takes longer to turn the transmitter on and off for this module and affects the timing?

To Reproduce

This is some code based off your example code that demonstrates my issue.

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

// SX12628 has different connections:
// NSS pin:   10
// DIO1 pin:  8
// NRST pin:  3
// BUSY pin:  9
SX1268 radio = new Module(10, 8, 3, 9);

#define TXEN 6  // TXEnable pin
#define TXenable digitalWrite(TXEN,HIGH)
#define TXdisable digitalWrite(TXEN,LOW)

// create Hellschreiber client instance using the FSK module
HellClient hell(&radio);

void setup() {
  Serial.begin(9600);
  Serial.print(F("[SX1268] Initializing ... "));
  int state = radio.beginFSK();
  if(state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while(true);
  }
  Serial.print(F("[Hell] Initializing ... "));
  state = hell.begin(434.0,620);
  if(state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while(true);
  }
}

void loop() {
  Serial.print(F("[Hell] Sending Hellschreiber data ... "));

  TXenable;
  hell.print("Hello");
  TXdisable;
  Serial.println(F("done!"));
  // wait for a second before transmitting again
  delay(1000);
}

Expected behavior I expected the same result that I get with the SX1278 module!

Screenshots Top is SX1268, bottom is SX1278. Same code (Not from the example code above, but same effect!) image

Additional info (please complete):

jgromes commented 1 year ago

Unfortunately I was not able to replicate this using Arduino Uno and Dorji SX1262 module. The output in X9 mode is massively skewed (probably due to timing errors and lack of TCXO on this module), but definitely legible:

Screenshot_38

X5 mode looks similar, although less skewed. If this was caused by the Tx switch/external PA, I would expect the output to be noisy/grey, not what you have, which are very sharp black/white pixels. That points more to timing issues. In X5 mode, each "pixel" is 1e6/620 = 1612 us. I think 8 MHz microcontroller should be fast enough to keep up, but I don't know how much uncertainty is there in the micros() function which is used for timing by default.

However, for ham radio modes, RadioLib supports interrupt-based timing: https://github.com/jgromes/RadioLib/wiki/Interrupt-Based-Timing, so you could try if that improves things. If not, then it most likely is an issue with the Tx switch.

Pacjunk commented 1 year ago

Thanks for checking. The arduino pro mini is certainly fast enough as I use the same board with the SX1278 at both X5 and X9 without problem. The only thing that changes is the module. There has to be some timing issue when the module switches TX rapidly (although it works with LORA which must also be switching rapidly!)

Pacjunk commented 1 year ago

Here is a trace at X9... (A B C D E F G) x9 Interesting that any vertical line with more than 1 pixel is OK (TX is not switched on and off rapidly). Single pixels are delayed. It looks to me that there is a delay enabling transmit. Once transmitting, the timing seems to be correct, and it switches off rapidly. X1 trace for comparison: x1

jgromes commented 1 year ago

Well, this was a journey ...

I managed to replicate this using a SX1268 with a TCXO - it doesn't happen with SX1262 that doesn't have a TCXO. I also found out that for SX126x with TCXO, starting transmission takes about 5500 us - compared to approx. 500 us when there's no TCXO. The difference comes out to approximately 5000 us - which is exactly the TCXO delay set by default.

In Hell X9, transmitting each pixel takes about 1/(122.5*9) = 907 us. Without TCXO, 500 us is already taken by various processing steps, so the maximum delay that can be used is 400 us. To leave some margin, I set the delay to 200 us and Hell X9 became easily legible.

So a workaround could be to just lower the TCXO delay. However, browsing through some 32 MHz TCXO datasheets, I found that the usual startup time is 1 - 10 ms, so the current default value of 5 ms is a good default.

There's another option though. SX126x implements two standby modes - one which only uses the internal RC oscillator (this is the mode that will be entered when calling standby without arguments), and another one, which leaves the crystal running. Using the other mode also fixes the problem, and doesn't require setting unreasonably short TCXO delay.

TL;DR - it should be fixed now. I also optimized the glyph printing loop a bit, so it only sends an SPI command when there is a change in the signal level. That seems to improve the quality of the resulting text.

Pacjunk commented 1 year ago

Thanks for your great work on this!

So are you saying that the TXCO is/was started for every pixel? In this case, the delay could be reduced by only starting it at the beginning of transmission, and stopping at the end. Maybe that is what you did - I haven't looked at the code yet.

Pacjunk commented 1 year ago

OK, had a look at the code. So the standby() in the loop doesn't need to be standby(RADIOLIB_STANDBY_WARM) as well?

jgromes commented 1 year ago

It was being restarted on every white->black transition (because I didn't realize the default mode for SX126x was RC when writing Hell). That's why a single line was OK, but multiple dots were not as you showed. Currently, it will not be restarted at all, since it will remain in the second stabdy mode.

So the standby() in the loop doesn't need to be standby(RADIOLIB_STANDBY_WARM) as well?

No, because that's Hellschreiber::standby(), not PhysicalLayer::standby();

Pacjunk commented 1 year ago

OK, thanks. My C++ skills aren't great, but I get by. I will give it a try later on today. So is this bug only in Hellschreiber mode or does it affect other modes as well?

jgromes commented 1 year ago

It is possible this is an issue in other modes too - I will investigate further in the next few days. However, it needs rapid on-off switching to become noticeable, which is exactly the case in Hellschreiber and not so much for other modes. It could become an issue for a very fast Morse code for example.

Pacjunk commented 1 year ago

Yes, just thinking about it, most of the other fast modes are FSK rather than OOK, so not an issue.

Thanks again...

Pacjunk commented 1 year ago

I can confirm that your fix works fine on my hardware.

Cheers,