Line data Source code
1 : #include "LR11x0.h"
2 :
3 : #include <math.h>
4 : #include <string.h>
5 :
6 : #if !RADIOLIB_EXCLUDE_LR11X0
7 :
8 16 : LR11x0::LR11x0(Module* mod) : LRxxxx(mod) {
9 16 : this->freqStep = RADIOLIB_LR11X0_FREQUENCY_STEP_SIZE;
10 16 : this->maxPacketLength = RADIOLIB_LR11X0_MAX_PACKET_LENGTH;
11 16 : this->implicitLen = RADIOLIB_LR11X0_MAX_PACKET_LENGTH;
12 16 : this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_LR11X0_IRQ_TX_DONE;
13 16 : this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_LR11X0_IRQ_RX_DONE;
14 16 : this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED;
15 16 : this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID;
16 16 : this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID;
17 16 : this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_LR11X0_IRQ_HEADER_ERR;
18 16 : this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_LR11X0_IRQ_CRC_ERR;
19 16 : this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_LR11X0_IRQ_CAD_DONE;
20 16 : this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_LR11X0_IRQ_CAD_DETECTED;
21 16 : this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_LR11X0_IRQ_TIMEOUT;
22 16 : }
23 :
24 0 : int16_t LR11x0::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage, bool high) {
25 : // set module properties and perform initial setup
26 0 : int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LORA);
27 0 : RADIOLIB_ASSERT(state);
28 :
29 : // configure publicly accessible settings
30 0 : state = setBandwidth(bw, high);
31 0 : RADIOLIB_ASSERT(state);
32 :
33 0 : state = setSpreadingFactor(sf);
34 0 : RADIOLIB_ASSERT(state);
35 :
36 0 : state = setCodingRate(cr);
37 0 : RADIOLIB_ASSERT(state);
38 :
39 0 : state = setSyncWord(syncWord);
40 0 : RADIOLIB_ASSERT(state);
41 :
42 0 : state = setPreambleLength(preambleLength);
43 0 : RADIOLIB_ASSERT(state);
44 :
45 : // set publicly accessible settings that are not a part of begin method
46 0 : state = setCRC(2);
47 0 : RADIOLIB_ASSERT(state);
48 :
49 0 : state = invertIQ(false);
50 0 : RADIOLIB_ASSERT(state);
51 :
52 0 : state = setRegulatorLDO();
53 0 : RADIOLIB_ASSERT(state);
54 :
55 0 : return(RADIOLIB_ERR_NONE);
56 : }
57 :
58 0 : int16_t LR11x0::beginGFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage) {
59 : // set module properties and perform initial setup
60 0 : int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_GFSK);
61 0 : RADIOLIB_ASSERT(state);
62 :
63 : // configure publicly accessible settings
64 0 : state = setBitRate(br);
65 0 : RADIOLIB_ASSERT(state);
66 :
67 0 : state = setFrequencyDeviation(freqDev);
68 0 : RADIOLIB_ASSERT(state);
69 :
70 0 : state = setRxBandwidth(rxBw);
71 0 : RADIOLIB_ASSERT(state);
72 :
73 0 : state = setPreambleLength(preambleLength);
74 0 : RADIOLIB_ASSERT(state);
75 :
76 : // set publicly accessible settings that are not a part of begin method
77 0 : uint8_t sync[] = { 0x12, 0xAD };
78 0 : state = setSyncWord(sync, 2);
79 0 : RADIOLIB_ASSERT(state);
80 :
81 0 : state = setDataShaping(RADIOLIB_SHAPING_NONE);
82 0 : RADIOLIB_ASSERT(state);
83 :
84 0 : state = setEncoding(RADIOLIB_ENCODING_NRZ);
85 0 : RADIOLIB_ASSERT(state);
86 :
87 0 : state = variablePacketLengthMode(RADIOLIB_LR11X0_MAX_PACKET_LENGTH);
88 0 : RADIOLIB_ASSERT(state);
89 :
90 0 : state = setCRC(2);
91 0 : RADIOLIB_ASSERT(state);
92 :
93 0 : state = setRegulatorLDO();
94 0 : RADIOLIB_ASSERT(state);
95 :
96 0 : return(RADIOLIB_ERR_NONE);
97 : }
98 :
99 0 : int16_t LR11x0::beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid, float tcxoVoltage) {
100 : // set module properties and perform initial setup
101 0 : int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS);
102 0 : RADIOLIB_ASSERT(state);
103 :
104 : // set grid spacing
105 0 : this->lrFhssGrid = narrowGrid ? RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC : RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_FCC;
106 :
107 : // configure publicly accessible settings
108 0 : state = setLrFhssConfig(bw, cr);
109 0 : RADIOLIB_ASSERT(state);
110 :
111 0 : uint8_t syncWord[] = { 0x12, 0xAD, 0x10, 0x1B };
112 0 : state = setSyncWord(syncWord, 4);
113 0 : RADIOLIB_ASSERT(state);
114 :
115 0 : state = setRegulatorLDO();
116 0 : RADIOLIB_ASSERT(state);
117 :
118 : // set fixed configuration
119 0 : return(setModulationParamsLrFhss(RADIOLIB_LRXXXX_LR_FHSS_BIT_RATE_RAW, RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0));
120 : }
121 :
122 0 : int16_t LR11x0::beginGNSS(uint8_t constellations, float tcxoVoltage) {
123 : // set module properties and perform initial setup - packet type does not matter
124 0 : int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LORA);
125 0 : RADIOLIB_ASSERT(state);
126 :
127 0 : state = this->clearErrors();
128 0 : RADIOLIB_ASSERT(state);
129 :
130 : // set GNSS flag to reserve DIO11 for LF clock
131 0 : this->gnss = true;
132 0 : state = this->configLfClock(RADIOLIB_LR11X0_LF_BUSY_RELEASE_DISABLED | RADIOLIB_LR11X0_LF_CLK_XOSC);
133 0 : RADIOLIB_ASSERT(state);
134 :
135 0 : uint16_t errs = 0;
136 0 : state = this->getErrors(&errs);
137 0 : RADIOLIB_ASSERT(state);
138 0 : if(errs & 0x40) {
139 : RADIOLIB_DEBUG_BASIC_PRINTLN("LF_XOSC_START_ERR");
140 0 : return(RADIOLIB_ERR_SPI_CMD_FAILED);
141 : }
142 :
143 0 : state = this->gnssSetConstellationToUse(constellations);
144 0 : RADIOLIB_ASSERT(state);
145 :
146 0 : state = setRegulatorLDO();
147 0 : RADIOLIB_ASSERT(state);
148 :
149 0 : return(RADIOLIB_ERR_NONE);
150 : }
151 :
152 3 : int16_t LR11x0::transmit(const uint8_t* data, size_t len, uint8_t addr) {
153 : // set mode to standby
154 3 : int16_t state = standby();
155 3 : RADIOLIB_ASSERT(state);
156 :
157 : // check packet length
158 0 : if (this->codingRate > RADIOLIB_LR11X0_LORA_CR_4_8_SHORT) {
159 : // Long Interleaver needs at least 8 bytes
160 0 : if(len < 8) {
161 0 : return(RADIOLIB_ERR_PACKET_TOO_SHORT);
162 : }
163 :
164 : // Long Interleaver supports up to 253 bytes if CRC is enabled
165 0 : if (this->crcTypeLoRa == RADIOLIB_LRXXXX_LORA_CRC_ENABLED && (len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH - 2)) {
166 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
167 : }
168 : }
169 0 : if(len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) {
170 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
171 : }
172 :
173 : // get currently active modem
174 0 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
175 0 : state = getPacketType(&modem);
176 0 : RADIOLIB_ASSERT(state);
177 0 : RadioLibTime_t timeout = getTimeOnAir(len);
178 0 : if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
179 : // calculate timeout (150% of expected time-on-air)
180 0 : timeout = (timeout * 3) / 2;
181 :
182 0 : } else if((modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) || (modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) {
183 : // calculate timeout (500% of expected time-on-air)
184 0 : timeout = timeout * 5;
185 :
186 : } else {
187 0 : return(RADIOLIB_ERR_UNKNOWN);
188 : }
189 :
190 : RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout);
191 :
192 : // start transmission
193 0 : state = startTransmit(data, len, addr);
194 0 : RADIOLIB_ASSERT(state);
195 :
196 : // wait for packet transmission or timeout
197 0 : RadioLibTime_t start = this->mod->hal->micros();
198 0 : while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
199 0 : this->mod->hal->yield();
200 0 : if(this->mod->hal->micros() - start > timeout) {
201 0 : finishTransmit();
202 0 : return(RADIOLIB_ERR_TX_TIMEOUT);
203 : }
204 : }
205 :
206 0 : return(finishTransmit());
207 : }
208 :
209 3 : int16_t LR11x0::receive(uint8_t* data, size_t len, RadioLibTime_t timeout) {
210 : // set mode to standby
211 3 : int16_t state = standby();
212 3 : RADIOLIB_ASSERT(state);
213 :
214 : // calculate timeout based on the configured modem
215 0 : RadioLibTime_t timeoutInternal = timeout;
216 0 : if(!timeoutInternal) {
217 : // get currently active modem
218 0 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
219 0 : state = getPacketType(&modem);
220 0 : RADIOLIB_ASSERT(state);
221 0 : if((modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) || (modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) {
222 : // calculate timeout (500 % of expected time-one-air)
223 0 : size_t maxLen = len;
224 0 : if(len == 0) { maxLen = RADIOLIB_LR11X0_MAX_PACKET_LENGTH; }
225 0 : timeoutInternal = (getTimeOnAir(maxLen) * 5) / 1000;
226 :
227 0 : } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
228 : // this modem cannot receive
229 0 : return(RADIOLIB_ERR_WRONG_MODEM);
230 :
231 : } else {
232 0 : return(RADIOLIB_ERR_UNKNOWN);
233 :
234 : }
235 : }
236 :
237 : RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeoutInternal);
238 :
239 : // start reception
240 0 : uint32_t timeoutValue = (uint32_t)(((float)timeoutInternal * 1000.0f) / 30.52f);
241 0 : state = startReceive(timeoutValue);
242 0 : RADIOLIB_ASSERT(state);
243 :
244 : // wait for packet reception or timeout
245 0 : bool softTimeout = false;
246 0 : RadioLibTime_t start = this->mod->hal->millis();
247 0 : while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
248 0 : this->mod->hal->yield();
249 : // safety check, the timeout should be done by the radio
250 0 : if(this->mod->hal->millis() - start > timeoutInternal) {
251 0 : softTimeout = true;
252 0 : break;
253 : }
254 : }
255 :
256 : // if it was a timeout, this will return an error code
257 : // TODO taken from SX126x, does this really work?
258 0 : state = standby();
259 0 : if((state != RADIOLIB_ERR_NONE) && (state != RADIOLIB_ERR_SPI_CMD_TIMEOUT)) {
260 0 : return(state);
261 : }
262 :
263 : // check whether this was a timeout or not
264 0 : if(softTimeout || (getIrqFlags() & this->irqMap[RADIOLIB_IRQ_TIMEOUT])) {
265 0 : (void)finishReceive();
266 0 : return(RADIOLIB_ERR_RX_TIMEOUT);
267 : }
268 :
269 : // read the received data
270 0 : return(readData(data, len));
271 : }
272 :
273 3 : int16_t LR11x0::transmitDirect(uint32_t frf) {
274 : // set RF switch (if present)
275 3 : this->mod->setRfSwitchState(Module::MODE_TX);
276 :
277 : // user requested to start transmitting immediately (required for RTTY)
278 3 : int16_t state = RADIOLIB_ERR_NONE;
279 3 : if(frf != 0) {
280 0 : state = setRfFrequency(frf);
281 : }
282 3 : RADIOLIB_ASSERT(state);
283 :
284 : // start transmitting
285 3 : return(setTxCw());
286 : }
287 :
288 3 : int16_t LR11x0::receiveDirect() {
289 : // set RF switch (if present)
290 3 : this->mod->setRfSwitchState(Module::MODE_RX);
291 :
292 : // LR11x0 is unable to output received data directly
293 3 : return(RADIOLIB_ERR_UNKNOWN);
294 : }
295 :
296 3 : int16_t LR11x0::scanChannel() {
297 3 : ChannelScanConfig_t cfg = {
298 : .cad = {
299 : .symNum = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
300 : .detPeak = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
301 : .detMin = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
302 : .exitMode = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
303 : .timeout = 0,
304 : .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
305 : .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
306 : },
307 : };
308 6 : return(this->scanChannel(cfg));
309 : }
310 :
311 6 : int16_t LR11x0::scanChannel(const ChannelScanConfig_t &config) {
312 : // set mode to CAD
313 6 : int state = startChannelScan(config);
314 6 : RADIOLIB_ASSERT(state);
315 :
316 : // wait for channel activity detected or timeout
317 0 : while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
318 0 : this->mod->hal->yield();
319 : }
320 :
321 : // check CAD result
322 0 : return(getChannelScanResult());
323 : }
324 :
325 15 : int16_t LR11x0::standby() {
326 15 : return(LR11x0::standby(RADIOLIB_LR11X0_STANDBY_RC));
327 : }
328 :
329 18 : int16_t LR11x0::standby(uint8_t mode) {
330 18 : return(LR11x0::standby(mode, true));
331 : }
332 :
333 18 : int16_t LR11x0::standby(uint8_t mode, bool wakeup) {
334 : // set RF switch (if present)
335 18 : this->mod->setRfSwitchState(Module::MODE_IDLE);
336 :
337 18 : if(wakeup) {
338 : // send a NOP command - this pulls the NSS low to exit the sleep mode,
339 : // while preventing interference with possible other SPI transactions
340 18 : (void)this->mod->SPIwriteStream((uint16_t)RADIOLIB_LR11X0_CMD_NOP, NULL, 0, false, false);
341 : }
342 :
343 18 : uint8_t buff[] = { mode };
344 36 : return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_STANDBY, true, buff, 1));
345 : }
346 :
347 3 : int16_t LR11x0::sleep() {
348 3 : return(LR11x0::sleep(true, 0));
349 : }
350 :
351 3 : int16_t LR11x0::sleep(bool retainConfig, uint32_t sleepTime) {
352 : // set RF switch (if present)
353 3 : this->mod->setRfSwitchState(Module::MODE_IDLE);
354 :
355 : uint8_t buff[] = {
356 3 : (uint8_t)retainConfig,
357 3 : (uint8_t)((sleepTime >> 24) & 0xFF), (uint8_t)((sleepTime >> 16) & 0xFF),
358 3 : (uint8_t)((sleepTime >> 16) & 0xFF), (uint8_t)(sleepTime & 0xFF),
359 3 : };
360 3 : if(sleepTime) {
361 0 : buff[0] |= RADIOLIB_LR11X0_SLEEP_WAKEUP_ENABLED;
362 : }
363 :
364 : // in sleep, the busy line will remain high, so we have to use this method to disable waiting for it to go low
365 3 : int16_t state = this->mod->SPIwriteStream(RADIOLIB_LR11X0_CMD_SET_SLEEP, buff, sizeof(buff), false, false);
366 :
367 : // wait for the module to safely enter sleep mode
368 3 : this->mod->hal->delay(1);
369 :
370 3 : return(state);
371 : }
372 :
373 3 : int16_t LR11x0::finishTransmit() {
374 : // clear interrupt flags
375 3 : clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
376 :
377 : // set mode to standby to disable transmitter/RF switch
378 3 : return(standby());
379 : }
380 :
381 3 : int16_t LR11x0::startReceive() {
382 3 : return(this->startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0));
383 : }
384 :
385 3 : int16_t LR11x0::readData(uint8_t* data, size_t len) {
386 : // check active modem
387 3 : int16_t state = RADIOLIB_ERR_NONE;
388 3 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
389 3 : state = getPacketType(&modem);
390 3 : RADIOLIB_ASSERT(state);
391 0 : if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) &&
392 0 : (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) {
393 0 : return(RADIOLIB_ERR_WRONG_MODEM);
394 : }
395 :
396 : // check integrity CRC
397 0 : uint32_t irq = getIrqStatus();
398 0 : int16_t crcState = RADIOLIB_ERR_NONE;
399 : // Report CRC mismatch when there's a payload CRC error, or a header error and no valid header (to avoid false alarm from previous packet)
400 0 : if((irq & RADIOLIB_LR11X0_IRQ_CRC_ERR) || ((irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID))) {
401 0 : crcState = RADIOLIB_ERR_CRC_MISMATCH;
402 : }
403 :
404 : // get packet length
405 : // the offset is needed since LR11x0 seems to move the buffer base by 4 bytes on every packet
406 0 : uint8_t offset = 0;
407 0 : size_t length = getPacketLength(true, &offset);
408 0 : if((len != 0) && (len < length)) {
409 : // user requested less data than we got, only return what was requested
410 0 : length = len;
411 : }
412 :
413 : // read packet data
414 0 : state = readBuffer8(data, length, offset);
415 0 : RADIOLIB_ASSERT(state);
416 :
417 : // clear the Rx buffer
418 0 : state = clearRxBuffer();
419 0 : RADIOLIB_ASSERT(state);
420 :
421 : // clear interrupt flags
422 0 : state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
423 :
424 : // check if CRC failed - this is done after reading data to give user the option to keep them
425 0 : RADIOLIB_ASSERT(crcState);
426 :
427 0 : return(state);
428 : }
429 :
430 3 : int16_t LR11x0::finishReceive() {
431 : // set mode to standby to disable RF switch
432 3 : int16_t state = standby();
433 3 : RADIOLIB_ASSERT(state);
434 :
435 : // clear interrupt flags
436 0 : return(clearIrqState(RADIOLIB_LR11X0_IRQ_ALL));
437 : }
438 :
439 3 : int16_t LR11x0::startChannelScan() {
440 3 : ChannelScanConfig_t cfg = {
441 : .cad = {
442 : .symNum = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
443 : .detPeak = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
444 : .detMin = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
445 : .exitMode = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT,
446 : .timeout = 0,
447 : .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
448 : .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
449 : },
450 : };
451 6 : return(this->startChannelScan(cfg));
452 : }
453 :
454 12 : int16_t LR11x0::startChannelScan(const ChannelScanConfig_t &config) {
455 : // check active modem
456 12 : int16_t state = RADIOLIB_ERR_NONE;
457 12 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
458 12 : state = getPacketType(&modem);
459 12 : RADIOLIB_ASSERT(state);
460 0 : if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
461 0 : return(RADIOLIB_ERR_WRONG_MODEM);
462 : }
463 :
464 : // set mode to standby
465 0 : state = standby();
466 0 : RADIOLIB_ASSERT(state);
467 :
468 : // set RF switch (if present)
469 0 : this->mod->setRfSwitchState(Module::MODE_RX);
470 :
471 : // set DIO pin mapping
472 0 : uint16_t irqFlags = (config.cad.irqFlags == RADIOLIB_IRQ_NOT_SUPPORTED) ? RADIOLIB_LR11X0_IRQ_CAD_DETECTED | RADIOLIB_LR11X0_IRQ_CAD_DONE : config.cad.irqFlags;
473 0 : state = setDioIrqParams(getIrqMapped(irqFlags), getIrqMapped(irqFlags));
474 0 : RADIOLIB_ASSERT(state);
475 :
476 : // clear interrupt flags
477 0 : state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
478 0 : RADIOLIB_ASSERT(state);
479 :
480 : // set mode to CAD
481 0 : return(startCad(config.cad.symNum, config.cad.detPeak, config.cad.detMin, config.cad.exitMode, config.cad.timeout));
482 : }
483 :
484 3 : int16_t LR11x0::getChannelScanResult() {
485 : // check active modem
486 3 : int16_t state = RADIOLIB_ERR_NONE;
487 3 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
488 3 : state = getPacketType(&modem);
489 3 : RADIOLIB_ASSERT(state);
490 0 : if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
491 0 : return(RADIOLIB_ERR_WRONG_MODEM);
492 : }
493 :
494 : // check CAD result
495 0 : uint32_t cadResult = getIrqStatus();
496 0 : if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DETECTED) {
497 : // detected some LoRa activity
498 0 : return(RADIOLIB_LORA_DETECTED);
499 0 : } else if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DONE) {
500 : // channel is free
501 0 : return(RADIOLIB_CHANNEL_FREE);
502 : }
503 :
504 0 : return(RADIOLIB_ERR_UNKNOWN);
505 : }
506 :
507 0 : int16_t LR11x0::setBandwidth(float bw, bool high) {
508 : // check active modem
509 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
510 0 : int16_t state = getPacketType(&type);
511 0 : RADIOLIB_ASSERT(state);
512 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
513 0 : return(RADIOLIB_ERR_WRONG_MODEM);
514 : }
515 :
516 : // ensure byte conversion doesn't overflow
517 0 : if (high) {
518 0 : RADIOLIB_CHECK_RANGE(bw, 203.125f, 815.0f, RADIOLIB_ERR_INVALID_BANDWIDTH);
519 :
520 0 : if(fabsf(bw - 203.125f) <= 0.001f) {
521 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_203_125;
522 0 : } else if(fabsf(bw - 406.25f) <= 0.001f) {
523 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_406_25;
524 0 : } else if(fabsf(bw - 812.5f) <= 0.001f) {
525 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_812_50;
526 : } else {
527 0 : return(RADIOLIB_ERR_INVALID_BANDWIDTH);
528 : }
529 : } else {
530 0 : RADIOLIB_CHECK_RANGE(bw, 0.0f, 510.0f, RADIOLIB_ERR_INVALID_BANDWIDTH);
531 :
532 : // check allowed bandwidth values
533 0 : uint8_t bw_div2 = bw / 2 + 0.01f;
534 0 : switch (bw_div2) {
535 0 : case 31: // 62.5:
536 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_62_5;
537 0 : break;
538 0 : case 62: // 125.0:
539 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_125_0;
540 0 : break;
541 0 : case 125: // 250.0
542 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_250_0;
543 0 : break;
544 0 : case 250: // 500.0
545 0 : this->bandwidth = RADIOLIB_LR11X0_LORA_BW_500_0;
546 0 : break;
547 0 : default:
548 0 : return(RADIOLIB_ERR_INVALID_BANDWIDTH);
549 : }
550 : }
551 :
552 : // update modulation parameters
553 0 : this->bandwidthKhz = bw;
554 0 : return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
555 : }
556 :
557 0 : int16_t LR11x0::setSpreadingFactor(uint8_t sf, bool legacy) {
558 : // check active modem
559 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
560 0 : int16_t state = getPacketType(&type);
561 0 : RADIOLIB_ASSERT(state);
562 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
563 0 : return(RADIOLIB_ERR_WRONG_MODEM);
564 : }
565 :
566 0 : RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
567 :
568 : // TODO enable SF6 legacy mode
569 : if(legacy && (sf == 6)) {
570 : //this->mod->SPIsetRegValue(RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT, RADIOLIB_LR11X0_SF6_SX127X, 18, 18);
571 : }
572 :
573 : // update modulation parameters
574 0 : this->spreadingFactor = sf;
575 0 : return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
576 : }
577 :
578 0 : int16_t LR11x0::setCodingRate(uint8_t cr, bool longInterleave) {
579 : // check active modem
580 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
581 0 : int16_t state = getPacketType(&type);
582 0 : RADIOLIB_ASSERT(state);
583 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
584 0 : return(RADIOLIB_ERR_WRONG_MODEM);
585 : }
586 :
587 0 : RADIOLIB_CHECK_RANGE(cr, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
588 :
589 0 : if(longInterleave) {
590 0 : switch(cr) {
591 0 : case 4:
592 0 : this->codingRate = 0;
593 0 : break;
594 0 : case 5:
595 : case 6:
596 0 : this->codingRate = cr;
597 0 : break;
598 0 : case 8:
599 0 : this->codingRate = cr - 1;
600 0 : break;
601 0 : default:
602 0 : return(RADIOLIB_ERR_INVALID_CODING_RATE);
603 : }
604 :
605 : } else {
606 0 : this->codingRate = cr - 4;
607 :
608 : }
609 :
610 : // update modulation parameters
611 0 : return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
612 : }
613 :
614 0 : int16_t LR11x0::setSyncWord(uint8_t syncWord) {
615 : // check active modem
616 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
617 0 : int16_t state = getPacketType(&type);
618 0 : RADIOLIB_ASSERT(state);
619 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
620 0 : return(RADIOLIB_ERR_WRONG_MODEM);
621 : }
622 :
623 0 : return(setLoRaSyncWord(syncWord));
624 : }
625 :
626 3 : int16_t LR11x0::setBitRate(float br) {
627 3 : RADIOLIB_CHECK_RANGE(br, 0.6f, 300.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
628 :
629 : // check active modem
630 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
631 0 : int16_t state = getPacketType(&type);
632 0 : RADIOLIB_ASSERT(state);
633 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
634 0 : return(RADIOLIB_ERR_WRONG_MODEM);
635 : }
636 :
637 : // set bit rate value
638 : // TODO implement fractional bit rate configuration
639 0 : this->bitRate = br * 1000.0f;
640 0 : state = setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
641 0 : RADIOLIB_ASSERT(state);
642 :
643 : // apply workaround
644 0 : return(workaroundGFSK());
645 : }
646 :
647 3 : int16_t LR11x0::setFrequencyDeviation(float freqDev) {
648 : // check active modem
649 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
650 3 : int16_t state = getPacketType(&type);
651 3 : RADIOLIB_ASSERT(state);
652 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
653 0 : return(RADIOLIB_ERR_WRONG_MODEM);
654 : }
655 :
656 : // set frequency deviation to lowest available setting (required for digimodes)
657 0 : float newFreqDev = freqDev;
658 0 : if(freqDev < 0.0f) {
659 0 : newFreqDev = 0.6f;
660 : }
661 :
662 0 : RADIOLIB_CHECK_RANGE(newFreqDev, 0.6f, 200.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
663 0 : this->frequencyDev = newFreqDev * 1000.0f;
664 0 : state = setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
665 0 : RADIOLIB_ASSERT(state);
666 :
667 : // apply workaround
668 0 : return(workaroundGFSK());
669 : }
670 :
671 0 : int16_t LR11x0::setRxBandwidth(float rxBw) {
672 : // check active modem
673 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
674 0 : int16_t state = getPacketType(&type);
675 0 : RADIOLIB_ASSERT(state);
676 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
677 0 : return(RADIOLIB_ERR_WRONG_MODEM);
678 : }
679 :
680 : // check modulation parameters
681 : /*if(2 * this->frequencyDev + this->bitRate > rxBw * 1000.0) {
682 : return(RADIOLIB_ERR_INVALID_MODULATION_PARAMETERS);
683 : }*/
684 :
685 0 : const uint8_t rxBwLut[] = {
686 : RADIOLIB_LR11X0_GFSK_RX_BW_4_8,
687 : RADIOLIB_LR11X0_GFSK_RX_BW_5_8,
688 : RADIOLIB_LR11X0_GFSK_RX_BW_7_3,
689 : RADIOLIB_LR11X0_GFSK_RX_BW_9_7,
690 : RADIOLIB_LR11X0_GFSK_RX_BW_11_7,
691 : RADIOLIB_LR11X0_GFSK_RX_BW_14_6,
692 : RADIOLIB_LR11X0_GFSK_RX_BW_19_5,
693 : RADIOLIB_LR11X0_GFSK_RX_BW_23_4,
694 : RADIOLIB_LR11X0_GFSK_RX_BW_29_3,
695 : RADIOLIB_LR11X0_GFSK_RX_BW_39_0,
696 : RADIOLIB_LR11X0_GFSK_RX_BW_46_9,
697 : RADIOLIB_LR11X0_GFSK_RX_BW_58_6,
698 : RADIOLIB_LR11X0_GFSK_RX_BW_78_2,
699 : RADIOLIB_LR11X0_GFSK_RX_BW_93_8,
700 : RADIOLIB_LR11X0_GFSK_RX_BW_117_3,
701 : RADIOLIB_LR11X0_GFSK_RX_BW_156_2,
702 : RADIOLIB_LR11X0_GFSK_RX_BW_187_2,
703 : RADIOLIB_LR11X0_GFSK_RX_BW_234_3,
704 : RADIOLIB_LR11X0_GFSK_RX_BW_312_0,
705 : RADIOLIB_LR11X0_GFSK_RX_BW_373_6,
706 : RADIOLIB_LR11X0_GFSK_RX_BW_467_0,
707 : };
708 :
709 0 : state = findRxBw(rxBw, rxBwLut, sizeof(rxBwLut)/sizeof(rxBwLut[0]), 467.0f, &this->rxBandwidth);
710 0 : RADIOLIB_ASSERT(state);
711 :
712 : // update modulation parameters
713 0 : state = setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
714 0 : RADIOLIB_ASSERT(state);
715 :
716 : // apply workaround
717 0 : return(workaroundGFSK());
718 : }
719 :
720 3 : int16_t LR11x0::setSyncWord(uint8_t* syncWord, size_t len) {
721 3 : if((!syncWord) || (!len) || (len > RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN)) {
722 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
723 : }
724 :
725 : // check active modem
726 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
727 3 : int16_t state = getPacketType(&type);
728 3 : RADIOLIB_ASSERT(state);
729 0 : if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
730 : // update sync word length
731 0 : this->syncWordLength = len*8;
732 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
733 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
734 0 : RADIOLIB_ASSERT(state);
735 :
736 : // sync word is passed most-significant byte first
737 0 : uint8_t fullSyncWord[RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN] = { 0 };
738 0 : memcpy(fullSyncWord, syncWord, len);
739 0 : return(setGfskSyncWord(fullSyncWord));
740 :
741 0 : } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
742 : // with length set to 1 and LoRa modem active, assume it is the LoRa sync word
743 0 : if(len > 1) {
744 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
745 : }
746 0 : return(setSyncWord(syncWord[0]));
747 :
748 0 : } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
749 : // with length set to 4 and LR-FHSS modem active, assume it is the LR-FHSS sync word
750 0 : if(len != sizeof(uint32_t)) {
751 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
752 : }
753 0 : uint32_t sync = 0;
754 0 : memcpy(&sync, syncWord, sizeof(uint32_t));
755 0 : return(lrFhssSetSyncWord(sync));
756 :
757 : }
758 :
759 0 : return(RADIOLIB_ERR_WRONG_MODEM);
760 : }
761 :
762 0 : int16_t LR11x0::setNodeAddress(uint8_t nodeAddr) {
763 : // check active modem
764 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
765 0 : int16_t state = getPacketType(&type);
766 0 : RADIOLIB_ASSERT(state);
767 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
768 0 : return(RADIOLIB_ERR_WRONG_MODEM);
769 : }
770 :
771 : // enable address filtering (node only)
772 0 : this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE;
773 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
774 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
775 0 : RADIOLIB_ASSERT(state);
776 :
777 : // set node address
778 0 : this->node = nodeAddr;
779 0 : return(setPacketAdrs(this->node, 0));
780 : }
781 :
782 0 : int16_t LR11x0::setBroadcastAddress(uint8_t broadAddr) {
783 : // check active modem
784 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
785 0 : int16_t state = getPacketType(&type);
786 0 : RADIOLIB_ASSERT(state);
787 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
788 0 : return(RADIOLIB_ERR_WRONG_MODEM);
789 : }
790 :
791 : // enable address filtering (node and broadcast)
792 0 : this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE_BROADCAST;
793 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
794 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
795 0 : RADIOLIB_ASSERT(state);
796 :
797 : // set node and broadcast address
798 0 : return(setPacketAdrs(this->node, broadAddr));
799 : }
800 :
801 0 : int16_t LR11x0::disableAddressFiltering() {
802 : // check active modem
803 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
804 0 : int16_t state = getPacketType(&type);
805 0 : RADIOLIB_ASSERT(state);
806 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
807 0 : return(RADIOLIB_ERR_WRONG_MODEM);
808 : }
809 :
810 : // disable address filtering
811 0 : this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED;
812 0 : return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
813 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
814 : }
815 :
816 3 : int16_t LR11x0::setDataShaping(uint8_t sh) {
817 : // check active modem
818 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
819 3 : int16_t state = getPacketType(&type);
820 3 : RADIOLIB_ASSERT(state);
821 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
822 0 : return(RADIOLIB_ERR_WRONG_MODEM);
823 : }
824 :
825 : // set data shaping
826 0 : switch(sh) {
827 0 : case RADIOLIB_SHAPING_NONE:
828 0 : this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_NONE;
829 0 : break;
830 0 : case RADIOLIB_SHAPING_0_3:
831 0 : this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_3;
832 0 : break;
833 0 : case RADIOLIB_SHAPING_0_5:
834 0 : this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_5;
835 0 : break;
836 0 : case RADIOLIB_SHAPING_0_7:
837 0 : this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_7;
838 0 : break;
839 0 : case RADIOLIB_SHAPING_1_0:
840 0 : this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_1_0;
841 0 : break;
842 0 : default:
843 0 : return(RADIOLIB_ERR_INVALID_DATA_SHAPING);
844 : }
845 :
846 : // update modulation parameters
847 0 : return(setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev));
848 : }
849 :
850 3 : int16_t LR11x0::setEncoding(uint8_t encoding) {
851 3 : return(setWhitening(encoding));
852 : }
853 :
854 0 : int16_t LR11x0::fixedPacketLengthMode(uint8_t len) {
855 0 : return(setPacketMode(RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED, len));
856 : }
857 :
858 0 : int16_t LR11x0::variablePacketLengthMode(uint8_t maxLen) {
859 0 : return(setPacketMode(RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_VARIABLE, maxLen));
860 : }
861 :
862 3 : int16_t LR11x0::setWhitening(bool enabled, uint16_t initial) {
863 : // check active modem
864 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
865 3 : int16_t state = getPacketType(&type);
866 3 : RADIOLIB_ASSERT(state);
867 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
868 0 : return(RADIOLIB_ERR_WRONG_MODEM);
869 : }
870 :
871 0 : if(!enabled) {
872 : // disable whitening
873 0 : this->whitening = RADIOLIB_LR11X0_GFSK_WHITENING_DISABLED;
874 :
875 : } else {
876 : // enable whitening
877 0 : this->whitening = RADIOLIB_LR11X0_GFSK_WHITENING_ENABLED;
878 :
879 : // write initial whitening value
880 0 : state = setGfskWhitParams(initial);
881 0 : RADIOLIB_ASSERT(state);
882 : }
883 :
884 0 : return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
885 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
886 : }
887 :
888 3 : int16_t LR11x0::setDataRate(DataRate_t dr, ModemType_t modem) {
889 : // get the current modem
890 : ModemType_t currentModem;
891 3 : int16_t state = this->getModem(¤tModem);
892 3 : RADIOLIB_ASSERT(state);
893 :
894 : // switch over if the requested modem is different
895 0 : if(modem != RADIOLIB_MODEM_NONE && modem != currentModem) {
896 0 : state = this->standby();
897 0 : RADIOLIB_ASSERT(state);
898 0 : state = this->setModem(modem);
899 0 : RADIOLIB_ASSERT(state);
900 : }
901 :
902 0 : if(modem == RADIOLIB_MODEM_NONE) {
903 0 : modem = currentModem;
904 : }
905 :
906 : // select interpretation based on modem
907 0 : if(modem == RADIOLIB_MODEM_FSK) {
908 : // set the bit rate
909 0 : state = this->setBitRate(dr.fsk.bitRate);
910 0 : RADIOLIB_ASSERT(state);
911 :
912 : // set the frequency deviation
913 0 : state = this->setFrequencyDeviation(dr.fsk.freqDev);
914 :
915 0 : } else if(modem == RADIOLIB_MODEM_LORA) {
916 : // set the spreading factor
917 0 : state = this->setSpreadingFactor(dr.lora.spreadingFactor);
918 0 : RADIOLIB_ASSERT(state);
919 :
920 : // set the bandwidth
921 0 : state = this->setBandwidth(dr.lora.bandwidth);
922 0 : RADIOLIB_ASSERT(state);
923 :
924 : // set the coding rate
925 0 : state = this->setCodingRate(dr.lora.codingRate);
926 :
927 0 : } else if(modem == RADIOLIB_MODEM_LRFHSS) {
928 : // set the basic config
929 0 : state = this->setLrFhssConfig(dr.lrFhss.bw, dr.lrFhss.cr);
930 0 : RADIOLIB_ASSERT(state);
931 :
932 : // set hopping grid
933 0 : this->lrFhssGrid = dr.lrFhss.narrowGrid ? RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC : RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_FCC;
934 :
935 : }
936 :
937 0 : return(state);
938 : }
939 :
940 3 : int16_t LR11x0::checkDataRate(DataRate_t dr, ModemType_t modem) {
941 3 : int16_t state = RADIOLIB_ERR_UNKNOWN;
942 :
943 : // retrieve modem if not supplied
944 3 : if(modem == RADIOLIB_MODEM_NONE) {
945 3 : state = this->getModem(&modem);
946 3 : RADIOLIB_ASSERT(state);
947 : }
948 :
949 : // select interpretation based on modem
950 0 : if(modem == RADIOLIB_MODEM_FSK) {
951 0 : RADIOLIB_CHECK_RANGE(dr.fsk.bitRate, 0.6f, 300.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
952 0 : RADIOLIB_CHECK_RANGE(dr.fsk.freqDev, 0.6f, 200.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
953 0 : return(RADIOLIB_ERR_NONE);
954 :
955 0 : } else if(modem == RADIOLIB_MODEM_LORA) {
956 0 : RADIOLIB_CHECK_RANGE(dr.lora.spreadingFactor, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
957 0 : RADIOLIB_CHECK_RANGE(dr.lora.bandwidth, 0.0f, 510.0f, RADIOLIB_ERR_INVALID_BANDWIDTH);
958 0 : RADIOLIB_CHECK_RANGE(dr.lora.codingRate, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
959 0 : return(RADIOLIB_ERR_NONE);
960 :
961 : }
962 :
963 0 : return(state);
964 : }
965 :
966 3 : int16_t LR11x0::setPreambleLength(size_t preambleLength) {
967 : // check active modem
968 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
969 3 : int16_t state = getPacketType(&type);
970 3 : RADIOLIB_ASSERT(state);
971 0 : if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
972 0 : this->preambleLengthLoRa = preambleLength;
973 0 : return(setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
974 0 : } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
975 0 : this->preambleLengthGFSK = preambleLength;
976 : //! \TODO: [LR11x0] this can probably be simplified with a simple calculation
977 0 : this->preambleDetLength = preambleLength >= 32 ? RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_32_BITS :
978 : preambleLength >= 24 ? RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_24_BITS :
979 : preambleLength >= 16 ? RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_16_BITS :
980 : preambleLength > 0 ? RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_8_BITS :
981 : RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_DISABLED;
982 0 : return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
983 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
984 : }
985 :
986 0 : return(RADIOLIB_ERR_WRONG_MODEM);
987 : }
988 :
989 0 : int16_t LR11x0::setTCXO(float voltage, uint32_t delay) {
990 : // check if TCXO is enabled at all
991 0 : if(this->XTAL) {
992 0 : return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
993 : }
994 :
995 : // set mode to standby
996 0 : standby();
997 :
998 : // check RADIOLIB_LR11X0_ERROR_STAT_HF_XOSC_START_ERR flag and clear it
999 0 : uint16_t errors = 0;
1000 0 : int16_t state = getErrors(&errors);
1001 0 : RADIOLIB_ASSERT(state);
1002 0 : if(errors & RADIOLIB_LR11X0_ERROR_STAT_HF_XOSC_START_ERR) {
1003 0 : clearErrors();
1004 : }
1005 :
1006 : // check 0 V disable
1007 0 : if(fabsf(voltage - 0.0f) <= 0.001f) {
1008 0 : setTcxoMode(0, 0);
1009 0 : return(reset());
1010 : }
1011 :
1012 : // check allowed voltage values
1013 0 : uint8_t tune = 0;
1014 0 : if(fabsf(voltage - 1.6f) <= 0.001f) {
1015 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_6;
1016 0 : } else if(fabsf(voltage - 1.7f) <= 0.001f) {
1017 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_7;
1018 0 : } else if(fabsf(voltage - 1.8f) <= 0.001f) {
1019 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_8;
1020 0 : } else if(fabsf(voltage - 2.2f) <= 0.001f) {
1021 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_2;
1022 0 : } else if(fabsf(voltage - 2.4f) <= 0.001f) {
1023 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_4;
1024 0 : } else if(fabsf(voltage - 2.7f) <= 0.001f) {
1025 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_7;
1026 0 : } else if(fabsf(voltage - 3.0f) <= 0.001f) {
1027 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_0;
1028 0 : } else if(fabsf(voltage - 3.3f) <= 0.001f) {
1029 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_3;
1030 : } else {
1031 0 : return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
1032 : }
1033 :
1034 : // calculate delay value
1035 0 : uint32_t delayValue = (uint32_t)((float)delay / 30.52f);
1036 0 : if(delayValue == 0) {
1037 0 : delayValue = 1;
1038 : }
1039 :
1040 : // enable TCXO control
1041 0 : return(setTcxoMode(tune, delayValue));
1042 : }
1043 :
1044 0 : int16_t LR11x0::setCRC(uint8_t len, uint32_t initial, uint32_t polynomial, bool inverted) {
1045 : // check active modem
1046 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1047 0 : int16_t state = getPacketType(&type);
1048 0 : RADIOLIB_ASSERT(state);
1049 0 : if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1050 : // LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion
1051 0 : this->crcTypeLoRa = len > 0 ? RADIOLIB_LRXXXX_LORA_CRC_ENABLED : RADIOLIB_LRXXXX_LORA_CRC_DISABLED;
1052 0 : state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled);
1053 :
1054 0 : } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
1055 : // update packet parameters
1056 0 : switch(len) {
1057 0 : case 0:
1058 0 : this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_DISABLED;
1059 0 : break;
1060 0 : case 1:
1061 0 : if(inverted) {
1062 0 : this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_1_BYTE_INV;
1063 : } else {
1064 0 : this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_1_BYTE;
1065 : }
1066 0 : break;
1067 0 : case 2:
1068 0 : if(inverted) {
1069 0 : this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_2_BYTE_INV;
1070 : } else {
1071 0 : this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_2_BYTE;
1072 : }
1073 0 : break;
1074 0 : default:
1075 0 : return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
1076 : }
1077 :
1078 0 : this->crcLenGFSK = len;
1079 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType,
1080 0 : (this->packetType == RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED) ? this->implicitLen : RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
1081 0 : RADIOLIB_ASSERT(state);
1082 :
1083 0 : state = setGfskCrcParams(initial, polynomial);
1084 :
1085 : }
1086 :
1087 0 : return(state);
1088 : }
1089 :
1090 3 : int16_t LR11x0::invertIQ(bool enable) {
1091 : // check active modem
1092 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1093 3 : int16_t state = getPacketType(&type);
1094 3 : RADIOLIB_ASSERT(state);
1095 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1096 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1097 : }
1098 :
1099 0 : this->invertIQEnabled = enable;
1100 0 : return(setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
1101 : }
1102 :
1103 3 : float LR11x0::getRSSI() {
1104 3 : float val = 0;
1105 :
1106 : // check active modem
1107 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1108 3 : (void)getPacketType(&type);
1109 3 : if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1110 0 : (void)getPacketStatusLoRa(&val, NULL, NULL);
1111 :
1112 3 : } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
1113 0 : (void)getPacketStatusGFSK(NULL, &val, NULL, NULL);
1114 :
1115 : }
1116 :
1117 3 : return(val);
1118 : }
1119 :
1120 0 : float LR11x0::getRSSI(bool packet, bool skipReceive) {
1121 0 : float val = 0;
1122 :
1123 : // check if RSSI of packet is requested
1124 0 : if (packet) {
1125 0 : val = getRSSI();
1126 : } else {
1127 0 : if(!skipReceive) { (void)startReceive(); }
1128 0 : int16_t state = getRssiInst(&val);
1129 0 : if(!skipReceive) { (void)standby(); }
1130 0 : if(state != RADIOLIB_ERR_NONE) { return(0); }
1131 : }
1132 :
1133 0 : return(val);
1134 : }
1135 :
1136 3 : float LR11x0::getSNR() {
1137 3 : float val = 0;
1138 :
1139 : // check active modem
1140 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1141 3 : (void)getPacketType(&type);
1142 3 : if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1143 0 : (void)getPacketStatusLoRa(NULL, &val, NULL);
1144 : }
1145 :
1146 3 : return(val);
1147 : }
1148 :
1149 0 : float LR11x0::getFrequencyError() {
1150 : // TODO implement this
1151 0 : return(0);
1152 : }
1153 :
1154 3 : size_t LR11x0::getPacketLength(bool update) {
1155 3 : return(this->getPacketLength(update, NULL));
1156 : }
1157 :
1158 3 : size_t LR11x0::getPacketLength(bool update, uint8_t* offset) {
1159 : (void)update;
1160 :
1161 : // in implicit mode, return the cached value
1162 3 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1163 3 : (void)getPacketType(&type);
1164 3 : if((type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) && (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT)) {
1165 0 : return(this->implicitLen);
1166 : }
1167 :
1168 3 : uint8_t len = 0;
1169 3 : int state = getRxBufferStatus(&len, offset);
1170 : RADIOLIB_DEBUG_BASIC_PRINT("getRxBufferStatus state = %d\n", state);
1171 : (void)state;
1172 3 : return((size_t)len);
1173 : }
1174 :
1175 3 : RadioLibTime_t LR11x0::getTimeOnAir(size_t len) {
1176 : ModemType_t modem;
1177 3 : getModem(&modem);
1178 6 : return(LRxxxx::getTimeOnAir(len, modem));
1179 : }
1180 :
1181 3 : uint32_t LR11x0::getIrqFlags() {
1182 3 : return((uint32_t)this->getIrqStatus());
1183 : }
1184 :
1185 3 : int16_t LR11x0::setIrqFlags(uint32_t irq) {
1186 3 : return(this->setDioIrqParams(irq, irq));
1187 : }
1188 :
1189 3 : int16_t LR11x0::clearIrqFlags(uint32_t irq) {
1190 3 : return(this->clearIrqState(irq));
1191 : }
1192 :
1193 3 : uint8_t LR11x0::randomByte() {
1194 3 : uint32_t num = 0;
1195 3 : (void)getRandomNumber(&num);
1196 3 : return((uint8_t)num);
1197 : }
1198 :
1199 0 : int16_t LR11x0::implicitHeader(size_t len) {
1200 0 : return(this->setHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT, len));
1201 : }
1202 :
1203 0 : int16_t LR11x0::explicitHeader() {
1204 0 : return(this->setHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_EXPLICIT));
1205 : }
1206 :
1207 0 : int16_t LR11x0::setRegulatorLDO() {
1208 0 : return(this->setRegMode(RADIOLIB_LR11X0_REG_MODE_LDO));
1209 : }
1210 :
1211 0 : int16_t LR11x0::setRegulatorDCDC() {
1212 0 : return(this->setRegMode(RADIOLIB_LR11X0_REG_MODE_DC_DC));
1213 : }
1214 :
1215 0 : int16_t LR11x0::setRxBoostedGainMode(bool en) {
1216 0 : uint8_t buff[1] = { (uint8_t)en };
1217 0 : return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RX_BOOSTED, true, buff, sizeof(buff)));
1218 : }
1219 :
1220 3 : int16_t LR11x0::setOutputPower(int8_t power, uint8_t paSel, uint8_t regPaSupply, uint8_t paDutyCycle, uint8_t paHpSel, uint8_t rampTime) {
1221 : // set PA config
1222 3 : int16_t state = setPaConfig(paSel, regPaSupply, paDutyCycle, paHpSel);
1223 3 : RADIOLIB_ASSERT(state);
1224 :
1225 : // set output power
1226 0 : state = setTxParams(power, rampTime);
1227 0 : return(state);
1228 : }
1229 :
1230 0 : void LR11x0::setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) {
1231 : // find which pins are used
1232 0 : uint8_t enable = 0;
1233 0 : for(size_t i = 0; i < Module::RFSWITCH_MAX_PINS; i++) {
1234 : // check if this pin is unused
1235 0 : if(pins[i] == RADIOLIB_NC) {
1236 0 : continue;
1237 : }
1238 :
1239 : // only keep DIO pins, there may be some GPIOs in the switch tabke
1240 0 : if(pins[i] & RFSWITCH_PIN_FLAG) {
1241 0 : enable |= 1UL << RADIOLIB_LRXXXX_DIOx_VAL(pins[i]);
1242 : }
1243 :
1244 : }
1245 :
1246 : // now get the configuration
1247 0 : uint8_t modes[7] = { 0 };
1248 0 : for(size_t i = 0; i < 7; i++) {
1249 : // check end of table
1250 0 : if(table[i].mode == LR11x0::MODE_END_OF_TABLE) {
1251 0 : break;
1252 : }
1253 :
1254 : // get the mode ID in case the modes are out-of-order
1255 0 : uint8_t index = table[i].mode - LR11x0::MODE_STBY;
1256 :
1257 : // iterate over the pins
1258 0 : for(size_t j = 0; j < Module::RFSWITCH_MAX_PINS; j++) {
1259 : // only process modes for the DIOx pins, skip GPIO pins
1260 0 : if(!(pins[j] & RFSWITCH_PIN_FLAG)) {
1261 0 : continue;
1262 : }
1263 0 : modes[index] |= (table[i].values[j] == this->mod->hal->GpioLevelHigh) ? (1UL << j) : 0;
1264 : }
1265 : }
1266 :
1267 : // set it
1268 0 : this->setDioAsRfSwitch(enable, modes[0], modes[1], modes[2], modes[3], modes[4], modes[5], modes[6]);
1269 0 : }
1270 :
1271 0 : int16_t LR11x0::forceLDRO(bool enable) {
1272 : // check packet type
1273 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1274 0 : int16_t state = getPacketType(&type);
1275 0 : RADIOLIB_ASSERT(state);
1276 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1277 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1278 : }
1279 :
1280 : // update modulation parameters
1281 0 : this->ldroAuto = false;
1282 0 : this->ldrOptimize = (uint8_t)enable;
1283 0 : return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
1284 : }
1285 :
1286 0 : int16_t LR11x0::autoLDRO() {
1287 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1288 0 : int16_t state = getPacketType(&type);
1289 0 : RADIOLIB_ASSERT(state);
1290 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1291 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1292 : }
1293 :
1294 0 : this->ldroAuto = true;
1295 0 : return(RADIOLIB_ERR_NONE);
1296 : }
1297 :
1298 0 : int16_t LR11x0::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeed) {
1299 : // check active modem
1300 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1301 0 : int16_t state = getPacketType(&type);
1302 0 : RADIOLIB_ASSERT(state);
1303 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
1304 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1305 : }
1306 :
1307 : // check and cache all parameters
1308 0 : RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE);
1309 0 : this->lrFhssCr = cr;
1310 0 : RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH);
1311 0 : this->lrFhssBw = bw;
1312 0 : RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
1313 0 : this->lrFhssHdrCount = hdrCount;
1314 0 : RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
1315 0 : this->lrFhssHopSeq = hopSeed;
1316 0 : return(RADIOLIB_ERR_NONE);
1317 : }
1318 :
1319 0 : int16_t LR11x0::getVersionInfo(LR11x0VersionInfo_t* info) {
1320 0 : RADIOLIB_ASSERT_PTR(info);
1321 :
1322 0 : int16_t state = this->getVersion(&info->hardware, &info->device, &info->fwMajor, &info->fwMinor);
1323 0 : RADIOLIB_ASSERT(state);
1324 :
1325 : // LR1121 does not have GNSS and WiFi scanning
1326 0 : if(this->chipType == RADIOLIB_LR11X0_DEVICE_LR1121) {
1327 0 : info->fwMajorWiFi = 0;
1328 0 : info->fwMinorWiFi = 0;
1329 0 : info->fwGNSS = 0;
1330 0 : info->almanacGNSS = 0;
1331 0 : return(RADIOLIB_ERR_NONE);
1332 : }
1333 :
1334 0 : state = this->wifiReadVersion(&info->fwMajorWiFi, &info->fwMinorWiFi);
1335 0 : RADIOLIB_ASSERT(state);
1336 0 : return(this->gnssReadVersion(&info->fwGNSS, &info->almanacGNSS));
1337 : }
1338 :
1339 0 : int16_t LR11x0::updateFirmware(const uint32_t* image, size_t size, bool nonvolatile) {
1340 0 : RADIOLIB_ASSERT_PTR(image);
1341 :
1342 : // put the device to bootloader mode
1343 0 : int16_t state = this->reboot(true);
1344 0 : RADIOLIB_ASSERT(state);
1345 0 : this->mod->hal->delay(500);
1346 :
1347 : // check we're in bootloader
1348 0 : uint8_t device = 0xFF;
1349 0 : state = this->getVersion(NULL, &device, NULL, NULL);
1350 0 : RADIOLIB_ASSERT(state);
1351 0 : if(device != RADIOLIB_LR11X0_DEVICE_BOOT) {
1352 : RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to put device to bootloader mode, %02x != %02x", (unsigned int)device, (unsigned int)RADIOLIB_LR11X0_DEVICE_BOOT);
1353 0 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
1354 : }
1355 :
1356 : // erase the image
1357 0 : state = this->bootEraseFlash();
1358 0 : RADIOLIB_ASSERT(state);
1359 :
1360 : // wait for BUSY to go low
1361 0 : RadioLibTime_t start = this->mod->hal->millis();
1362 0 : while(this->mod->hal->digitalRead(this->mod->getGpio())) {
1363 0 : this->mod->hal->yield();
1364 0 : if(this->mod->hal->millis() - start >= 3000) {
1365 : RADIOLIB_DEBUG_BASIC_PRINTLN("BUSY pin timeout after erase!");
1366 0 : return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
1367 : }
1368 : }
1369 :
1370 : // upload the new image
1371 0 : const size_t maxLen = 64;
1372 0 : size_t rem = size % maxLen;
1373 0 : size_t numWrites = (rem == 0) ? (size / maxLen) : ((size / maxLen) + 1);
1374 : RADIOLIB_DEBUG_BASIC_PRINTLN("Writing image in %lu chunks, last chunk size is %lu words", (unsigned long)numWrites, (unsigned long)rem);
1375 0 : for(size_t i = 0; i < numWrites; i ++) {
1376 0 : uint32_t offset = i * maxLen;
1377 0 : uint32_t len = (i == (numWrites - 1)) ? rem : maxLen;
1378 : RADIOLIB_DEBUG_BASIC_PRINTLN("Writing chunk %d at offset %08lx (%u words)", (int)i, (unsigned long)offset, (unsigned int)len);
1379 0 : this->bootWriteFlashEncrypted(offset*sizeof(uint32_t), const_cast<uint32_t*>(&image[offset]), len, nonvolatile);
1380 : }
1381 :
1382 : // kick the device from bootloader
1383 0 : state = this->reset();
1384 0 : RADIOLIB_ASSERT(state);
1385 :
1386 : // verify we are no longer in bootloader
1387 0 : state = this->getVersion(NULL, &device, NULL, NULL);
1388 0 : RADIOLIB_ASSERT(state);
1389 0 : if(device == RADIOLIB_LR11X0_DEVICE_BOOT) {
1390 : RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to kick device from bootloader mode, %02x == %02x", (unsigned int)device, (unsigned int)RADIOLIB_LR11X0_DEVICE_BOOT);
1391 0 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
1392 : }
1393 :
1394 0 : return(state);
1395 : }
1396 :
1397 12 : int16_t LR11x0::getModem(ModemType_t* modem) {
1398 12 : RADIOLIB_ASSERT_PTR(modem);
1399 :
1400 12 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1401 12 : int16_t state = getPacketType(&type);
1402 12 : RADIOLIB_ASSERT(state);
1403 :
1404 0 : switch(type) {
1405 0 : case(RADIOLIB_LR11X0_PACKET_TYPE_LORA):
1406 0 : *modem = ModemType_t::RADIOLIB_MODEM_LORA;
1407 0 : return(RADIOLIB_ERR_NONE);
1408 0 : case(RADIOLIB_LR11X0_PACKET_TYPE_GFSK):
1409 0 : *modem = ModemType_t::RADIOLIB_MODEM_FSK;
1410 0 : return(RADIOLIB_ERR_NONE);
1411 0 : case(RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS):
1412 0 : *modem = ModemType_t::RADIOLIB_MODEM_LRFHSS;
1413 0 : return(RADIOLIB_ERR_NONE);
1414 : }
1415 :
1416 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1417 : }
1418 :
1419 12 : int16_t LR11x0::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) {
1420 : int16_t state;
1421 :
1422 12 : switch(mode) {
1423 9 : case(RADIOLIB_RADIO_MODE_RX): {
1424 : // check active modem
1425 9 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1426 9 : state = getPacketType(&modem);
1427 9 : RADIOLIB_ASSERT(state);
1428 0 : if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) &&
1429 0 : (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK)) {
1430 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1431 : }
1432 :
1433 : // set DIO mapping
1434 0 : if(cfg->receive.timeout != RADIOLIB_LR11X0_RX_TIMEOUT_INF) {
1435 0 : cfg->receive.irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT);
1436 : }
1437 0 : state = setDioIrqParams(getIrqMapped(cfg->receive.irqFlags & cfg->receive.irqMask));
1438 0 : RADIOLIB_ASSERT(state);
1439 :
1440 : // clear interrupt flags
1441 0 : state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
1442 0 : RADIOLIB_ASSERT(state);
1443 :
1444 : // set implicit mode and expected len if applicable
1445 0 : if((this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) && (modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA)) {
1446 0 : state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, this->invertIQEnabled);
1447 0 : RADIOLIB_ASSERT(state);
1448 : }
1449 :
1450 : // if max(uint32_t) is used, revert to RxContinuous
1451 0 : if(cfg->receive.timeout == 0xFFFFFFFF) {
1452 0 : cfg->receive.timeout = 0xFFFFFF;
1453 : }
1454 0 : this->rxTimeout = cfg->receive.timeout;
1455 0 : } break;
1456 :
1457 3 : case(RADIOLIB_RADIO_MODE_TX): {
1458 : // check packet length
1459 3 : if(cfg->transmit.len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) {
1460 3 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
1461 : }
1462 :
1463 : // maximum packet length is decreased by 1 when address filtering is active
1464 3 : if((this->addrComp != RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED) && (cfg->transmit.len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH - 1)) {
1465 0 : return(RADIOLIB_ERR_PACKET_TOO_LONG);
1466 : }
1467 :
1468 : // set packet Length
1469 3 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1470 3 : state = getPacketType(&modem);
1471 3 : RADIOLIB_ASSERT(state);
1472 0 : if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1473 0 : state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, cfg->transmit.len, this->crcTypeLoRa, this->invertIQEnabled);
1474 :
1475 0 : } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
1476 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, cfg->transmit.len, this->crcTypeGFSK, this->whitening);
1477 :
1478 0 : } else if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
1479 0 : return(RADIOLIB_ERR_UNKNOWN);
1480 :
1481 : }
1482 0 : RADIOLIB_ASSERT(state);
1483 :
1484 : // set DIO mapping
1485 0 : state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_TX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT);
1486 0 : RADIOLIB_ASSERT(state);
1487 :
1488 0 : if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
1489 : // in LR-FHSS mode, the packet is built by the device
1490 : // TODO add configurable device offset
1491 0 : state = LRxxxx::lrFhssBuildFrame(RADIOLIB_LR11X0_CMD_LR_FHSS_BUILD_FRAME, this->lrFhssHdrCount, this->lrFhssCr, this->lrFhssGrid, true, this->lrFhssBw, this->lrFhssHopSeq, 0, cfg->transmit.data, cfg->transmit.len);
1492 0 : RADIOLIB_ASSERT(state);
1493 :
1494 : } else {
1495 : // write packet to buffer
1496 0 : state = writeBuffer8(cfg->transmit.data, cfg->transmit.len);
1497 0 : RADIOLIB_ASSERT(state);
1498 :
1499 : }
1500 :
1501 : // clear interrupt flags
1502 0 : state = clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
1503 0 : RADIOLIB_ASSERT(state);
1504 0 : } break;
1505 :
1506 0 : default:
1507 0 : return(RADIOLIB_ERR_UNSUPPORTED);
1508 : }
1509 :
1510 0 : this->stagedMode = mode;
1511 0 : return(state);
1512 : }
1513 :
1514 3 : int16_t LR11x0::launchMode() {
1515 : int16_t state;
1516 3 : switch(this->stagedMode) {
1517 3 : case(RADIOLIB_RADIO_MODE_RX): {
1518 3 : this->mod->setRfSwitchState(Module::MODE_RX);
1519 3 : state = setRx(this->rxTimeout);
1520 3 : } break;
1521 :
1522 0 : case(RADIOLIB_RADIO_MODE_TX): {
1523 0 : this->mod->setRfSwitchState(Module::MODE_TX);
1524 0 : state = setTx(RADIOLIB_LR11X0_TX_TIMEOUT_NONE);
1525 0 : RADIOLIB_ASSERT(state);
1526 :
1527 : // wait for BUSY to go low (= PA ramp up done)
1528 0 : while(this->mod->hal->digitalRead(this->mod->getGpio())) {
1529 0 : this->mod->hal->yield();
1530 : }
1531 0 : } break;
1532 :
1533 0 : default:
1534 0 : return(RADIOLIB_ERR_UNSUPPORTED);
1535 : }
1536 :
1537 3 : this->stagedMode = RADIOLIB_RADIO_MODE_NONE;
1538 3 : return(state);
1539 : }
1540 :
1541 0 : int16_t LR11x0::workaroundGFSK() {
1542 : // first, check we are using GFSK modem
1543 0 : uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1544 0 : int16_t state = getPacketType(&modem);
1545 0 : RADIOLIB_ASSERT(state);
1546 0 : if(modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
1547 : // not in GFSK, nothing to do here
1548 0 : return(RADIOLIB_ERR_NONE);
1549 : }
1550 :
1551 : // this seems to always be the first step (even when resetting)
1552 0 : state = this->writeRegMemMask32(RADIOLIB_LR11X0_REG_GFSK_FIX1, 0x30, 0x10);
1553 0 : RADIOLIB_ASSERT(state);
1554 :
1555 : // these are the default values that will be applied if nothing matches
1556 0 : uint32_t valFix2 = 0x01;
1557 0 : uint32_t valFix3 = 0x0A01;
1558 :
1559 : // next, decide what to change based on modulation properties
1560 0 : if((this->bitRate == 1200) && (this->frequencyDev == 5000) && (this->rxBandwidth == RADIOLIB_LR11X0_GFSK_RX_BW_19_5)) {
1561 : // workaround for 1.2 kbps
1562 0 : valFix2 = 0x04;
1563 :
1564 0 : } else if((this->bitRate == 600) && (this->frequencyDev == 800) && (this->rxBandwidth == RADIOLIB_LR11X0_GFSK_RX_BW_4_8)) {
1565 : // value to write depends on the frequency
1566 0 : valFix3 = (this->freqMHz >= 1000.0f) ? 0x1100 : 0x0600;
1567 :
1568 : }
1569 :
1570 : // update the registers
1571 0 : state = this->writeRegMemMask32(RADIOLIB_LR11X0_REG_GFSK_FIX2, 0x05, valFix2);
1572 0 : RADIOLIB_ASSERT(state);
1573 0 : return(this->writeRegMemMask32(RADIOLIB_LR11X0_REG_GFSK_FIX3, 0x01FF03, valFix3));
1574 : }
1575 :
1576 0 : int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) {
1577 0 : this->mod->init();
1578 0 : this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput);
1579 0 : this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput);
1580 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_LR11X0_CMD_READ_REG_MEM;
1581 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_LR11X0_CMD_WRITE_REG_MEM;
1582 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_LR11X0_CMD_NOP;
1583 0 : this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_LR11X0_CMD_GET_STATUS;
1584 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_32;
1585 0 : this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8;
1586 0 : this->gnss = false;
1587 :
1588 : // try to find the LR11x0 chip - this will also reset the module at least once
1589 0 : if(!LR11x0::findChip(this->chipType)) {
1590 : RADIOLIB_DEBUG_BASIC_PRINTLN("No LR11x0 found!");
1591 0 : this->mod->term();
1592 0 : return(RADIOLIB_ERR_CHIP_NOT_FOUND);
1593 : }
1594 : RADIOLIB_DEBUG_BASIC_PRINTLN("M\tLR11x0");
1595 :
1596 : // set mode to standby
1597 0 : int16_t state = standby();
1598 0 : RADIOLIB_ASSERT(state);
1599 :
1600 : // set TCXO control, if requested
1601 0 : if(!this->XTAL && tcxoVoltage > 0.0f) {
1602 0 : state = setTCXO(tcxoVoltage);
1603 0 : RADIOLIB_ASSERT(state);
1604 : }
1605 :
1606 : // configure settings not accessible by API
1607 0 : return(config(modem));
1608 : }
1609 :
1610 0 : bool LR11x0::findChip(uint8_t ver) {
1611 0 : uint8_t i = 0;
1612 0 : bool flagFound = false;
1613 0 : while((i < 10) && !flagFound) {
1614 : // reset the module
1615 0 : reset();
1616 :
1617 : // read the version
1618 : LR11x0VersionInfo_t info;
1619 0 : int16_t state = getVersionInfo(&info);
1620 0 : RADIOLIB_ASSERT(state);
1621 :
1622 0 : if((info.device == ver) || (info.device == RADIOLIB_LR11X0_DEVICE_BOOT)) {
1623 : RADIOLIB_DEBUG_BASIC_PRINTLN("Found LR11x0: RADIOLIB_LR11X0_CMD_GET_VERSION = 0x%02x", info.device);
1624 : RADIOLIB_DEBUG_BASIC_PRINTLN("Base FW version: %d.%d", (int)info.fwMajor, (int)info.fwMinor);
1625 0 : if(this->chipType != RADIOLIB_LR11X0_DEVICE_LR1121) {
1626 : RADIOLIB_DEBUG_BASIC_PRINTLN("WiFi FW version: %d.%d", (int)info.fwMajorWiFi, (int)info.fwMinorWiFi);
1627 : RADIOLIB_DEBUG_BASIC_PRINTLN("GNSS FW version: %d.%d", (int)info.fwGNSS, (int)info.almanacGNSS);
1628 : }
1629 0 : if(info.device == RADIOLIB_LR11X0_DEVICE_BOOT) {
1630 : RADIOLIB_DEBUG_BASIC_PRINTLN("Warning: device is in bootloader mode! Only FW update is possible now.");
1631 : }
1632 0 : flagFound = true;
1633 : } else {
1634 : RADIOLIB_DEBUG_BASIC_PRINTLN("LR11x0 not found! (%d of 10 tries) RADIOLIB_LR11X0_CMD_GET_VERSION = 0x%02x", i + 1, info.device);
1635 : RADIOLIB_DEBUG_BASIC_PRINTLN("Expected: 0x%02x", ver);
1636 0 : this->mod->hal->delay(10);
1637 0 : i++;
1638 : }
1639 : }
1640 :
1641 :
1642 0 : return(flagFound);
1643 : }
1644 :
1645 0 : int16_t LR11x0::config(uint8_t modem) {
1646 0 : int16_t state = RADIOLIB_ERR_UNKNOWN;
1647 :
1648 : // set Rx/Tx fallback mode to STDBY_RC
1649 0 : state = this->setRxTxFallbackMode(RADIOLIB_LR11X0_FALLBACK_MODE_STBY_RC);
1650 0 : RADIOLIB_ASSERT(state);
1651 :
1652 : // clear IRQ
1653 0 : state = this->clearIrqState(RADIOLIB_LR11X0_IRQ_ALL);
1654 0 : state |= this->setDioIrqParams(RADIOLIB_LR11X0_IRQ_NONE);
1655 0 : RADIOLIB_ASSERT(state);
1656 :
1657 : // calibrate all blocks
1658 0 : state = this->calibrate(RADIOLIB_LR11X0_CALIBRATE_ALL);
1659 :
1660 : // wait for calibration completion
1661 0 : this->mod->hal->delay(5);
1662 0 : while(this->mod->hal->digitalRead(this->mod->getGpio())) {
1663 0 : this->mod->hal->yield();
1664 : }
1665 :
1666 : // if something failed, show the device errors
1667 : #if RADIOLIB_DEBUG_BASIC
1668 : if(state != RADIOLIB_ERR_NONE) {
1669 : // unless mode is forced to standby, device errors will be 0
1670 : standby();
1671 : uint16_t errors = 0;
1672 : getErrors(&errors);
1673 : RADIOLIB_DEBUG_BASIC_PRINTLN("Calibration failed, device errors: 0x%X", errors);
1674 : }
1675 : #else
1676 0 : RADIOLIB_ASSERT(state);
1677 : #endif
1678 :
1679 : // set modem
1680 0 : state = this->setPacketType(modem);
1681 0 : return(state);
1682 : }
1683 :
1684 0 : int16_t LR11x0::setPacketMode(uint8_t mode, uint8_t len) {
1685 : // check active modem
1686 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1687 0 : int16_t state = getPacketType(&type);
1688 0 : RADIOLIB_ASSERT(state);
1689 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
1690 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1691 : }
1692 :
1693 : // set requested packet mode
1694 0 : state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
1695 0 : RADIOLIB_ASSERT(state);
1696 :
1697 : // update cached value
1698 0 : this->packetType = mode;
1699 0 : return(state);
1700 : }
1701 :
1702 0 : int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout) {
1703 : // check active modem
1704 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1705 0 : int16_t state = getPacketType(&type);
1706 0 : RADIOLIB_ASSERT(state);
1707 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1708 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1709 : }
1710 :
1711 : // select CAD parameters
1712 : // TODO the magic numbers are based on Semtech examples, this is probably suboptimal
1713 0 : uint8_t num = symbolNum;
1714 0 : if(num == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
1715 0 : num = 2;
1716 : }
1717 :
1718 0 : const uint8_t detPeakValues[8] = { 48, 48, 50, 55, 55, 59, 61, 65 };
1719 0 : uint8_t peak = detPeak;
1720 0 : if(peak == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
1721 0 : peak = detPeakValues[this->spreadingFactor - 5];
1722 : }
1723 :
1724 0 : uint8_t min = detMin;
1725 0 : if(min == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
1726 0 : min = 10;
1727 : }
1728 :
1729 0 : uint8_t mode = exitMode;
1730 0 : if(mode == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
1731 0 : mode = RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC;
1732 : }
1733 :
1734 0 : uint32_t timeout_raw = (float)timeout / 30.52f;
1735 :
1736 : // set CAD parameters
1737 : // TODO add configurable exit mode and timeout
1738 0 : state = setCadParams(num, peak, min, mode, timeout_raw);
1739 0 : RADIOLIB_ASSERT(state);
1740 :
1741 : // start CAD
1742 0 : return(setCad());
1743 : }
1744 :
1745 0 : int16_t LR11x0::setHeaderType(uint8_t hdrType, size_t len) {
1746 : // check active modem
1747 0 : uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
1748 0 : int16_t state = getPacketType(&type);
1749 0 : RADIOLIB_ASSERT(state);
1750 0 : if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
1751 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1752 : }
1753 :
1754 : // set requested packet mode
1755 0 : state = setPacketParamsLoRa(this->preambleLengthLoRa, hdrType, len, this->crcTypeLoRa, this->invertIQEnabled);
1756 0 : RADIOLIB_ASSERT(state);
1757 :
1758 : // update cached value
1759 0 : this->headerType = hdrType;
1760 0 : this->implicitLen = len;
1761 :
1762 0 : return(state);
1763 : }
1764 :
1765 0 : Module* LR11x0::getMod() {
1766 0 : return(this->mod);
1767 : }
1768 :
1769 : #endif
|