forked from hallard/RadioHead
-
Notifications
You must be signed in to change notification settings - Fork 1
/
RH_RF69.cpp
617 lines (544 loc) · 21.4 KB
/
RH_RF69.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
// RH_RF69.cpp
//
// Copyright (C) 2011 Mike McCauley
// $Id: RH_RF69.cpp,v 1.27 2017/01/12 23:58:00 mikem Exp $
#include <RH_RF69.h>
// Interrupt vectors for the 3 Arduino interrupt pins
// Each interrupt can be handled by a different instance of RH_RF69, allowing you to have
// 2 or more RF69s per Arduino
#ifndef RH_RF69_IRQLESS
RH_RF69* RH_RF69::_deviceForInterrupt[RH_RF69_NUM_INTERRUPTS] = {0, 0, 0};
uint8_t RH_RF69::_interruptCount = 0; // Index into _deviceForInterrupt for next device
#endif
// These are indexed by the values of ModemConfigChoice
// Stored in flash (program) memory to save SRAM
// It is important to keep the modulation index for FSK between 0.5 and 10
// modulation index = 2 * Fdev / BR
// Note that I have not had much success with FSK with Fd > ~5
// You have to construct these by hand, using the data from the RF69 Datasheet :-(
// or use the SX1231 starter kit software (Ctl-Alt-N to use that without a connected radio)
#define CONFIG_FSK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_FSK | RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_NONE)
#define CONFIG_GFSK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_FSK | RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT1_0)
#define CONFIG_OOK (RH_RF69_DATAMODUL_DATAMODE_PACKET | RH_RF69_DATAMODUL_MODULATIONTYPE_OOK | RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_NONE)
// Choices for RH_RF69_REG_37_PACKETCONFIG1:
#define CONFIG_NOWHITE (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_NONE | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
#define CONFIG_WHITE (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_WHITENING | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
#define CONFIG_MANCHESTER (RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE | RH_RF69_PACKETCONFIG1_DCFREE_MANCHESTER | RH_RF69_PACKETCONFIG1_CRC_ON | RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE)
PROGMEM static const RH_RF69::ModemConfig MODEM_CONFIG_TABLE[] =
{
// 02, 03, 04, 05, 06, 19, 1a, 37
// FSK, No Manchester, no shaping, whitening, CRC, no address filtering
// AFC BW == RX BW == 2 x bit rate
// Low modulation indexes of ~ 1 at slow speeds do not seem to work very well. Choose MI of 2.
{ CONFIG_FSK, 0x3e, 0x80, 0x00, 0x52, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb2Fd5
{ CONFIG_FSK, 0x34, 0x15, 0x00, 0x4f, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb2_4Fd4_8
{ CONFIG_FSK, 0x1a, 0x0b, 0x00, 0x9d, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb4_8Fd9_6
{ CONFIG_FSK, 0x0d, 0x05, 0x01, 0x3b, 0xf4, 0xf4, CONFIG_WHITE}, // FSK_Rb9_6Fd19_2
{ CONFIG_FSK, 0x06, 0x83, 0x02, 0x75, 0xf3, 0xf3, CONFIG_WHITE}, // FSK_Rb19_2Fd38_4
{ CONFIG_FSK, 0x03, 0x41, 0x04, 0xea, 0xf2, 0xf2, CONFIG_WHITE}, // FSK_Rb38_4Fd76_8
{ CONFIG_FSK, 0x02, 0x2c, 0x07, 0xae, 0xe2, 0xe2, CONFIG_WHITE}, // FSK_Rb57_6Fd120
{ CONFIG_FSK, 0x01, 0x00, 0x08, 0x00, 0xe1, 0xe1, CONFIG_WHITE}, // FSK_Rb125Fd125
{ CONFIG_FSK, 0x00, 0x80, 0x10, 0x00, 0xe0, 0xe0, CONFIG_WHITE}, // FSK_Rb250Fd250
{ CONFIG_FSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x42, CONFIG_WHITE}, // FSK_Rb55555Fd50
// MOTEINO
// REGAFCBW set to default 0x8A, No DC-free encoding/decoding performed
{ CONFIG_FSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x8A, CONFIG_NOWHITE}, // FSK_MOTEINO
// 02, 03, 04, 05, 06, 19, 1a, 37
// GFSK (BT=1.0), No Manchester, whitening, CRC, no address filtering
// AFC BW == RX BW == 2 x bit rate
{ CONFIG_GFSK, 0x3e, 0x80, 0x00, 0x52, 0xf4, 0xf5, CONFIG_WHITE}, // GFSK_Rb2Fd5
{ CONFIG_GFSK, 0x34, 0x15, 0x00, 0x4f, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb2_4Fd4_8
{ CONFIG_GFSK, 0x1a, 0x0b, 0x00, 0x9d, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb4_8Fd9_6
{ CONFIG_GFSK, 0x0d, 0x05, 0x01, 0x3b, 0xf4, 0xf4, CONFIG_WHITE}, // GFSK_Rb9_6Fd19_2
{ CONFIG_GFSK, 0x06, 0x83, 0x02, 0x75, 0xf3, 0xf3, CONFIG_WHITE}, // GFSK_Rb19_2Fd38_4
{ CONFIG_GFSK, 0x03, 0x41, 0x04, 0xea, 0xf2, 0xf2, CONFIG_WHITE}, // GFSK_Rb38_4Fd76_8
{ CONFIG_GFSK, 0x02, 0x2c, 0x07, 0xae, 0xe2, 0xe2, CONFIG_WHITE}, // GFSK_Rb57_6Fd120
{ CONFIG_GFSK, 0x01, 0x00, 0x08, 0x00, 0xe1, 0xe1, CONFIG_WHITE}, // GFSK_Rb125Fd125
{ CONFIG_GFSK, 0x00, 0x80, 0x10, 0x00, 0xe0, 0xe0, CONFIG_WHITE}, // GFSK_Rb250Fd250
{ CONFIG_GFSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x42, CONFIG_WHITE}, // GFSK_Rb55555Fd50
// 02, 03, 04, 05, 06, 19, 1a, 37
// OOK, No Manchester, no shaping, whitening, CRC, no address filtering
// with the help of the SX1231 configuration program
// AFC BW == RX BW
// All OOK configs have the default:
// Threshold Type: Peak
// Peak Threshold Step: 0.5dB
// Peak threshiold dec: ONce per chip
// Fixed threshold: 6dB
{ CONFIG_OOK, 0x7d, 0x00, 0x00, 0x10, 0x88, 0x88, CONFIG_WHITE}, // OOK_Rb1Bw1
{ CONFIG_OOK, 0x68, 0x2b, 0x00, 0x10, 0xf1, 0xf1, CONFIG_WHITE}, // OOK_Rb1_2Bw75
{ CONFIG_OOK, 0x34, 0x15, 0x00, 0x10, 0xf5, 0xf5, CONFIG_WHITE}, // OOK_Rb2_4Bw4_8
{ CONFIG_OOK, 0x1a, 0x0b, 0x00, 0x10, 0xf4, 0xf4, CONFIG_WHITE}, // OOK_Rb4_8Bw9_6
{ CONFIG_OOK, 0x0d, 0x05, 0x00, 0x10, 0xf3, 0xf3, CONFIG_WHITE}, // OOK_Rb9_6Bw19_2
{ CONFIG_OOK, 0x06, 0x83, 0x00, 0x10, 0xf2, 0xf2, CONFIG_WHITE}, // OOK_Rb19_2Bw38_4
{ CONFIG_OOK, 0x03, 0xe8, 0x00, 0x10, 0xe2, 0xe2, CONFIG_WHITE}, // OOK_Rb32Bw64
// { CONFIG_FSK, 0x68, 0x2b, 0x00, 0x52, 0x55, 0x55, CONFIG_WHITE}, // works: Rb1200 Fd 5000 bw10000, DCC 400
// { CONFIG_FSK, 0x0c, 0x80, 0x02, 0x8f, 0x52, 0x52, CONFIG_WHITE}, // works 10/40/80
// { CONFIG_FSK, 0x0c, 0x80, 0x02, 0x8f, 0x53, 0x53, CONFIG_WHITE}, // works 10/40/40
};
RH_RF69::RH_RF69(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi)
:
RHSPIDriver(slaveSelectPin, spi)
{
_idleMode = RH_RF69_OPMODE_MODE_STDBY;
#ifndef RH_RF69_IRQLESS
_interruptPin = interruptPin;
_myInterruptIndex = 0xff; // Not allocated yet
#endif
}
void RH_RF69::setIdleMode(uint8_t idleMode)
{
_idleMode = idleMode;
}
bool RH_RF69::init()
{
if (!RHSPIDriver::init())
return false;
#ifndef RH_RF69_IRQLESS
// Determine the interrupt number that corresponds to the interruptPin
int interruptNumber = digitalPinToInterrupt(_interruptPin);
if (interruptNumber == NOT_AN_INTERRUPT)
return false;
#ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
interruptNumber = _interruptPin;
#endif
#endif // ndef RH_RF69_IRQLESS
// Get the device type and check it
// This also tests whether we are really connected to a device
// My test devices return 0x24
_deviceType = spiRead(RH_RF69_REG_10_VERSION);
if (_deviceType == 00 ||
_deviceType == 0xff)
return false;
#ifndef RH_RF69_IRQLESS
// Add by Adrien van den Bossche <vandenbo@univ-tlse2.fr> for Teensy
// ARM M4 requires the below. else pin interrupt doesn't work properly.
// On all other platforms, its innocuous, belt and braces
pinMode(_interruptPin, INPUT);
// Set up interrupt handler
// Since there are a limited number of interrupt glue functions isr*() available,
// we can only support a limited number of devices simultaneously
// ON some devices, notably most Arduinos, the interrupt pin passed in is actuallt the
// interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping
// yourself based on knwledge of what Arduino board you are running on.
if (_myInterruptIndex == 0xff)
{
// First run, no interrupt allocated yet
if (_interruptCount <= RH_RF69_NUM_INTERRUPTS)
_myInterruptIndex = _interruptCount++;
else
return false; // Too many devices, not enough interrupt vectors
}
_deviceForInterrupt[_myInterruptIndex] = this;
if (_myInterruptIndex == 0)
attachInterrupt(interruptNumber, isr0, RISING);
else if (_myInterruptIndex == 1)
attachInterrupt(interruptNumber, isr1, RISING);
else if (_myInterruptIndex == 2)
attachInterrupt(interruptNumber, isr2, RISING);
else
return false; // Too many devices, not enough interrupt vectors
#endif // ndef RH_RF69_IRQLESS
setModeIdle();
// Configure important RH_RF69 registers
// Here we set up the standard packet format for use by the RH_RF69 library:
// 4 bytes preamble
// 2 SYNC words 2d, d4
// 2 CRC CCITT octets computed on the header, length and data (this in the modem config data)
// 0 to 60 bytes data
// RSSI Threshold -114dBm
// We dont use the RH_RF69s address filtering: instead we prepend our own headers to the beginning
// of the RH_RF69 payload
spiWrite(RH_RF69_REG_3C_FIFOTHRESH, RH_RF69_FIFOTHRESH_TXSTARTCONDITION_NOTEMPTY | 0x0f); // thresh 15 is default
// RSSITHRESH is default
// spiWrite(RH_RF69_REG_29_RSSITHRESH, 220); // -110 dbM
// SYNCCONFIG is default. SyncSize is set later by setSyncWords()
// spiWrite(RH_RF69_REG_2E_SYNCCONFIG, RH_RF69_SYNCCONFIG_SYNCON); // auto, tolerance 0
// PAYLOADLENGTH is default
// spiWrite(RH_RF69_REG_38_PAYLOADLENGTH, RH_RF69_FIFO_SIZE); // max size only for RX
// PACKETCONFIG 2 is default
spiWrite(RH_RF69_REG_6F_TESTDAGC, RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAOFF);
// If high power boost set previously, disable it
spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
// The following can be changed later by the user if necessary.
// Set up default configuration
uint8_t syncwords[] = { 0x2d, 0xd4 };
setSyncWords(syncwords, sizeof(syncwords)); // Same as RF22's
// Reasonably fast and reliable default speed and modulation
setModemConfig(GFSK_Rb250Fd250);
// 3 would be sufficient, but this is the same as RF22's
setPreambleLength(4);
// An innocuous ISM frequency, same as RF22's
setFrequency(434.0);
// No encryption
setEncryptionKey(NULL);
// +13dBm, same as power-on default
setTxPower(13);
return true;
}
// C++ level interrupt handler for this instance
// RH_RF69 is unusual in Mthat it has several interrupt lines, and not a single, combined one.
// On Moteino, only one of the several interrupt lines (DI0) from the RH_RF69 is connnected to the processor.
// We use this to get PACKETSDENT and PAYLOADRADY interrupts.
#ifndef RH_RF69_IRQLESS
void RH_RF69::handleInterrupt()
{
// Get the interrupt cause
uint8_t irqflags2 = spiRead(RH_RF69_REG_28_IRQFLAGS2);
if (_mode == RHModeTx && (irqflags2 & RH_RF69_IRQFLAGS2_PACKETSENT))
{
// A transmitter message has been fully sent
setModeIdle(); // Clears FIFO
_txGood++;
// Serial.println("PACKETSENT");
}
// Must look for PAYLOADREADY, not CRCOK, since only PAYLOADREADY occurs _after_ AES decryption
// has been done
if (_mode == RHModeRx && (irqflags2 & RH_RF69_IRQFLAGS2_PAYLOADREADY))
{
// A complete message has been received with good CRC
_lastRssi = -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1));
_lastPreambleTime = millis();
setModeIdle();
// Save it in our buffer
readFifo();
// Serial.println("PAYLOADREADY");
}
}
#endif // ndef RH_RF69_IRQLESS
// Low level function reads the FIFO and checks the address
// Caution: since we put our headers in what the RH_RF69 considers to be the payload, if encryption is enabled
// we have to suffer the cost of decryption before we can determine whether the address is acceptable.
// Performance issue?
void RH_RF69::readFifo()
{
ATOMIC_BLOCK_START;
digitalWrite(_slaveSelectPin, LOW);
_spi.transfer(RH_RF69_REG_00_FIFO); // Send the start address with the write mask off
uint8_t payloadlen = _spi.transfer(0); // First byte is payload len (counting the headers)
if (payloadlen <= RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN &&
payloadlen >= RH_RF69_HEADER_LEN)
{
_rxHeaderTo = _spi.transfer(0);
// Check addressing
if (_promiscuous ||
_rxHeaderTo == _thisAddress ||
_rxHeaderTo == RH_BROADCAST_ADDRESS)
{
// Get the rest of the headers
_rxHeaderFrom = _spi.transfer(0);
_rxHeaderId = _spi.transfer(0);
_rxHeaderFlags = _spi.transfer(0);
// And now the real payload
for (_bufLen = 0; _bufLen < (payloadlen - RH_RF69_HEADER_LEN); _bufLen++)
_buf[_bufLen] = _spi.transfer(0);
_rxGood++;
_rxBufValid = true;
}
}
digitalWrite(_slaveSelectPin, HIGH);
ATOMIC_BLOCK_END;
// Any junk remaining in the FIFO will be cleared next time we go to receive mode.
}
// These are low level functions that call the interrupt handler for the correct
// instance of RH_RF69.
// 3 interrupts allows us to have 3 different devices
#ifndef RH_RF69_IRQLESS
void RH_RF69::isr0()
{
if (_deviceForInterrupt[0])
_deviceForInterrupt[0]->handleInterrupt();
}
void RH_RF69::isr1()
{
if (_deviceForInterrupt[1])
_deviceForInterrupt[1]->handleInterrupt();
}
void RH_RF69::isr2()
{
if (_deviceForInterrupt[2])
_deviceForInterrupt[2]->handleInterrupt();
}
#endif // ndef RH_RF69_IRQLESS
int8_t RH_RF69::temperatureRead()
{
// Caution: must be ins standby.
// setModeIdle();
spiWrite(RH_RF69_REG_4E_TEMP1, RH_RF69_TEMP1_TEMPMEASSTART); // Start the measurement
while (spiRead(RH_RF69_REG_4E_TEMP1) & RH_RF69_TEMP1_TEMPMEASRUNNING)
; // Wait for the measurement to complete
return 166 - spiRead(RH_RF69_REG_4F_TEMP2); // Very approximate, based on observation
}
bool RH_RF69::setFrequency(float centre, float afcPullInRange)
{
// Frf = FRF / FSTEP
uint32_t frf = (uint32_t)((centre * 1000000.0) / RH_RF69_FSTEP);
spiWrite(RH_RF69_REG_07_FRFMSB, (frf >> 16) & 0xff);
spiWrite(RH_RF69_REG_08_FRFMID, (frf >> 8) & 0xff);
spiWrite(RH_RF69_REG_09_FRFLSB, frf & 0xff);
// afcPullInRange is not used
return true;
}
int8_t RH_RF69::rssiRead()
{
// Force a new value to be measured
// Hmmm, this hangs forever!
#if 0
spiWrite(RH_RF69_REG_23_RSSICONFIG, RH_RF69_RSSICONFIG_RSSISTART);
while (!(spiRead(RH_RF69_REG_23_RSSICONFIG) & RH_RF69_RSSICONFIG_RSSIDONE))
;
#endif
return -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1));
}
void RH_RF69::setOpMode(uint8_t mode)
{
uint8_t opmode = spiRead(RH_RF69_REG_01_OPMODE);
opmode &= ~RH_RF69_OPMODE_MODE;
opmode |= (mode & RH_RF69_OPMODE_MODE);
spiWrite(RH_RF69_REG_01_OPMODE, opmode);
// Wait for mode to change.
while (!(spiRead(RH_RF69_REG_27_IRQFLAGS1) & RH_RF69_IRQFLAGS1_MODEREADY))
;
}
void RH_RF69::setModeIdle()
{
if (_mode != RHModeIdle)
{
if (_power >= 18)
{
// If high power boost, return power amp to receive mode
spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
}
setOpMode(_idleMode);
_mode = RHModeIdle;
}
}
bool RH_RF69::sleep()
{
if (_mode != RHModeSleep)
{
spiWrite(RH_RF69_REG_01_OPMODE, RH_RF69_OPMODE_MODE_SLEEP);
_mode = RHModeSleep;
}
return true;
}
void RH_RF69::setModeRx()
{
if (_mode != RHModeRx)
{
if (_power >= 18)
{
// If high power boost, return power amp to receive mode
spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_NORMAL);
spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_NORMAL);
}
spiWrite(RH_RF69_REG_25_DIOMAPPING1, RH_RF69_DIOMAPPING1_DIO0MAPPING_01); // Set interrupt line 0 PayloadReady
setOpMode(RH_RF69_OPMODE_MODE_RX); // Clears FIFO
_mode = RHModeRx;
}
}
void RH_RF69::setModeTx()
{
if (_mode != RHModeTx)
{
if (_power >= 18)
{
// Set high power boost mode
// Note that OCP defaults to ON so no need to change that.
spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_BOOST);
spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_BOOST);
}
spiWrite(RH_RF69_REG_25_DIOMAPPING1, RH_RF69_DIOMAPPING1_DIO0MAPPING_00); // Set interrupt line 0 PacketSent
setOpMode(RH_RF69_OPMODE_MODE_TX); // Clears FIFO
_mode = RHModeTx;
}
}
void RH_RF69::setTxPower(int8_t power)
{
_power = power;
uint8_t palevel;
if (_power < -18)
_power = -18;
// See http://www.hoperf.com/upload/rfchip/RF69-V1.2.pdf section 3.3.6
// for power formulas
if (_power <= 13)
{
// -18dBm to +13dBm
palevel = RH_RF69_PALEVEL_PA0ON | ((_power + 18) & RH_RF69_PALEVEL_OUTPUTPOWER);
}
else if (_power >= 18)
{
// +18dBm to +20dBm
// Need PA1+PA2
// Also need PA boost settings change when tx is turned on and off, see setModeTx()
palevel = RH_RF69_PALEVEL_PA1ON | RH_RF69_PALEVEL_PA2ON | ((_power + 11) & RH_RF69_PALEVEL_OUTPUTPOWER);
}
else
{
// +14dBm to +17dBm
// Need PA1+PA2
palevel = RH_RF69_PALEVEL_PA1ON | RH_RF69_PALEVEL_PA2ON | ((_power + 14) & RH_RF69_PALEVEL_OUTPUTPOWER);
}
spiWrite(RH_RF69_REG_11_PALEVEL, palevel);
}
// Sets registers from a canned modem configuration structure
void RH_RF69::setModemRegisters(const ModemConfig* config)
{
spiBurstWrite(RH_RF69_REG_02_DATAMODUL, &config->reg_02, 5);
spiBurstWrite(RH_RF69_REG_19_RXBW, &config->reg_19, 2);
spiWrite(RH_RF69_REG_37_PACKETCONFIG1, config->reg_37);
}
// Set one of the canned FSK Modem configs
// Returns true if its a valid choice
bool RH_RF69::setModemConfig(ModemConfigChoice index)
{
if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
return false;
ModemConfig cfg;
memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF69::ModemConfig));
setModemRegisters(&cfg);
return true;
}
// Return the canned FSK Modem configs
bool RH_RF69::getModemConfig(ModemConfigChoice index, ModemConfig* config)
{
if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
return false;
memcpy_P(config, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF69::ModemConfig));
return true;
}
void RH_RF69::setPreambleLength(uint16_t bytes)
{
spiWrite(RH_RF69_REG_2C_PREAMBLEMSB, bytes >> 8);
spiWrite(RH_RF69_REG_2D_PREAMBLELSB, bytes & 0xff);
}
void RH_RF69::setSyncWords(const uint8_t* syncWords, uint8_t len)
{
uint8_t syncconfig = spiRead(RH_RF69_REG_2E_SYNCCONFIG);
if (syncWords && len && len <= 4)
{
spiBurstWrite(RH_RF69_REG_2F_SYNCVALUE1, syncWords, len);
syncconfig |= RH_RF69_SYNCCONFIG_SYNCON;
}
else
syncconfig &= ~RH_RF69_SYNCCONFIG_SYNCON;
syncconfig &= ~RH_RF69_SYNCCONFIG_SYNCSIZE;
syncconfig |= (len-1) << 3;
spiWrite(RH_RF69_REG_2E_SYNCCONFIG, syncconfig);
}
void RH_RF69::setEncryptionKey(uint8_t* key)
{
if (key)
{
spiBurstWrite(RH_RF69_REG_3E_AESKEY1, key, 16);
spiWrite(RH_RF69_REG_3D_PACKETCONFIG2, spiRead(RH_RF69_REG_3D_PACKETCONFIG2) | RH_RF69_PACKETCONFIG2_AESON);
}
else
{
spiWrite(RH_RF69_REG_3D_PACKETCONFIG2, spiRead(RH_RF69_REG_3D_PACKETCONFIG2) & ~RH_RF69_PACKETCONFIG2_AESON);
}
}
bool RH_RF69::available()
{
#ifdef RH_RF69_IRQLESS
// As we have not enabled IRQ, ne need to check internal IRQ register of device
uint8_t irqflags2 = spiRead(RH_RF69_REG_28_IRQFLAGS2);
if (_mode == RHModeRx && (irqflags2 & RH_RF69_IRQFLAGS2_PAYLOADREADY))
{
// A complete message has been received with good CRC
_lastRssi = -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1));
_lastPreambleTime = millis();
setModeIdle();
// Save it in our buffer
readFifo();
}
#endif // defined RH_RF69_IRQLESS
if (_mode == RHModeTx)
return false;
setModeRx(); // Make sure we are receiving
return _rxBufValid;
}
bool RH_RF69::recv(uint8_t* buf, uint8_t* len)
{
if (!available())
return false;
if (buf && len)
{
ATOMIC_BLOCK_START;
if (*len > _bufLen)
*len = _bufLen;
memcpy(buf, _buf, *len);
ATOMIC_BLOCK_END;
}
_rxBufValid = false; // Got the most recent message
// printBuffer("recv:", buf, *len);
return true;
}
bool RH_RF69::send(const uint8_t* data, uint8_t len)
{
if (len > RH_RF69_MAX_MESSAGE_LEN)
return false;
waitPacketSent(); // Make sure we dont interrupt an outgoing message
setModeIdle(); // Prevent RX while filling the fifo
if (!waitCAD())
return false; // Check channel activity
ATOMIC_BLOCK_START;
digitalWrite(_slaveSelectPin, LOW);
_spi.transfer(RH_RF69_REG_00_FIFO | RH_RF69_SPI_WRITE_MASK); // Send the start address with the write mask on
_spi.transfer(len + RH_RF69_HEADER_LEN); // Include length of headers
// First the 4 headers
_spi.transfer(_txHeaderTo);
_spi.transfer(_txHeaderFrom);
_spi.transfer(_txHeaderId);
_spi.transfer(_txHeaderFlags);
// Now the payload
while (len--)
_spi.transfer(*data++);
digitalWrite(_slaveSelectPin, HIGH);
ATOMIC_BLOCK_END;
setModeTx(); // Start the transmitter
return true;
}
#ifdef RH_RF69_IRQLESS
// Since we have no interrupts, we need to implement our own
// waitPacketSent for the driver by reading RF69 internal register
bool RH_RF69::waitPacketSent()
{
// If we are not currently in transmit mode, there is no packet to wait for
if (_mode != RHModeTx)
return false;
while (!(spiRead(RH_RF69_REG_28_IRQFLAGS2) & RH_RF69_IRQFLAGS2_PACKETSENT)){
YIELD;
}
// A transmitter message has been fully sent
setModeIdle(); // Clears FIFO
_txGood++;
return true;
}
#endif // defined RH_RF69_IRQLESS
uint8_t RH_RF69::maxMessageLength()
{
return RH_RF69_MAX_MESSAGE_LEN;
}
bool RH_RF69::printRegister(uint8_t reg)
{
#ifdef RH_HAVE_SERIAL
Serial.print(reg, HEX);
Serial.print(" ");
Serial.println(spiRead(reg), HEX);
#endif
return true;
}
bool RH_RF69::printRegisters()
{
uint8_t i;
for (i = 0; i < 0x50; i++)
printRegister(i);
// Non-contiguous registers
printRegister(RH_RF69_REG_58_TESTLNA);
printRegister(RH_RF69_REG_6F_TESTDAGC);
printRegister(RH_RF69_REG_71_TESTAFC);
return true;
}