radio: harden SX126x LoRa mode on Ratdeck

This commit is contained in:
DeFiDude
2026-05-21 19:57:26 -06:00
parent 02597f90e9
commit 8031fe0c4d
4 changed files with 102 additions and 11 deletions
+8
View File
@@ -395,6 +395,14 @@ void onHotkeyDiag() {
devErr, status, (status >> 4) & 0x07, (status >> 1) & 0x07);
if (devErr & 0x40) Serial.println(" *** PLL LOCK FAILED ***");
Serial.printf("Current RSSI: %d dBm\n", radio.currentRssi());
uint8_t packetType = radio.getPacketType();
const char* packetTypeName =
(packetType == 0x00) ? "GFSK" :
(packetType == 0x01) ? "LoRa" :
(packetType == 0x02) ? "LR-FHSS" : "unknown";
Serial.printf("Packet type: 0x%02X (%s)%s\n",
packetType, packetTypeName,
packetType == 0x01 ? "" : " *** NOT LoRa ***");
}
Serial.printf("Free heap: %lu bytes PSRAM: %lu bytes\n",
(unsigned long)ESP.getFreeHeap(), (unsigned long)ESP.getFreePsram());
+1
View File
@@ -25,6 +25,7 @@
#define OP_STATUS_6X 0xC0
#define OP_TX_PARAMS_6X 0x8E
#define OP_PACKET_TYPE_6X 0x8A
#define OP_GET_PACKET_TYPE_6X 0x11
#define OP_BUFFER_BASE_ADDR_6X 0x8F
#define OP_READ_REGISTER_6X 0x1D
#define OP_WRITE_REGISTER_6X 0x0D
+88 -9
View File
@@ -8,6 +8,28 @@
SX1262* SX1262::_instance = nullptr;
namespace {
constexpr uint8_t IMAGE_CAL_UNSUPPORTED = 0xFF;
uint8_t imageCalParams(uint32_t frequency, uint8_t out[2]) {
if (frequency >= 430000000UL && frequency <= 440000000UL) { out[0] = 0x6B; out[1] = 0x6F; return 0; }
else if (frequency >= 470000000UL && frequency <= 510000000UL) { out[0] = 0x75; out[1] = 0x81; return 1; }
else if (frequency >= 779000000UL && frequency <= 787000000UL) { out[0] = 0xC1; out[1] = 0xC5; return 2; }
else if (frequency >= 863000000UL && frequency <= 870000000UL) { out[0] = 0xD7; out[1] = 0xDB; return 3; }
else if (frequency >= 902000000UL && frequency <= 928000000UL) { out[0] = 0xE1; out[1] = 0xE9; return 4; }
return IMAGE_CAL_UNSUPPORTED;
}
const char* packetTypeName(uint8_t packetType) {
switch (packetType) {
case 0x00: return "GFSK";
case 0x01: return "LoRa";
case 0x02: return "LR-FHSS";
default: return "unknown";
}
}
}
SX1262::SX1262(SPIClass* spi, int ss, int sclk, int mosi, int miso,
int reset, int irq, int busy, int rxen,
bool tcxo, bool dio2_as_rf_switch)
@@ -171,15 +193,25 @@ void SX1262::calibrate() {
waitOnBusy();
}
void SX1262::calibrate_image(uint32_t frequency) {
bool SX1262::calibrate_image(uint32_t frequency) {
uint8_t image_freq[2] = {0};
if (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; }
else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; }
else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; }
else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; }
else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; }
uint8_t band = imageCalParams(frequency, image_freq);
if (band == IMAGE_CAL_UNSUPPORTED) {
Serial.printf("[SX1262] No image calibration table for %lu Hz\n",
(unsigned long)frequency);
return false;
}
if (_imageCalBand == band) return true;
standby();
executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
waitOnBusy();
_imageCalBand = band;
Serial.printf("[SX1262] Image calibrated for %lu Hz (0x%02X 0x%02X)\n",
(unsigned long)frequency, image_freq[0], image_freq[1]);
return true;
}
void SX1262::enableTCXO() {
@@ -192,9 +224,36 @@ void SX1262::enableTCXO() {
}
}
void SX1262::loraMode() {
bool SX1262::loraMode() {
uint8_t mode = MODE_LONG_RANGE_MODE_6X;
executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
for (int attempt = 0; attempt < 5; attempt++) {
executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
delay(2);
uint8_t packetType = getPacketType();
if (packetType == MODE_LONG_RANGE_MODE_6X) {
if (attempt > 0) {
Serial.printf("[SX1262] LoRa packet type set after %d attempts\n", attempt + 1);
}
return true;
}
Serial.printf("[SX1262] SetPacketType LoRa attempt %d/5 read back 0x%02X (%s)\n",
attempt + 1, packetType, packetTypeName(packetType));
}
Serial.println("[SX1262] Failed to enter LoRa packet mode");
return false;
}
bool SX1262::ensureLoRaMode(const char* context) {
uint8_t packetType = getPacketType();
if (packetType == MODE_LONG_RANGE_MODE_6X) return true;
Serial.printf("[SX1262] %s found packet_type=0x%02X (%s), reasserting LoRa\n",
context ? context : "config", packetType, packetTypeName(packetType));
standby();
return loraMode();
}
void SX1262::rxAntEnable() {
@@ -206,6 +265,7 @@ void SX1262::rxAntEnable() {
bool SX1262::begin(uint32_t frequency) {
_frequency = frequency;
reset();
_imageCalBand = IMAGE_CAL_UNSUPPORTED;
if (_busy != -1) { pinMode(_busy, INPUT); }
if (!_preinitDone) {
if (!preInit()) {
@@ -234,7 +294,9 @@ bool SX1262::begin(uint32_t frequency) {
calibrate_image(_frequency);
// Set LoRa packet type and return to STDBY_XOSC
loraMode();
if (!loraMode()) {
return false;
}
standby();
// Post-calibration diagnostic
@@ -287,6 +349,8 @@ void SX1262::end() {
int SX1262::beginPacket(int implicitHeader) {
standby();
if (!ensureLoRaMode("beginPacket")) return 0;
if (implicitHeader) { implicitHeaderMode(); } else { explicitHeaderMode(); }
_payloadLength = 0;
_fifo_tx_addr_ptr = 0;
@@ -295,6 +359,7 @@ int SX1262::beginPacket(int implicitHeader) {
}
int SX1262::endPacket(bool async) {
if (!ensureLoRaMode("endPacket")) return 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
uint8_t timeout[3] = {0};
@@ -378,6 +443,8 @@ size_t SX1262::write(const uint8_t* buffer, size_t size) {
}
void SX1262::receive(int size) {
if (!ensureLoRaMode("receive")) return;
uint8_t clear[2] = {0xFF, 0xFF};
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clear, 2);
@@ -521,6 +588,12 @@ uint8_t SX1262::getStatus() {
return buf[0];
}
uint8_t SX1262::getPacketType() {
uint8_t buf[1] = {0xFF};
executeOpcodeRead(OP_GET_PACKET_TYPE_6X, buf, 1);
return buf[0];
}
uint16_t SX1262::getIrqFlags() {
uint8_t buf[2] = {0};
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
@@ -535,6 +608,8 @@ bool IRAM_ATTR SX1262::getPacketValidity() {
}
void SX1262::setFrequency(uint32_t frequency) {
calibrate_image(frequency);
standby();
_frequency = frequency;
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
uint8_t buf[4];
@@ -628,11 +703,15 @@ void SX1262::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
// SetModulationParams is only valid in STDBY mode (SX1262 DS Table 11-2).
// Calling from RX/TX mode is silently rejected by the hardware.
standby();
if (!ensureLoRaMode("setModulationParams")) return;
uint8_t buf[4] = {sf, bw, cr, (uint8_t)ldro};
executeOpcode(OP_MODULATION_PARAMS_6X, buf, 4);
}
void SX1262::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
if (!ensureLoRaMode("setPacketParams")) return;
uint8_t buf[6];
buf[0] = (uint8_t)((preamble & 0xFF00) >> 8);
buf[1] = (uint8_t)(preamble & 0x00FF);
+5 -2
View File
@@ -60,6 +60,7 @@ public:
uint16_t getDeviceErrors();
void clearDeviceErrors();
uint8_t getStatus();
uint8_t getPacketType();
uint16_t getIrqFlags();
float getAirtime(uint16_t written);
@@ -91,10 +92,11 @@ private:
void writeBuffer(const uint8_t* buffer, size_t size);
void waitOnBusy();
void loraMode();
bool loraMode();
bool ensureLoRaMode(const char* context);
void rxAntEnable();
void calibrate();
void calibrate_image(uint32_t frequency);
bool calibrate_image(uint32_t frequency);
void enableTCXO();
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
@@ -131,6 +133,7 @@ private:
bool _radioOnline = false;
bool _tcxo = false;
bool _dio2_as_rf_switch = false;
uint8_t _imageCalBand = 0xFF;
bool _txActive = false;
uint32_t _txStartMs = 0;
uint32_t _txTimeoutMs = 0;