PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RH_RF69.cpp 20KB

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