From 6fab141c7ea6baebd3b9651ef0a118478e68519d Mon Sep 17 00:00:00 2001 From: willem oldemans Date: Thu, 1 Oct 2020 09:36:42 +0200 Subject: [PATCH] forked and modified initial value for return values and changed timer definition to TIM1 instead of int(1) --- LICENSE | 17 + MANIFEST | 181 ++ MGOSCompat/HardwareSPI.cpp | 154 ++ MGOSCompat/HardwareSPI.h | 49 + MGOSCompat/HardwareSerial.cpp | 189 ++ MGOSCompat/HardwareSerial.h | 49 + MGOSCompat/MGOS.cpp | 159 ++ MGOSCompat/MGOS.h | 101 + MGOSCompat/README | 1 + RF24configs/README | 19 + .../radio_config_Si4464_27_434_2GFSK_5_10.h | 516 ++++ .../radio_config_Si4464_30_434_2GFSK_10_20.h | 516 ++++ .../radio_config_Si4464_30_434_2GFSK_5_10.h | 516 ++++ .../radio_config_Si4464_30_915_2GFSK_10_20.h | 516 ++++ .../radio_config_Si4464_30_915_2GFSK_5_10.h | 516 ++++ RHCRC.cpp | 104 + RHCRC.h | 19 + RHDatagram.cpp | 123 + RHDatagram.h | 162 ++ RHEncryptedDriver.cpp | 156 ++ RHEncryptedDriver.h | 251 ++ RHGenericDriver.cpp | 221 ++ RHGenericDriver.h | 310 +++ RHGenericSPI.cpp | 31 + RHGenericSPI.h | 183 ++ RHHardwareSPI.cpp | 505 ++++ RHHardwareSPI.h | 116 + RHMesh.cpp | 253 ++ RHMesh.h | 264 ++ RHNRFSPIDriver.cpp | 137 + RHNRFSPIDriver.h | 101 + RHReliableDatagram.cpp | 209 ++ RHReliableDatagram.h | 221 ++ RHRouter.cpp | 318 +++ RHRouter.h | 342 +++ RHSPIDriver.cpp | 120 + RHSPIDriver.h | 109 + RHSoftwareSPI.cpp | 166 ++ RHSoftwareSPI.h | 90 + RHTcpProtocol.h | 66 + RH_ABZ.cpp | 116 + RH_ABZ.h | 191 ++ RH_ASK.cpp | 939 +++++++ RH_ASK.h | 449 ++++ RH_CC110.cpp | 493 ++++ RH_CC110.h | 895 +++++++ RH_E32.cpp | 347 +++ RH_E32.h | 453 ++++ RH_MRF89.cpp | 570 ++++ RH_MRF89.h | 628 +++++ RH_NRF24.cpp | 349 +++ RH_NRF24.h | 647 +++++ RH_NRF51.cpp | 400 +++ RH_NRF51.h | 302 +++ RH_NRF905.cpp | 270 ++ RH_NRF905.h | 424 +++ RH_RF22.cpp | 814 ++++++ RH_RF22.h | 1393 ++++++++++ RH_RF24.cpp | 1054 ++++++++ RH_RF24.h | 1164 ++++++++ RH_RF24_property_data/convert.pl | 132 + RH_RF69.cpp | 569 ++++ RH_RF69.h | 1052 ++++++++ RH_RF95.cpp | 679 +++++ RH_RF95.h | 917 +++++++ RH_Serial.cpp | 249 ++ RH_Serial.h | 267 ++ RH_TCP.cpp | 304 +++ RH_TCP.h | 187 ++ RHutil/HardwareSerial.cpp | 246 ++ RHutil/HardwareSerial.h | 100 + RHutil/RasPi.cpp | 176 ++ RHutil/RasPi.h | 75 + RHutil/atomic.h | 71 + RHutil/simulator.h | 84 + RHutil_pigpio/RasPi.cpp | 264 ++ RHutil_pigpio/RasPi.h | 214 ++ RadioHead.h | 1701 ++++++++++++ STM32ArduinoCompat/HardwareSPI.cpp | 181 ++ STM32ArduinoCompat/HardwareSPI.h | 38 + STM32ArduinoCompat/HardwareSerial.cpp | 349 +++ STM32ArduinoCompat/HardwareSerial.h | 78 + STM32ArduinoCompat/README | 6 + STM32ArduinoCompat/wirish.cpp | 413 +++ STM32ArduinoCompat/wirish.h | 157 ++ examples/abz/abz_client/abz_client.pde | 102 + examples/abz/abz_server/abz_server.pde | 90 + examples/ask/ask_receiver/ask_receiver.pde | 43 + .../ask_reliable_datagram_client.pde | 60 + .../ask_reliable_datagram_server.pde | 54 + .../ask/ask_transmitter/ask_transmitter.pde | 38 + examples/cc110/cc110_client/cc110_client.pde | 75 + examples/cc110/cc110_server/cc110_server.pde | 69 + examples/cc110/jsrpc/config.h | 27 + examples/cc110/jsrpc/jsrpc.ino | 569 ++++ examples/e32/e32_client/e32_client.pde | 70 + examples/e32/e32_server/e32_server.pde | 64 + examples/mrf89/mrf89_client/mrf89_client.pde | 68 + examples/mrf89/mrf89_server/mrf89_server.pde | 67 + examples/nrf24/nrf24_client/nrf24_client.pde | 67 + .../nrf24_encrypted_client.pde | 78 + .../nrf24_encrypted_server.pde | 69 + .../nrf24_reliable_datagram_client.pde | 63 + .../nrf24_reliable_datagram_server.pde | 57 + examples/nrf24/nrf24_server/nrf24_server.pde | 60 + .../nrf51/nrf51_audio_rx/nrf51_audio_rx.pde | 113 + examples/nrf51/nrf51_audio_tx/nrf51_audio.pdf | Bin 0 -> 11291 bytes .../nrf51/nrf51_audio_tx/nrf51_audio_tx.pde | 143 + examples/nrf51/nrf51_client/nrf51_client.pde | 73 + .../nrf51_reliable_datagram_client.pde | 66 + .../nrf51_reliable_datagram_server.pde | 61 + examples/nrf51/nrf51_server/nrf51_server.pde | 64 + .../nrf905/nrf905_client/nrf905_client.pde | 59 + .../nrf905_reliable_datagram_client.pde | 60 + .../nrf905_reliable_datagram_server.pde | 54 + .../nrf905/nrf905_server/nrf905_server.pde | 52 + examples/raspi/Makefile | 53 + examples/raspi/RasPiRH.cpp | 138 + examples/raspi/rf95/rf95_client/Makefile | 43 + .../raspi/rf95/rf95_client/rf95_client.cpp | 147 ++ examples/raspi/rf95/rf95_mesh_client/Makefile | 52 + .../rf95_mesh_client/rf95_mesh_client.cpp | 151 ++ .../raspi/rf95/rf95_mesh_server1/Makefile | 52 + .../rf95_mesh_server1/rf95_mesh_server1.cpp | 138 + .../raspi/rf95/rf95_mesh_server2/Makefile | 52 + .../rf95_mesh_server2/rf95_mesh_server2.cpp | 138 + .../raspi/rf95/rf95_mesh_server3/Makefile | 52 + .../rf95_mesh_server3/rf95_mesh_server3.cpp | 138 + .../rf95_reliable_datagram_client/Makefile | 46 + .../rf95_reliable_datagram_client.cpp | 141 + .../rf95_reliable_datagram_server/Makefile | 46 + .../rf95_reliable_datagram_server.cpp | 136 + .../raspi/rf95/rf95_router_client/Makefile | 49 + .../rf95_router_client/rf95_router_client.cpp | 155 ++ .../raspi/rf95/rf95_router_server1/Makefile | 49 + .../rf95_router_server1.cpp | 143 + .../raspi/rf95/rf95_router_server2/Makefile | 49 + .../rf95_router_server2.cpp | 143 + .../raspi/rf95/rf95_router_server3/Makefile | 49 + .../rf95_router_server3.cpp | 143 + examples/raspi/rf95/rf95_router_test/Makefile | 49 + .../rf95_router_test/rf95_router_test.cpp | 183 ++ examples/raspi/rf95/rf95_server/Makefile | 43 + .../raspi/rf95/rf95_server/rf95_server.cpp | 134 + examples/rf22/rf22_client/rf22_client.pde | 57 + .../rf22_mesh_client/rf22_mesh_client.pde | 68 + .../rf22_mesh_server1/rf22_mesh_server1.pde | 56 + .../rf22_mesh_server2/rf22_mesh_server2.pde | 56 + .../rf22_mesh_server3/rf22_mesh_server3.pde | 56 + .../rf22_reliable_datagram_client.pde | 62 + .../rf22_reliable_datagram_server.pde | 56 + .../rf22_router_client/rf22_router_client.pde | 72 + .../rf22_router_server1.pde | 59 + .../rf22_router_server2.pde | 59 + .../rf22_router_server3.pde | 59 + .../rf22_router_test/rf22_router_test.pde | 102 + examples/rf22/rf22_server/rf22_server.pde | 53 + examples/rf24/rf24_client/rf24_client.pde | 62 + .../rf24_lowpower_client.pde | 80 + .../rf24_reliable_datagram_client.pde | 65 + .../rf24_reliable_datagram_server.pde | 59 + examples/rf24/rf24_server/rf24_server.pde | 57 + examples/rf69/rf69_client/rf69_client.pde | 77 + .../rf69_reliable_datagram_client.pde | 70 + .../rf69_reliable_datagram_server.pde | 64 + examples/rf69/rf69_server/rf69_server.pde | 81 + examples/rf95/rf95_client/rf95_client.pde | 83 + .../rf95_encrypted_client.pde | 47 + .../rf95_encrypted_server.pde | 48 + .../rf95_reliable_datagram_client.pde | 84 + .../rf95_reliable_datagram_server.pde | 78 + examples/rf95/rf95_server/rf95_server.pde | 82 + .../serial_reliable_datagram_client.pde | 82 + .../serial_reliable_datagram_server.pde | 75 + .../simulator_reliable_datagram_client.pde | 67 + .../simulator_reliable_datagram_server.pde | 59 + project.cfg | 2331 +++++++++++++++++ tools/chain.conf | 10 + tools/etherSimulator.pl | 224 ++ tools/simBuild | 13 + tools/simMain.cpp | 71 + 181 files changed, 38514 insertions(+) create mode 100644 LICENSE create mode 100644 MANIFEST create mode 100644 MGOSCompat/HardwareSPI.cpp create mode 100644 MGOSCompat/HardwareSPI.h create mode 100644 MGOSCompat/HardwareSerial.cpp create mode 100644 MGOSCompat/HardwareSerial.h create mode 100644 MGOSCompat/MGOS.cpp create mode 100644 MGOSCompat/MGOS.h create mode 100644 MGOSCompat/README create mode 100644 RF24configs/README create mode 100755 RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h create mode 100755 RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h create mode 100755 RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h create mode 100755 RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h create mode 100755 RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h create mode 100644 RHCRC.cpp create mode 100644 RHCRC.h create mode 100644 RHDatagram.cpp create mode 100644 RHDatagram.h create mode 100644 RHEncryptedDriver.cpp create mode 100644 RHEncryptedDriver.h create mode 100644 RHGenericDriver.cpp create mode 100644 RHGenericDriver.h create mode 100644 RHGenericSPI.cpp create mode 100644 RHGenericSPI.h create mode 100644 RHHardwareSPI.cpp create mode 100644 RHHardwareSPI.h create mode 100644 RHMesh.cpp create mode 100644 RHMesh.h create mode 100644 RHNRFSPIDriver.cpp create mode 100644 RHNRFSPIDriver.h create mode 100644 RHReliableDatagram.cpp create mode 100644 RHReliableDatagram.h create mode 100644 RHRouter.cpp create mode 100644 RHRouter.h create mode 100644 RHSPIDriver.cpp create mode 100644 RHSPIDriver.h create mode 100644 RHSoftwareSPI.cpp create mode 100644 RHSoftwareSPI.h create mode 100644 RHTcpProtocol.h create mode 100644 RH_ABZ.cpp create mode 100644 RH_ABZ.h create mode 100644 RH_ASK.cpp create mode 100644 RH_ASK.h create mode 100644 RH_CC110.cpp create mode 100644 RH_CC110.h create mode 100644 RH_E32.cpp create mode 100644 RH_E32.h create mode 100644 RH_MRF89.cpp create mode 100644 RH_MRF89.h create mode 100644 RH_NRF24.cpp create mode 100644 RH_NRF24.h create mode 100644 RH_NRF51.cpp create mode 100644 RH_NRF51.h create mode 100644 RH_NRF905.cpp create mode 100644 RH_NRF905.h create mode 100644 RH_RF22.cpp create mode 100644 RH_RF22.h create mode 100644 RH_RF24.cpp create mode 100644 RH_RF24.h create mode 100755 RH_RF24_property_data/convert.pl create mode 100644 RH_RF69.cpp create mode 100644 RH_RF69.h create mode 100644 RH_RF95.cpp create mode 100644 RH_RF95.h create mode 100644 RH_Serial.cpp create mode 100644 RH_Serial.h create mode 100644 RH_TCP.cpp create mode 100644 RH_TCP.h create mode 100644 RHutil/HardwareSerial.cpp create mode 100644 RHutil/HardwareSerial.h create mode 100644 RHutil/RasPi.cpp create mode 100644 RHutil/RasPi.h create mode 100644 RHutil/atomic.h create mode 100644 RHutil/simulator.h create mode 100644 RHutil_pigpio/RasPi.cpp create mode 100644 RHutil_pigpio/RasPi.h create mode 100644 RadioHead.h create mode 100644 STM32ArduinoCompat/HardwareSPI.cpp create mode 100644 STM32ArduinoCompat/HardwareSPI.h create mode 100644 STM32ArduinoCompat/HardwareSerial.cpp create mode 100644 STM32ArduinoCompat/HardwareSerial.h create mode 100644 STM32ArduinoCompat/README create mode 100644 STM32ArduinoCompat/wirish.cpp create mode 100644 STM32ArduinoCompat/wirish.h create mode 100644 examples/abz/abz_client/abz_client.pde create mode 100644 examples/abz/abz_server/abz_server.pde create mode 100644 examples/ask/ask_receiver/ask_receiver.pde create mode 100644 examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde create mode 100644 examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde create mode 100644 examples/ask/ask_transmitter/ask_transmitter.pde create mode 100644 examples/cc110/cc110_client/cc110_client.pde create mode 100644 examples/cc110/cc110_server/cc110_server.pde create mode 100644 examples/cc110/jsrpc/config.h create mode 100644 examples/cc110/jsrpc/jsrpc.ino create mode 100644 examples/e32/e32_client/e32_client.pde create mode 100644 examples/e32/e32_server/e32_server.pde create mode 100644 examples/mrf89/mrf89_client/mrf89_client.pde create mode 100644 examples/mrf89/mrf89_server/mrf89_server.pde create mode 100644 examples/nrf24/nrf24_client/nrf24_client.pde create mode 100644 examples/nrf24/nrf24_encrypted_client/nrf24_encrypted_client.pde create mode 100644 examples/nrf24/nrf24_encrypted_server/nrf24_encrypted_server.pde create mode 100644 examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde create mode 100644 examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde create mode 100644 examples/nrf24/nrf24_server/nrf24_server.pde create mode 100644 examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde create mode 100644 examples/nrf51/nrf51_audio_tx/nrf51_audio.pdf create mode 100644 examples/nrf51/nrf51_audio_tx/nrf51_audio_tx.pde create mode 100644 examples/nrf51/nrf51_client/nrf51_client.pde create mode 100644 examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde create mode 100644 examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde create mode 100644 examples/nrf51/nrf51_server/nrf51_server.pde create mode 100644 examples/nrf905/nrf905_client/nrf905_client.pde create mode 100644 examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde create mode 100644 examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde create mode 100644 examples/nrf905/nrf905_server/nrf905_server.pde create mode 100644 examples/raspi/Makefile create mode 100644 examples/raspi/RasPiRH.cpp create mode 100644 examples/raspi/rf95/rf95_client/Makefile create mode 100644 examples/raspi/rf95/rf95_client/rf95_client.cpp create mode 100644 examples/raspi/rf95/rf95_mesh_client/Makefile create mode 100644 examples/raspi/rf95/rf95_mesh_client/rf95_mesh_client.cpp create mode 100644 examples/raspi/rf95/rf95_mesh_server1/Makefile create mode 100644 examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp create mode 100644 examples/raspi/rf95/rf95_mesh_server2/Makefile create mode 100644 examples/raspi/rf95/rf95_mesh_server2/rf95_mesh_server2.cpp create mode 100644 examples/raspi/rf95/rf95_mesh_server3/Makefile create mode 100644 examples/raspi/rf95/rf95_mesh_server3/rf95_mesh_server3.cpp create mode 100644 examples/raspi/rf95/rf95_reliable_datagram_client/Makefile create mode 100644 examples/raspi/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.cpp create mode 100644 examples/raspi/rf95/rf95_reliable_datagram_server/Makefile create mode 100644 examples/raspi/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.cpp create mode 100644 examples/raspi/rf95/rf95_router_client/Makefile create mode 100644 examples/raspi/rf95/rf95_router_client/rf95_router_client.cpp create mode 100644 examples/raspi/rf95/rf95_router_server1/Makefile create mode 100644 examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp create mode 100644 examples/raspi/rf95/rf95_router_server2/Makefile create mode 100644 examples/raspi/rf95/rf95_router_server2/rf95_router_server2.cpp create mode 100644 examples/raspi/rf95/rf95_router_server3/Makefile create mode 100644 examples/raspi/rf95/rf95_router_server3/rf95_router_server3.cpp create mode 100644 examples/raspi/rf95/rf95_router_test/Makefile create mode 100644 examples/raspi/rf95/rf95_router_test/rf95_router_test.cpp create mode 100644 examples/raspi/rf95/rf95_server/Makefile create mode 100644 examples/raspi/rf95/rf95_server/rf95_server.cpp create mode 100644 examples/rf22/rf22_client/rf22_client.pde create mode 100644 examples/rf22/rf22_mesh_client/rf22_mesh_client.pde create mode 100644 examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde create mode 100644 examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde create mode 100644 examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde create mode 100644 examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde create mode 100644 examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde create mode 100644 examples/rf22/rf22_router_client/rf22_router_client.pde create mode 100644 examples/rf22/rf22_router_server1/rf22_router_server1.pde create mode 100644 examples/rf22/rf22_router_server2/rf22_router_server2.pde create mode 100644 examples/rf22/rf22_router_server3/rf22_router_server3.pde create mode 100644 examples/rf22/rf22_router_test/rf22_router_test.pde create mode 100644 examples/rf22/rf22_server/rf22_server.pde create mode 100644 examples/rf24/rf24_client/rf24_client.pde create mode 100644 examples/rf24/rf24_lowpower_client/rf24_lowpower_client.pde create mode 100644 examples/rf24/rf24_reliable_datagram_client/rf24_reliable_datagram_client.pde create mode 100644 examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde create mode 100644 examples/rf24/rf24_server/rf24_server.pde create mode 100644 examples/rf69/rf69_client/rf69_client.pde create mode 100644 examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde create mode 100644 examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde create mode 100644 examples/rf69/rf69_server/rf69_server.pde create mode 100644 examples/rf95/rf95_client/rf95_client.pde create mode 100644 examples/rf95/rf95_encrypted_client/rf95_encrypted_client.pde create mode 100644 examples/rf95/rf95_encrypted_server/rf95_encrypted_server.pde create mode 100644 examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde create mode 100644 examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde create mode 100644 examples/rf95/rf95_server/rf95_server.pde create mode 100644 examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde create mode 100644 examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde create mode 100644 examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde create mode 100644 examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde create mode 100644 project.cfg create mode 100644 tools/chain.conf create mode 100755 tools/etherSimulator.pl create mode 100755 tools/simBuild create mode 100644 tools/simMain.cpp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..814fc7b --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +This software is Copyright (C) 2008 Mike McCauley. Use is subject to license +conditions. The main licensing options available are GPL V3 or Commercial: + +Open Source Licensing GPL V3 + +This is the appropriate option if you want to share the source code of your +application with everyone you distribute it to, and you also want to give them +the right to share who uses it. If you wish to use this software under Open +Source Licensing, you must contribute all your source code to the open source +community in accordance with the GPL Version 3 when your application is +distributed. See http://www.gnu.org/copyleft/gpl.html + +Commercial Licensing + +This is the appropriate option if you are creating proprietary applications +and you are not prepared to distribute and share the source code of your +application. Contact info@open.com.au for details. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..873bb6f --- /dev/null +++ b/MANIFEST @@ -0,0 +1,181 @@ +RadioHead/LICENSE +RadioHead/MANIFEST +RadioHead/project.cfg +RadioHead/RadioHead.h +RadioHead/RH_ASK.cpp +RadioHead/RH_ASK.h +RadioHead/RH_ABZ.cpp +RadioHead/RH_ABZ.h +RadioHead/RHCRC.cpp +RadioHead/RHCRC.h +RadioHead/RHDatagram.cpp +RadioHead/RHDatagram.h +RadioHead/RHEncryptedDriver.h +RadioHead/RHEncryptedDriver.cpp +RadioHead/RHGenericDriver.cpp +RadioHead/RHGenericDriver.h +RadioHead/RHGenericSPI.cpp +RadioHead/RHGenericSPI.h +RadioHead/RHHardwareSPI.cpp +RadioHead/RHHardwareSPI.h +RadioHead/RHMesh.cpp +RadioHead/RHMesh.h +RadioHead/RHReliableDatagram.cpp +RadioHead/RHReliableDatagram.h +RadioHead/RH_CC110.cpp +RadioHead/RH_CC110.h +RadioHead/RH_E32.cpp +RadioHead/RH_E32.h +RadioHead/RH_NRF24.cpp +RadioHead/RH_NRF24.h +RadioHead/RH_NRF51.cpp +RadioHead/RH_NRF51.h +RadioHead/RH_NRF905.cpp +RadioHead/RH_NRF905.h +RadioHead/RH_RF22.cpp +RadioHead/RH_RF22.h +RadioHead/RH_RF24.cpp +RadioHead/RH_RF24.h +RadioHead/RH_RF69.cpp +RadioHead/RH_RF69.h +RadioHead/RH_MRF89.cpp +RadioHead/RH_MRF89.h +RadioHead/RH_RF95.cpp +RadioHead/RH_RF95.h +RadioHead/RH_TCP.cpp +RadioHead/RH_TCP.h +RadioHead/RHRouter.cpp +RadioHead/RHRouter.h +RadioHead/RH_Serial.cpp +RadioHead/RH_Serial.h +RadioHead/RHSoftwareSPI.cpp +RadioHead/RHSoftwareSPI.h +RadioHead/RHSPIDriver.cpp +RadioHead/RHSPIDriver.h +RadioHead/RHTcpProtocol.h +RadioHead/RHNRFSPIDriver.cpp +RadioHead/RHNRFSPIDriver.h +RadioHead/RHutil +RadioHead/RHutil/atomic.h +RadioHead/RHutil/simulator.h +RadioHead/RHutil/HardwareSerial.h +RadioHead/RHutil/HardwareSerial.cpp +RadioHead/RHutil/RasPi.cpp +RadioHead/RHutil/RasPi.h +RadioHead/RHutil_pigpio/RasPi.cpp +RadioHead/RHutil_pigpio/RasPi.h +RadioHead/examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde +RadioHead/examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde +RadioHead/examples/ask/ask_transmitter/ask_transmitter.pde +RadioHead/examples/ask/ask_receiver/ask_receiver.pde +RadioHead/examples/cc110/cc110_client/cc110_client.pde +RadioHead/examples/cc110/cc110_server/cc110_server.pde +RadioHead/examples/e32/e32_client/e32_client.pde +RadioHead/examples/e32/e32_server/e32_server.pde +RadioHead/examples/abz/abz_client/abz_client.pde +RadioHead/examples/abz/abz_server/abz_server.pde +RadioHead/examples/rf95/rf95_client/rf95_client.pde +RadioHead/examples/rf95/rf95_server/rf95_server.pde +RadioHead/examples/rf95/rf95_encrypted_client/rf95_encrypted_client.pde +RadioHead/examples/rf95/rf95_encrypted_server/rf95_encrypted_server.pde +RadioHead/examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde +RadioHead/examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde +RadioHead/examples/rf22/rf22_client/rf22_client.pde +RadioHead/examples/rf22/rf22_mesh_client/rf22_mesh_client.pde +RadioHead/examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde +RadioHead/examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde +RadioHead/examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde +RadioHead/examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde +RadioHead/examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde +RadioHead/examples/rf22/rf22_router_client/rf22_router_client.pde +RadioHead/examples/rf22/rf22_router_server1/rf22_router_server1.pde +RadioHead/examples/rf22/rf22_router_server2/rf22_router_server2.pde +RadioHead/examples/rf22/rf22_router_server3/rf22_router_server3.pde +RadioHead/examples/rf22/rf22_router_test/rf22_router_test.pde +RadioHead/examples/rf22/rf22_server/rf22_server.pde +RadioHead/examples/rf24/rf24_client/rf24_client.pde +RadioHead/examples/rf24/rf24_lowpower_client/rf24_lowpower_client.pde +RadioHead/examples/rf24/rf24_reliable_datagram_client/rf24_reliable_datagram_client.pde +RadioHead/examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde +RadioHead/examples/rf24/rf24_server/rf24_server.pde +RadioHead/examples/rf69/rf69_client/rf69_client.pde +RadioHead/examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde +RadioHead/examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde +RadioHead/examples/rf69/rf69_server/rf69_server.pde +RadioHead/examples/mrf89/mrf89_client/mrf89_client.pde +RadioHead/examples/mrf89/mrf89_server/mrf89_server.pde +RadioHead/examples/nrf24/nrf24_client/nrf24_client.pde +RadioHead/examples/nrf24/nrf24_encrypted_server/nrf24_encrypted_server.pde +RadioHead/examples/nrf24/nrf24_encrypted_client/nrf24_encrypted_client.pde +RadioHead/examples/nrf24/nrf24_server/nrf24_server.pde +RadioHead/examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde +RadioHead/examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde +RadioHead/examples/nrf51/nrf51_client/nrf51_client.pde +RadioHead/examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde +RadioHead/examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde +RadioHead/examples/nrf51/nrf51_server/nrf51_server.pde +RadioHead/examples/nrf51/nrf51_audio_tx/nrf51_audio_tx.pde +RadioHead/examples/nrf51/nrf51_audio_tx/nrf51_audio.pdf +RadioHead/examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde +RadioHead/examples/nrf905/nrf905_client/nrf905_client.pde +RadioHead/examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde +RadioHead/examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde +RadioHead/examples/nrf905/nrf905_server/nrf905_server.pde +RadioHead/examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde +RadioHead/examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde +RadioHead/examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde +RadioHead/examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde +RadioHead/examples/raspi/RasPiRH.cpp +RadioHead/examples/raspi/Makefile +RadioHead/examples/raspi/rf95/rf95_reliable_datagram_client/Makefile +RadioHead/examples/raspi/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.cpp +RadioHead/examples/raspi/rf95/rf95_reliable_datagram_server/Makefile +RadioHead/examples/raspi/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.cpp +RadioHead/examples/raspi/rf95/rf95_server/Makefile +RadioHead/examples/raspi/rf95/rf95_server/rf95_server.cpp +RadioHead/examples/raspi/rf95/rf95_router_test/Makefile +RadioHead/examples/raspi/rf95/rf95_router_test/rf95_router_test.cpp +RadioHead/examples/raspi/rf95/rf95_mesh_server3/rf95_mesh_server3.cpp +RadioHead/examples/raspi/rf95/rf95_mesh_server3/Makefile +RadioHead/examples/raspi/rf95/rf95_mesh_client/Makefile +RadioHead/examples/raspi/rf95/rf95_mesh_client/rf95_mesh_client.cpp +RadioHead/examples/raspi/rf95/rf95_mesh_server2/Makefile +RadioHead/examples/raspi/rf95/rf95_mesh_server2/rf95_mesh_server2.cpp +RadioHead/examples/raspi/rf95/rf95_client/Makefile +RadioHead/examples/raspi/rf95/rf95_client/rf95_client.cpp +RadioHead/examples/raspi/rf95/rf95_router_client/Makefile +RadioHead/examples/raspi/rf95/rf95_router_client/rf95_router_client.cpp +RadioHead/examples/raspi/rf95/rf95_router_server2/Makefile +RadioHead/examples/raspi/rf95/rf95_router_server2/rf95_router_server2.cpp +RadioHead/examples/raspi/rf95/rf95_router_server3/rf95_router_server3.cpp +RadioHead/examples/raspi/rf95/rf95_router_server3/Makefile +RadioHead/examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp +RadioHead/examples/raspi/rf95/rf95_router_server1/Makefile +RadioHead/examples/raspi/rf95/rf95_mesh_server1/Makefile +RadioHead/examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp +RadioHead/tools/etherSimulator.pl +RadioHead/tools/chain.conf +RadioHead/tools/simMain.cpp +RadioHead/tools/simBuild +RadioHead/doc +RadioHead/STM32ArduinoCompat/HardwareSerial.cpp +RadioHead/STM32ArduinoCompat/HardwareSerial.h +RadioHead/STM32ArduinoCompat/HardwareSPI.cpp +RadioHead/STM32ArduinoCompat/HardwareSPI.h +RadioHead/STM32ArduinoCompat/wirish.cpp +RadioHead/STM32ArduinoCompat/wirish.h +RadioHead/STM32ArduinoCompat/README +RadioHead/RH_RF24_property_data/convert.pl +RadioHead/RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h +RadioHead/RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h +RadioHead/RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h +RadioHead/RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h +RadioHead/RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h +RadioHead/RF24configs/README +RadioHead/MGOSCompat/MGOS.h +RadioHead/MGOSCompat/HardwareSPI.cpp +RadioHead/MGOSCompat/MGOS.cpp +RadioHead/MGOSCompat/HardwareSerial.h +RadioHead/MGOSCompat/README +RadioHead/MGOSCompat/HardwareSerial.cpp +RadioHead/MGOSCompat/HardwareSPI.h diff --git a/MGOSCompat/HardwareSPI.cpp b/MGOSCompat/HardwareSPI.cpp new file mode 100644 index 0000000..d7d74cf --- /dev/null +++ b/MGOSCompat/HardwareSPI.cpp @@ -0,0 +1,154 @@ +// ArduinoCompat/HardwareSPI.cpp +// +// Interface between Arduino-like SPI interface and STM32F4 Discovery and similar +// using STM32F4xx_DSP_StdPeriph_Lib_V1.3.0 + +#include +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + +#include +#include +#include + +HardwareSPI::HardwareSPI(uint32_t spiPortNumber) : spiPortNumber(spiPortNumber) +{ +} + +void HardwareSPI::begin(int frequency, uint32_t bitOrder, uint32_t mode) +{ + //Set the SPI tx/rx buffer pointers. + txn.fd.tx_data = spiTXBuf; + txn.fd.rx_data = spiRXBuf; + + txn.freq = frequency; + this->bitOrder = bitOrder; + txn.mode = mode; +#ifdef RH_USE_SPI + txn.cs = mgos_sys_config_get_rh_spi_cs(); +#else + txn.cs = -1; +#endif +} + +void HardwareSPI::end(void) +{ + struct mgos_spi *spi = mgos_spi_get_global(); + mgos_spi_close(spi); +} + +uint8_t HardwareSPI::reverseBits(uint8_t value) +{ + value = (value & 0xF0) >> 4 | (value & 0x0F) << 4; + value = (value & 0xCC) >> 2 | (value & 0x33) << 2; + value = (value & 0xAA) >> 1 | (value & 0x55) << 1; + return value; +} + +uint8_t HardwareSPI::transfer(uint8_t data) +{ + uint8_t status=0; + txn.fd.len=1; + spiTXBuf[0]=data; + if( bitOrder != MSBFIRST ) { + spiTXBuf[0]=reverseBits(spiTXBuf[0]); + } + bool success = mgos_spi_run_txn( mgos_spi_get_global(), true, &txn); + if( !success ) { + LOG(LL_INFO, ("%s: Failed SPI transfer()", __FUNCTION__) ); + } + status = spiRXBuf[0]; + if( bitOrder != MSBFIRST ) { + status = reverseBits(status); + } + return status; +} + +uint8_t HardwareSPI::transfer2B(uint8_t byte0, uint8_t byte1) +{ + uint8_t status=0; + txn.fd.len=2; + spiTXBuf[0]=byte0; + spiTXBuf[1]=byte1; + if( bitOrder != MSBFIRST ) { + spiTXBuf[0]=reverseBits(spiTXBuf[0]); + spiTXBuf[1]=reverseBits(spiTXBuf[1]); + } + bool success = mgos_spi_run_txn( mgos_spi_get_global(), true, &txn); + if( !success ) { + LOG(LL_INFO, ("%s: Failed SPI transfer()", __FUNCTION__) ); + } + + status = spiRXBuf[0]; + if( bitOrder != MSBFIRST ) { + status = reverseBits(status); + } + return status; +} + +uint8_t HardwareSPI::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len) { + uint8_t status=0; + if( len+1 <= SPI_RX_BUFFER_SIZE ) { + txn.fd.len=len+1; + memset(spiTXBuf, 0, SPI_RX_BUFFER_SIZE); + spiTXBuf[0]=reg; + if( bitOrder != MSBFIRST ) { + spiTXBuf[0]=reverseBits(spiTXBuf[0]); + } + bool success = mgos_spi_run_txn( mgos_spi_get_global(), true, &txn); + if( !success ) { + LOG(LL_INFO, ("%s: Failed SPI transfer()", __FUNCTION__) ); + } + if( bitOrder != MSBFIRST ) { + uint8_t index=0; + for( index=0 ; index +#include +#include + +extern "C" +{ + struct mgos_spi *mgos_spi_get_global(void); + bool mgos_spi_run_txn(struct mgos_spi *spi, bool full_duplex, const struct mgos_spi_txn *txn); +} + +//Not used on MGOS as SPI config is set in mos.yml +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x01 +#define SPI_MODE2 0x03 +#define SPI_MODE3 0x02 + +#define SPI_TX_BUFFER_SIZE 64 +#define SPI_RX_BUFFER_SIZE 64 + +class HardwareSPI +{ +public: + HardwareSPI(uint32_t spiPortNumber); // Only port SPI1 is currently supported + void begin(int frequency, uint32_t bitOrder, uint32_t mode); + void end(void); + uint8_t reverseBits(uint8_t value); + int8_t getCSGpio(); + uint8_t transfer(uint8_t data); + uint8_t transfer2B(uint8_t byte0, uint8_t byte1); + uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len); + uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len); +private: + uint32_t spiPortNumber; // Not used + struct mgos_spi_txn txn; + uint32_t bitOrder; + //Define spi TX and RX buffers.This is a little wasteful of memory + //but no dynamic memory allocation fits with the RadioHead library. + uint8_t spiTXBuf[SPI_TX_BUFFER_SIZE]; + uint8_t spiRXBuf[SPI_RX_BUFFER_SIZE]; +}; +extern HardwareSPI SPI; + + +#endif diff --git a/MGOSCompat/HardwareSerial.cpp b/MGOSCompat/HardwareSerial.cpp new file mode 100644 index 0000000..7e226d6 --- /dev/null +++ b/MGOSCompat/HardwareSerial.cpp @@ -0,0 +1,189 @@ +// ArduinoCompat/HardwareSerial.cpp +// +// Author: mikem@airspayce.com + +#include +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) +#include +#include + +extern "C" { + static inline void mgos_sys_config_set_rh_serial_baud(int v); + static inline void mgos_sys_config_set_rh_serial_databits(int v); + static inline void mgos_sys_config_set_rh_serial_parity(int v); + static inline void mgos_sys_config_set_rh_serial_stopbits(int v); + + void mgos_uart_config_set_defaults(int uart_no, struct mgos_uart_config *cfg); + bool mgos_uart_configure(int uart_no, const struct mgos_uart_config *cfg); + void mgos_uart_set_rx_enabled(int uart_no, bool enabled); + size_t mgos_uart_read_avail(int uart_no); + size_t mgos_uart_read(int uart_no, void *buf, size_t len); + size_t mgos_uart_write(int uart_no, const void *buf, size_t len); +}; + + +// instantiate Serial objects +HardwareSerial Serial0(0); +HardwareSerial Serial1(1); +HardwareSerial Serial2(2); + +/* + * Serial ports + * + * ESP8266 + * The esp8266 device has two uarts (0 and 1), Uart 1 TX is typically used for debugging. No Uart 1 RX is available on the esp8266. + * + * Uart 0 + * RX = GPIO3 + * TX = GPIO1 + * + * Uart 1 + * TX = GPIO2 + * + * ESP32 + * The esp32 device has three uarts (0,1 and 2). Uart 0 is typically used for debugging/loading code. + * + * Uart 0 + * RX = GPIO3 + * TX = GPIO1 + * CTS = GPIO19 + * RTS = GPIO22 + * + * Uart 1 + * RX = GPIO25 + * TX = GPIO26 + * CTS = GPIO27 + * RTS = GPIO13 + * + * Uart 2 + * RX = GPIO16 + * TX = GPIO17 + * CTS = GPIO14 + * RTS = GPIO15 + */ + +/////////////////////////////////////////////////////////////// +// HardwareSerial +/////////////////////////////////////////////////////////////// + +HardwareSerial::HardwareSerial(int uartIndex) +{ + this->uartIndex=uartIndex; +} + +/** + * @brief Init the serial port. + * @param baud If the baud rate is 0 or -ve then the persistent sorage value + * is used. Starting at the value in mos.yml. + */ +void HardwareSerial::begin(int baud) +{ + struct mgos_uart_config ucfg; + + if( mgos_sys_config_get_rh_serial_baud() != baud ) { + mgos_sys_config_set_rh_serial_baud(baud); + } + + mgos_uart_config_set_defaults(this->uartIndex, &ucfg); + ucfg.baud_rate = mgos_sys_config_get_rh_serial_baud(); + ucfg.num_data_bits = mgos_sys_config_get_rh_serial_databits(); + ucfg.parity = (mgos_uart_parity)mgos_sys_config_get_rh_serial_parity(); + ucfg.stop_bits = (mgos_uart_stop_bits)mgos_sys_config_get_rh_serial_stopbits(); + mgos_uart_configure(this->uartIndex, &ucfg); + if( mgos_uart_configure(this->uartIndex, &ucfg) ) { + mgos_uart_set_rx_enabled(this->uartIndex, true); + mgos_uart_set_dispatcher(this->uartIndex, NULL, NULL); + } + +#ifdef NO_ESP32_RXD_PULLUP + if( this->uartIndex == 0 ) { + mgos_gpio_setup_input(3, MGOS_GPIO_PULL_NONE); + } + else if( this->uartIndex == 1 ) { + mgos_gpio_setup_input(25, MGOS_GPIO_PULL_NONE); + } + else if( this->uartIndex == 2 ) { + mgos_gpio_setup_input(16, MGOS_GPIO_PULL_NONE); + } +#endif +} + +void HardwareSerial::end() +{ + mgos_uart_set_rx_enabled(this->uartIndex, false); +} + +int HardwareSerial::available(void) +{ + size_t reqRxByteCount=1; + //We have to read the byte because Mongoose OS requires a return + //to the RTOS in order to update the value read by mgos_uart_read_avail() + rxByteCountAvail = mgos_uart_read(this->uartIndex, &rxByte, reqRxByteCount); + return rxByteCountAvail; +} + +int HardwareSerial::read(void) +{ + return rxByte; +} + +size_t HardwareSerial::write(uint8_t ch) +{ + size_t wr_byte_count = 0; + + wr_byte_count = mgos_uart_write(this->uartIndex, &ch, 1); + + return wr_byte_count; +} + +size_t HardwareSerial::print(char ch) +{ + printf("%c", ch); + return 0; +} + +size_t HardwareSerial::println(char ch) +{ + printf("%c\n", ch); + return 0; +} + +size_t HardwareSerial::print(unsigned char ch, int base) +{ + if( base == DEC ) { + printf("%d", ch); + } + else if( base == HEX ) { + printf("%02x", ch); + } + else if( base == OCT ) { + printf("%o", ch); + } + //TODO Add binary print + return 0; +} + +size_t HardwareSerial::println(unsigned char ch, int base) +{ + print((unsigned int)ch, base); + printf("\n"); + return 0; +} + +size_t HardwareSerial::println(const char* s) +{ + if( s ) { + printf("%s\n",s); + } + return 0; +} + +size_t HardwareSerial::print(const char* s) +{ + if( s) { + printf(s); + } + return 0; +} + +#endif diff --git a/MGOSCompat/HardwareSerial.h b/MGOSCompat/HardwareSerial.h new file mode 100644 index 0000000..4ffb6b5 --- /dev/null +++ b/MGOSCompat/HardwareSerial.h @@ -0,0 +1,49 @@ +// ArduinoCompat/HardwareSerial.h +// Mongoose OS implementation of Arduino compatible serial class + +#include +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) +#ifndef _HardwareSerial_h +#define _HardwareSerial_h + +#include +#include +#include + +// Mostly compatible wuith Arduino HardwareSerial +// There is just enough here to support RadioHead RH_Serial +class HardwareSerial +{ +public: + HardwareSerial(int uart_index); + void begin(int baud); + void end(); + virtual int available(void); + virtual int read(void); + virtual size_t write(uint8_t); + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + + //These methods will send debug info on the debug serial port (if enabled) + size_t println(unsigned char ch, int base); + size_t print(unsigned char ch, int base); + size_t println(const char ch); + size_t print(const char ch); + size_t println(const char* s); + size_t print(const char* s); + +private: + int uartIndex; + size_t rxByteCountAvail; + uint8_t rxByte; +}; + +extern HardwareSerial Serial0; +extern HardwareSerial Serial1; +extern HardwareSerial Serial2; + +#endif + +#endif diff --git a/MGOSCompat/MGOS.cpp b/MGOSCompat/MGOS.cpp new file mode 100644 index 0000000..bf8ecc8 --- /dev/null +++ b/MGOSCompat/MGOS.cpp @@ -0,0 +1,159 @@ +// RasPi.cpp +// +// Routines for implementing RadioHead on Raspberry Pi +// using BCM2835 library for GPIO +// +// Contributed by Mike Poublon and used with permission + + +#include + +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + +#include "mgos.h" + +int pwmFreq = 1000; +float pwmDutyCycle = 0.5; + +/** + * @brief Set the direction of a GPIO pin. + * @param pin the Pin whose direction is to be set. + * @param mode The direction of the pin (OUTPUT or INPUT) + **/ +void pinMode(uint8_t pin, WiringPinMode mode) +{ + + //SPI CS GPIO controlled by MGOS lib call so don't allow it to be set here + if( SPI.getCSGpio() != pin ) { + if (mode == OUTPUT) + { + mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_OUTPUT); + } + else if (mode == OUTPUT_OPEN_DRAIN) + { + mgos_gpio_set_pull(pin, MGOS_GPIO_PULL_UP); + mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_OUTPUT); + } + else if (mode == INPUT || mode == INPUT_FLOATING ) + { + mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_INPUT); + } + else if (mode == INPUT_ANALOG) + { + mgos_adc_enable(pin); + } + else if (mode == INPUT_PULLUP) + { + mgos_gpio_set_pull(pin, MGOS_GPIO_PULL_UP); + mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_INPUT); + } + else if (mode == INPUT_PULLDOWN) + { + mgos_gpio_set_pull(pin, MGOS_GPIO_PULL_DOWN); + mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_INPUT); + } + else if (mode == PWM) + { + mgos_pwm_set(pin, pwmFreq, pwmDutyCycle); + } + else if (mode == PWM_OPEN_DRAIN) { + mgos_pwm_set(pin, pwmFreq, pwmDutyCycle); + } + } +} + +/** + * @brief Set the state of a GPIO pin. + * @param pin the Pin whose state is to be set. + * @param value The state of the pin. + */ +void digitalWrite(unsigned char pin, unsigned char value) +{ + + //SPI CS GPIO controlled by MGOS lib call so don't allow it to be set here + if( SPI.getCSGpio() != pin ) { + mgos_gpio_write(pin,value); + } +} + +/** + * @brief Read the state of a GPIO pin. + * @param pin the Pin whose state is to be set. + * @return 1 If high, 0 if low. + */ +uint8_t digitalRead(uint8_t pin) +{ + uint8_t pinState=0; + //SPI CS GPIO controlled by MGOS lib call so don't allow it to be set here + if( SPI.getCSGpio() != pin ) { + pinState = (uint8_t)mgos_gpio_read(pin); + } + return pinState; +} + +/** + * @brief Get the number of elapsed milliseconds since the last boot. + */ +uint32_t millis(void) +{ + return (uint32_t)mgos_uptime_micros()/1000; +} + +/** + * @brief Provide a delay in milliseconds. + * @param ms The number of Milli Seconds to delay. + */ +void delay (unsigned long ms) +{ + mgos_msleep(ms); +} + +/** + * @brief Generate a random number between limits. + * @param min The minimum random value to be generated. + * @param max The maximum random value to be generated. + */ +long random(long min, long max) +{ + return mgos_rand_range( (float)min, (float)max); +} + +static void mgos_gpio_int_handler(int pin, void *arg) { + void (*handler)(void) = (void (*)())arg; + //Note that this handler is executed in interrupt context (ISR) + //therefore ensure that actions performed here are acceptable for the + //platform on which the code will execute. + //E.G + //Use of the LOG macro to send debug data on the serial port crashes + //esp8266 and esp32 code. + handler(); + (void) pin; + (void) arg; +} + +void attachInterrupt(uint8_t pin, void (*handler)(void), int rh_mode) +{ + mgos_gpio_int_mode mgos_mode = MGOS_GPIO_INT_NONE; + if( rh_mode == CHANGE ) { + mgos_mode = MGOS_GPIO_INT_EDGE_ANY; + } else if( rh_mode == FALLING ) { + mgos_mode = MGOS_GPIO_INT_EDGE_NEG; + } else if( rh_mode == RISING ) { + mgos_mode = MGOS_GPIO_INT_EDGE_POS; + } + mgos_gpio_set_int_handler_isr((int)pin, mgos_mode, mgos_gpio_int_handler, (void*)handler); +} + +void enableInterupt(uint8_t pin) { + mgos_gpio_enable_int(pin); +} + +/** + * @brief Perform functions that under Mongoose OS we would normally return + * to the RTOS. E,G flush the TX UART buffer. + */ +void mgosYield(void) { + mgos_uart_flush(RH_SERIAL_PORT); +} + +#endif diff --git a/MGOSCompat/MGOS.h b/MGOSCompat/MGOS.h new file mode 100644 index 0000000..31f0da2 --- /dev/null +++ b/MGOSCompat/MGOS.h @@ -0,0 +1,101 @@ +// MGOS.h +// +// Routines for implementing RadioHead when using Mongoose OS +// see https://mongoose-os.com/mos.html for H/W support when using Mongoose OS +// Contributed by Paul Austen + +#ifndef MGOS_h +#define MGOS_h + +#define PROGMEM +#define memcpy_P memcpy + +#define DEC 10 +#define HEX 16 +#define OCT 8 +#define BIN 2 + +#define HIGH 0x1 +#define LOW 0x0 + +#define LSBFIRST 0 +#define MSBFIRST 1 + +#define CHANGE 1 +#define FALLING 2 +#define RISING 3 + +typedef enum WiringPinMode { + OUTPUT, /**< Basic digital output: when the pin is HIGH, the + voltage is held at +3.3v (Vcc) and when it is LOW, it + is pulled down to ground. */ + + OUTPUT_OPEN_DRAIN, /**< In open drain mode, the pin indicates + "low" by accepting current flow to ground + and "high" by providing increased + impedance. An example use would be to + connect a pin to a bus line (which is pulled + up to a positive voltage by a separate + supply through a large resistor). When the + pin is high, not much current flows through + to ground and the line stays at positive + voltage; when the pin is low, the bus + "drains" to ground with a small amount of + current constantly flowing through the large + resistor from the external supply. In this + mode, no current is ever actually sourced + from the pin. */ + + INPUT, /**< Basic digital input. The pin voltage is sampled; when + it is closer to 3.3v (Vcc) the pin status is high, and + when it is closer to 0v (ground) it is low. If no + external circuit is pulling the pin voltage to high or + low, it will tend to randomly oscillate and be very + sensitive to noise (e.g., a breath of air across the pin + might cause the state to flip). */ + + INPUT_ANALOG, /**< This is a special mode for when the pin will be + used for analog (not digital) reads. Enables ADC + conversion to be performed on the voltage at the + pin. */ + + INPUT_PULLUP, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled up" towards +3.3v. This means + the state will be high unless an external device + is specifically pulling the pin down to ground, + in which case the "gentle" pull up will not + affect the state of the input. */ + + INPUT_PULLDOWN, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled down" towards 0v. This means + the state will be low unless an external device + is specifically pulling the pin up to 3.3v, in + which case the "gentle" pull down will not + affect the state of the input. */ + + INPUT_FLOATING, /**< Synonym for INPUT. */ + + PWM, /**< This is a special mode for when the pin will be used for + PWM output (a special case of digital output). */ + + PWM_OPEN_DRAIN, /**< Like PWM, except that instead of alternating + cycles of LOW and HIGH, the voltage on the pin + consists of alternating cycles of LOW and + floating (disconnected). */ +} WiringPinMode; + +extern "C" { + void pinMode(uint8_t pin, WiringPinMode mode); + void digitalWrite(unsigned char pin, unsigned char value); + uint8_t digitalRead(uint8_t pin); + uint32_t millis(void); + void delay (unsigned long ms); + long random(long min, long max); + void attachInterrupt(uint8_t pin, void (*handler)(void), int rh_mode); + void mgosYield(void); + void enableInterupt(uint8_t pin); +} + +#endif diff --git a/MGOSCompat/README b/MGOSCompat/README new file mode 100644 index 0000000..1342355 --- /dev/null +++ b/MGOSCompat/README @@ -0,0 +1 @@ +This directory contains some files to allow RadioHead to be built on Mongoose OS. diff --git a/RF24configs/README b/RF24configs/README new file mode 100644 index 0000000..d3926a1 --- /dev/null +++ b/RF24configs/README @@ -0,0 +1,19 @@ +This directory contains a selection of radio configuiration files for use by RH_RF24.cpp +They were generated by Silicon Labs Wireless Development Suite (WDS) 3.2.11.0 +The configuration file controls all the basic frequency aand modulation parameters for the radio. +The appropriate one for your application or sketch must be #included by RH_RF24.cpp + +You can generate your own custom configuration file by generatng a new one with WDS, +copying it to this directory with a unique anme and #include it in RH_RF24.cpp + +The file names encode the basic parameters: + +radio_config_Siaaaa_bb_ccc_dddd_ee_ff_gg.h + +where +aaaa = Chip typenukber eg 4464 +bb = Crytsyal frequency in MHz +ccc = RF base frequency in MHz +dddd = Modulation type eg 2GFSK +ee = Data rate in kbps +ff = Deviation in kHz diff --git a/RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h b/RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h new file mode 100755 index 0000000..2b1e785 --- /dev/null +++ b/RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h @@ -0,0 +1,516 @@ +/*! @file radio_config.h + * @brief This file contains the automatically generated + * configurations. + * + * @n WDS GUI Version: 3.2.11.0 + * @n Device: Si4464 Rev.: B1 + * + * @b COPYRIGHT + * @n Silicon Laboratories Confidential + * @n Copyright 2017 Silicon Laboratories, Inc. + * @n http://www.silabs.com + */ + +#ifndef RADIO_CONFIG_H_ +#define RADIO_CONFIG_H_ + +// USER DEFINED PARAMETERS +// Define your own parameters here + +// INPUT DATA +/* +// Crys_freq(Hz): 27000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0 +// MOD_type: 3 Rsymb(sps): 5000 Fdev(Hz): 10000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 3 +// RF Freq.(MHz): 434 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1 +// +// # RX IF frequency is -421875 Hz +// # WB filter 2 (BW = 61.84 kHz); NB-filter 2 (BW = 61.84 kHz) +// +// Modulation index: 4 +*/ + + +// CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 27000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000 + + +// CONFIGURATION COMMANDS + +/* +// Command: RF_POWER_UP +// Description: Command to power-up the device and select the operational mode and functionality. +*/ +#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0x9B, 0xFC, 0xC0 + +/* +// Command: RF_GPIO_PIN_CFG +// Description: Configures the GPIO pins. +*/ +#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_GLOBAL_XO_TUNE_2 +// Number of properties: 2 +// Group ID: 0x00 +// Start ID: 0x00 +// Default values: 0x40, 0x00, +// Descriptions: +// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator. +// GLOBAL_CLK_CFG - Clock configuration options. +*/ +#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00 + +/* +// Set properties: RF_GLOBAL_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x00 +// Start ID: 0x03 +// Default values: 0x20, +// Descriptions: +// GLOBAL_CONFIG - Global configuration settings. +*/ +#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60 + +/* +// Set properties: RF_INT_CTL_ENABLE_2 +// Number of properties: 2 +// Group ID: 0x01 +// Start ID: 0x00 +// Default values: 0x04, 0x00, +// Descriptions: +// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin. +// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin. +*/ +#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x20 + +/* +// Set properties: RF_FRR_CTL_A_MODE_4 +// Number of properties: 4 +// Group ID: 0x02 +// Start ID: 0x00 +// Default values: 0x01, 0x02, 0x09, 0x00, +// Descriptions: +// FRR_CTL_A_MODE - Fast Response Register A Configuration. +// FRR_CTL_B_MODE - Fast Response Register B Configuration. +// FRR_CTL_C_MODE - Fast Response Register C Configuration. +// FRR_CTL_D_MODE - Fast Response Register D Configuration. +*/ +#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_PREAMBLE_TX_LENGTH_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x00 +// Default values: 0x08, +// Descriptions: +// PREAMBLE_TX_LENGTH - Configure length of TX Preamble. +*/ +#define RF_PREAMBLE_TX_LENGTH_1 0x11, 0x10, 0x01, 0x00, 0x0A + +/* +// Set properties: RF_PREAMBLE_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x04 +// Default values: 0x21, +// Descriptions: +// PREAMBLE_CONFIG - General configuration bits for the Preamble field. +*/ +#define RF_PREAMBLE_CONFIG_1 0x11, 0x10, 0x01, 0x04, 0x31 + +/* +// Set properties: RF_SYNC_CONFIG_3 +// Number of properties: 3 +// Group ID: 0x11 +// Start ID: 0x00 +// Default values: 0x01, 0x2D, 0xD4, +// Descriptions: +// SYNC_CONFIG - Sync Word configuration bits. +// SYNC_BITS_31_24 - Sync word. +// SYNC_BITS_23_16 - Sync word. +*/ +#define RF_SYNC_CONFIG_3 0x11, 0x11, 0x03, 0x00, 0x01, 0xB4, 0x2B + +/* +// Set properties: RF_PKT_CONFIG1_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x06 +// Default values: 0x00, +// Descriptions: +// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet. +*/ +#define RF_PKT_CONFIG1_1 0x11, 0x12, 0x01, 0x06, 0x02 + +/* +// Set properties: RF_PKT_FIELD_1_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x0F +// Default values: 0x00, +// Descriptions: +// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1. +*/ +#define RF_PKT_FIELD_1_CONFIG_1 0x11, 0x12, 0x01, 0x0F, 0x04 + +/* +// Set properties: RF_MODEM_MOD_TYPE_12 +// Number of properties: 12 +// Group ID: 0x20 +// Start ID: 0x00 +// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06, +// Descriptions: +// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation. +// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits. +// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer. +// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word. +// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x03, 0x0D, 0x40, 0x05, 0x9B, 0xFC, 0xC0, 0x00, 0x03 + +/* +// Set properties: RF_MODEM_FREQ_DEV_0_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x0C +// Default values: 0xD3, +// Descriptions: +// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0x09 + +/* +// Set properties: RF_MODEM_TX_RAMP_DELAY_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x18 +// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, +// Descriptions: +// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting. +// MODEM_MDM_CTRL - MDM control. +// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation. +// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number). +// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter. +// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter. +*/ +#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x20, 0x10 + +/* +// Set properties: RF_MODEM_BCR_OSR_1_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x22 +// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, +// Descriptions: +// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GEAR - RX BCR loop gear control. +// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop. +*/ +#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x01, 0xC2, 0x01, 0x23, 0x45, 0x00, 0x92, 0x02, 0xC2 + +/* +// Set properties: RF_MODEM_AFC_GEAR_7 +// Number of properties: 7 +// Group ID: 0x20 +// Start ID: 0x2C +// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0, +// Descriptions: +// MODEM_AFC_GEAR - RX AFC loop gear control. +// MODEM_AFC_WAIT - RX AFC loop wait time control. +// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_LIMITER_1 - Set the AFC limiter value. +// MODEM_AFC_LIMITER_0 - Set the AFC limiter value. +// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits. +*/ +#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x10, 0x18, 0x36, 0x80 + +/* +// Set properties: RF_MODEM_AGC_CONTROL_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x35 +// Default values: 0xE0, +// Descriptions: +// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain. +*/ +#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2 + +/* +// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x38 +// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, +// Descriptions: +// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm. +// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors. +// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors. +// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code. +// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector. +*/ +#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0x62, 0x62, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x2A + +/* +// Set properties: RF_MODEM_OOK_CNT1_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x42 +// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, +// Descriptions: +// MODEM_OOK_CNT1 - OOK control. +// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator. +// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors. +// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode. +// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold. +// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold. +// MODEM_ANT_DIV_MODE - Antenna diversity mode settings. +// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm. +*/ +#define RF_MODEM_OOK_CNT1_8 0x11, 0x20, 0x08, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x00, 0x90, 0x01, 0x80 + +/* +// Set properties: RF_MODEM_RSSI_COMP_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x4E +// Default values: 0x32, +// Descriptions: +// MODEM_RSSI_COMP - RSSI compensation value. +*/ +#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40 + +/* +// Set properties: RF_MODEM_CLKGEN_BAND_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x51 +// Default values: 0x08, +// Descriptions: +// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band. +*/ +#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x0A + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x00 +// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x0C +// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5 + +/* +// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x18 +// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, +// Descriptions: +// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00 + +/* +// Set properties: RF_PA_MODE_4 +// Number of properties: 4 +// Group ID: 0x22 +// Start ID: 0x00 +// Default values: 0x08, 0x7F, 0x00, 0x5D, +// Descriptions: +// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size). +// PA_PWR_LVL - Configuration of PA output power level. +// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source. +// PA_TC - Configuration of PA ramping parameters. +*/ +#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x3D + +/* +// Set properties: RF_SYNTH_PFDCP_CPFF_7 +// Number of properties: 7 +// Group ID: 0x23 +// Start ID: 0x00 +// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03, +// Descriptions: +// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection. +// SYNTH_PFDCP_CPINT - Integration charge pump current selection. +// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path. +// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter. +// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter. +// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter. +// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter. +*/ +#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03 + +/* +// Set properties: RF_FREQ_CONTROL_INTE_8 +// Number of properties: 8 +// Group ID: 0x40 +// Start ID: 0x00 +// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, +// Descriptions: +// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number. +// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration. +// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode. +*/ +#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x3F, 0x0A, 0x5E, 0xD0, 0x4B, 0xDA, 0x20, 0xFE + + +// AUTOMATICALLY GENERATED CODE! +// DO NOT EDIT/MODIFY BELOW THIS LINE! +// -------------------------------------------- + +#ifndef FIRMWARE_LOAD_COMPILE +#define RADIO_CONFIGURATION_DATA_ARRAY { \ + 0x07, RF_POWER_UP, \ + 0x08, RF_GPIO_PIN_CFG, \ + 0x06, RF_GLOBAL_XO_TUNE_2, \ + 0x05, RF_GLOBAL_CONFIG_1, \ + 0x06, RF_INT_CTL_ENABLE_2, \ + 0x08, RF_FRR_CTL_A_MODE_4, \ + 0x05, RF_PREAMBLE_TX_LENGTH_1, \ + 0x05, RF_PREAMBLE_CONFIG_1, \ + 0x07, RF_SYNC_CONFIG_3, \ + 0x05, RF_PKT_CONFIG1_1, \ + 0x05, RF_PKT_FIELD_1_CONFIG_1, \ + 0x10, RF_MODEM_MOD_TYPE_12, \ + 0x05, RF_MODEM_FREQ_DEV_0_1, \ + 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \ + 0x0D, RF_MODEM_BCR_OSR_1_9, \ + 0x0B, RF_MODEM_AFC_GEAR_7, \ + 0x05, RF_MODEM_AGC_CONTROL_1, \ + 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \ + 0x0C, RF_MODEM_OOK_CNT1_8, \ + 0x05, RF_MODEM_RSSI_COMP_1, \ + 0x05, RF_MODEM_CLKGEN_BAND_1, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \ + 0x08, RF_PA_MODE_4, \ + 0x0B, RF_SYNTH_PFDCP_CPFF_7, \ + 0x0C, RF_FREQ_CONTROL_INTE_8, \ + 0x00 \ + } +#else +#define RADIO_CONFIGURATION_DATA_ARRAY { 0 } +#endif + +// DEFAULT VALUES FOR CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000 + +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { } + +#ifndef RADIO_CONFIGURATION_DATA_ARRAY +#error "This property must be defined!" +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT +#endif + +#define RADIO_CONFIGURATION_DATA { \ + Radio_Configuration_Data_Array, \ + RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \ + RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \ + RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \ + RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \ + } + +#endif /* RADIO_CONFIG_H_ */ diff --git a/RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h b/RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h new file mode 100755 index 0000000..fffee0b --- /dev/null +++ b/RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h @@ -0,0 +1,516 @@ +/*! @file radio_config.h + * @brief This file contains the automatically generated + * configurations. + * + * @n WDS GUI Version: 3.2.11.0 + * @n Device: Si4464 Rev.: B1 + * + * @b COPYRIGHT + * @n Silicon Laboratories Confidential + * @n Copyright 2017 Silicon Laboratories, Inc. + * @n http://www.silabs.com + */ + +#ifndef RADIO_CONFIG_H_ +#define RADIO_CONFIG_H_ + +// USER DEFINED PARAMETERS +// Define your own parameters here + +// INPUT DATA +/* +// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0 +// MOD_type: 3 Rsymb(sps): 10000 Fdev(Hz): 20000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 3 +// RF Freq.(MHz): 434 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1 +// +// # RX IF frequency is -468750 Hz +// # WB filter 4 (BW = 82.64 kHz); NB-filter 4 (BW = 82.64 kHz) +// +// Modulation index: 4 +*/ + + +// CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000 + + +// CONFIGURATION COMMANDS + +/* +// Command: RF_POWER_UP +// Description: Command to power-up the device and select the operational mode and functionality. +*/ +#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80 + +/* +// Command: RF_GPIO_PIN_CFG +// Description: Configures the GPIO pins. +*/ +#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_GLOBAL_XO_TUNE_2 +// Number of properties: 2 +// Group ID: 0x00 +// Start ID: 0x00 +// Default values: 0x40, 0x00, +// Descriptions: +// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator. +// GLOBAL_CLK_CFG - Clock configuration options. +*/ +#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00 + +/* +// Set properties: RF_GLOBAL_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x00 +// Start ID: 0x03 +// Default values: 0x20, +// Descriptions: +// GLOBAL_CONFIG - Global configuration settings. +*/ +#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60 + +/* +// Set properties: RF_INT_CTL_ENABLE_2 +// Number of properties: 2 +// Group ID: 0x01 +// Start ID: 0x00 +// Default values: 0x04, 0x00, +// Descriptions: +// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin. +// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin. +*/ +#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x20 + +/* +// Set properties: RF_FRR_CTL_A_MODE_4 +// Number of properties: 4 +// Group ID: 0x02 +// Start ID: 0x00 +// Default values: 0x01, 0x02, 0x09, 0x00, +// Descriptions: +// FRR_CTL_A_MODE - Fast Response Register A Configuration. +// FRR_CTL_B_MODE - Fast Response Register B Configuration. +// FRR_CTL_C_MODE - Fast Response Register C Configuration. +// FRR_CTL_D_MODE - Fast Response Register D Configuration. +*/ +#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_PREAMBLE_TX_LENGTH_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x00 +// Default values: 0x08, +// Descriptions: +// PREAMBLE_TX_LENGTH - Configure length of TX Preamble. +*/ +#define RF_PREAMBLE_TX_LENGTH_1 0x11, 0x10, 0x01, 0x00, 0x0A + +/* +// Set properties: RF_PREAMBLE_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x04 +// Default values: 0x21, +// Descriptions: +// PREAMBLE_CONFIG - General configuration bits for the Preamble field. +*/ +#define RF_PREAMBLE_CONFIG_1 0x11, 0x10, 0x01, 0x04, 0x31 + +/* +// Set properties: RF_SYNC_CONFIG_3 +// Number of properties: 3 +// Group ID: 0x11 +// Start ID: 0x00 +// Default values: 0x01, 0x2D, 0xD4, +// Descriptions: +// SYNC_CONFIG - Sync Word configuration bits. +// SYNC_BITS_31_24 - Sync word. +// SYNC_BITS_23_16 - Sync word. +*/ +#define RF_SYNC_CONFIG_3 0x11, 0x11, 0x03, 0x00, 0x01, 0xB4, 0x2B + +/* +// Set properties: RF_PKT_CONFIG1_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x06 +// Default values: 0x00, +// Descriptions: +// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet. +*/ +#define RF_PKT_CONFIG1_1 0x11, 0x12, 0x01, 0x06, 0x02 + +/* +// Set properties: RF_PKT_FIELD_1_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x0F +// Default values: 0x00, +// Descriptions: +// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1. +*/ +#define RF_PKT_FIELD_1_CONFIG_1 0x11, 0x12, 0x01, 0x0F, 0x04 + +/* +// Set properties: RF_MODEM_MOD_TYPE_12 +// Number of properties: 12 +// Group ID: 0x20 +// Start ID: 0x00 +// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06, +// Descriptions: +// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation. +// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits. +// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer. +// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word. +// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x06, 0x1A, 0x80, 0x05, 0xC9, 0xC3, 0x80, 0x00, 0x05 + +/* +// Set properties: RF_MODEM_FREQ_DEV_0_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x0C +// Default values: 0xD3, +// Descriptions: +// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0x76 + +/* +// Set properties: RF_MODEM_TX_RAMP_DELAY_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x18 +// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, +// Descriptions: +// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting. +// MODEM_MDM_CTRL - MDM control. +// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation. +// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number). +// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter. +// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter. +*/ +#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x20, 0x20 + +/* +// Set properties: RF_MODEM_BCR_OSR_1_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x22 +// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, +// Descriptions: +// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GEAR - RX BCR loop gear control. +// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop. +*/ +#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x01, 0x77, 0x01, 0x5D, 0x86, 0x00, 0xAF, 0x02, 0xC2 + +/* +// Set properties: RF_MODEM_AFC_GEAR_7 +// Number of properties: 7 +// Group ID: 0x20 +// Start ID: 0x2C +// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0, +// Descriptions: +// MODEM_AFC_GEAR - RX AFC loop gear control. +// MODEM_AFC_WAIT - RX AFC loop wait time control. +// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_LIMITER_1 - Set the AFC limiter value. +// MODEM_AFC_LIMITER_0 - Set the AFC limiter value. +// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits. +*/ +#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x1D, 0x10, 0x04, 0x80 + +/* +// Set properties: RF_MODEM_AGC_CONTROL_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x35 +// Default values: 0xE0, +// Descriptions: +// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain. +*/ +#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2 + +/* +// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x38 +// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, +// Descriptions: +// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm. +// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors. +// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors. +// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code. +// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector. +*/ +#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0x52, 0x52, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x2A + +/* +// Set properties: RF_MODEM_OOK_CNT1_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x42 +// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, +// Descriptions: +// MODEM_OOK_CNT1 - OOK control. +// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator. +// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors. +// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode. +// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold. +// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold. +// MODEM_ANT_DIV_MODE - Antenna diversity mode settings. +// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm. +*/ +#define RF_MODEM_OOK_CNT1_8 0x11, 0x20, 0x08, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x00, 0xAD, 0x01, 0x80 + +/* +// Set properties: RF_MODEM_RSSI_COMP_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x4E +// Default values: 0x32, +// Descriptions: +// MODEM_RSSI_COMP - RSSI compensation value. +*/ +#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40 + +/* +// Set properties: RF_MODEM_CLKGEN_BAND_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x51 +// Default values: 0x08, +// Descriptions: +// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band. +*/ +#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x0A + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x00 +// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xA2, 0x81, 0x26, 0xAF, 0x3F, 0xEE, 0xC8, 0xC7, 0xDB, 0xF2, 0x02, 0x08 + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x0C +// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0x07, 0x03, 0x15, 0xFC, 0x0F, 0x00, 0xA2, 0x81, 0x26, 0xAF, 0x3F, 0xEE + +/* +// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x18 +// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, +// Descriptions: +// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xC8, 0xC7, 0xDB, 0xF2, 0x02, 0x08, 0x07, 0x03, 0x15, 0xFC, 0x0F, 0x00 + +/* +// Set properties: RF_PA_MODE_4 +// Number of properties: 4 +// Group ID: 0x22 +// Start ID: 0x00 +// Default values: 0x08, 0x7F, 0x00, 0x5D, +// Descriptions: +// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size). +// PA_PWR_LVL - Configuration of PA output power level. +// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source. +// PA_TC - Configuration of PA ramping parameters. +*/ +#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x3D + +/* +// Set properties: RF_SYNTH_PFDCP_CPFF_7 +// Number of properties: 7 +// Group ID: 0x23 +// Start ID: 0x00 +// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03, +// Descriptions: +// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection. +// SYNTH_PFDCP_CPINT - Integration charge pump current selection. +// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path. +// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter. +// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter. +// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter. +// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter. +*/ +#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03 + +/* +// Set properties: RF_FREQ_CONTROL_INTE_8 +// Number of properties: 8 +// Group ID: 0x40 +// Start ID: 0x00 +// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, +// Descriptions: +// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number. +// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration. +// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode. +*/ +#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x38, 0x0E, 0xEE, 0xEE, 0x44, 0x44, 0x20, 0xFE + + +// AUTOMATICALLY GENERATED CODE! +// DO NOT EDIT/MODIFY BELOW THIS LINE! +// -------------------------------------------- + +#ifndef FIRMWARE_LOAD_COMPILE +#define RADIO_CONFIGURATION_DATA_ARRAY { \ + 0x07, RF_POWER_UP, \ + 0x08, RF_GPIO_PIN_CFG, \ + 0x06, RF_GLOBAL_XO_TUNE_2, \ + 0x05, RF_GLOBAL_CONFIG_1, \ + 0x06, RF_INT_CTL_ENABLE_2, \ + 0x08, RF_FRR_CTL_A_MODE_4, \ + 0x05, RF_PREAMBLE_TX_LENGTH_1, \ + 0x05, RF_PREAMBLE_CONFIG_1, \ + 0x07, RF_SYNC_CONFIG_3, \ + 0x05, RF_PKT_CONFIG1_1, \ + 0x05, RF_PKT_FIELD_1_CONFIG_1, \ + 0x10, RF_MODEM_MOD_TYPE_12, \ + 0x05, RF_MODEM_FREQ_DEV_0_1, \ + 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \ + 0x0D, RF_MODEM_BCR_OSR_1_9, \ + 0x0B, RF_MODEM_AFC_GEAR_7, \ + 0x05, RF_MODEM_AGC_CONTROL_1, \ + 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \ + 0x0C, RF_MODEM_OOK_CNT1_8, \ + 0x05, RF_MODEM_RSSI_COMP_1, \ + 0x05, RF_MODEM_CLKGEN_BAND_1, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \ + 0x08, RF_PA_MODE_4, \ + 0x0B, RF_SYNTH_PFDCP_CPFF_7, \ + 0x0C, RF_FREQ_CONTROL_INTE_8, \ + 0x00 \ + } +#else +#define RADIO_CONFIGURATION_DATA_ARRAY { 0 } +#endif + +// DEFAULT VALUES FOR CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000 + +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { } + +#ifndef RADIO_CONFIGURATION_DATA_ARRAY +#error "This property must be defined!" +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT +#endif + +#define RADIO_CONFIGURATION_DATA { \ + Radio_Configuration_Data_Array, \ + RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \ + RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \ + RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \ + RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \ + } + +#endif /* RADIO_CONFIG_H_ */ diff --git a/RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h b/RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h new file mode 100755 index 0000000..cc20448 --- /dev/null +++ b/RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h @@ -0,0 +1,516 @@ +/*! @file radio_config.h + * @brief This file contains the automatically generated + * configurations. + * + * @n WDS GUI Version: 3.2.11.0 + * @n Device: Si4464 Rev.: B1 + * + * @b COPYRIGHT + * @n Silicon Laboratories Confidential + * @n Copyright 2017 Silicon Laboratories, Inc. + * @n http://www.silabs.com + */ + +#ifndef RADIO_CONFIG_H_ +#define RADIO_CONFIG_H_ + +// USER DEFINED PARAMETERS +// Define your own parameters here + +// INPUT DATA +/* +// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0 +// MOD_type: 3 Rsymb(sps): 5000 Fdev(Hz): 10000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 3 +// RF Freq.(MHz): 434 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1 +// +// # RX IF frequency is -468750 Hz +// # WB filter 1 (BW = 57.23 kHz); NB-filter 1 (BW = 57.23 kHz) +// +// Modulation index: 4 +*/ + + +// CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000 + + +// CONFIGURATION COMMANDS + +/* +// Command: RF_POWER_UP +// Description: Command to power-up the device and select the operational mode and functionality. +*/ +#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80 + +/* +// Command: RF_GPIO_PIN_CFG +// Description: Configures the GPIO pins. +*/ +#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_GLOBAL_XO_TUNE_2 +// Number of properties: 2 +// Group ID: 0x00 +// Start ID: 0x00 +// Default values: 0x40, 0x00, +// Descriptions: +// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator. +// GLOBAL_CLK_CFG - Clock configuration options. +*/ +#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00 + +/* +// Set properties: RF_GLOBAL_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x00 +// Start ID: 0x03 +// Default values: 0x20, +// Descriptions: +// GLOBAL_CONFIG - Global configuration settings. +*/ +#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60 + +/* +// Set properties: RF_INT_CTL_ENABLE_2 +// Number of properties: 2 +// Group ID: 0x01 +// Start ID: 0x00 +// Default values: 0x04, 0x00, +// Descriptions: +// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin. +// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin. +*/ +#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x20 + +/* +// Set properties: RF_FRR_CTL_A_MODE_4 +// Number of properties: 4 +// Group ID: 0x02 +// Start ID: 0x00 +// Default values: 0x01, 0x02, 0x09, 0x00, +// Descriptions: +// FRR_CTL_A_MODE - Fast Response Register A Configuration. +// FRR_CTL_B_MODE - Fast Response Register B Configuration. +// FRR_CTL_C_MODE - Fast Response Register C Configuration. +// FRR_CTL_D_MODE - Fast Response Register D Configuration. +*/ +#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_PREAMBLE_TX_LENGTH_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x00 +// Default values: 0x08, +// Descriptions: +// PREAMBLE_TX_LENGTH - Configure length of TX Preamble. +*/ +#define RF_PREAMBLE_TX_LENGTH_1 0x11, 0x10, 0x01, 0x00, 0x0A + +/* +// Set properties: RF_PREAMBLE_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x04 +// Default values: 0x21, +// Descriptions: +// PREAMBLE_CONFIG - General configuration bits for the Preamble field. +*/ +#define RF_PREAMBLE_CONFIG_1 0x11, 0x10, 0x01, 0x04, 0x31 + +/* +// Set properties: RF_SYNC_CONFIG_3 +// Number of properties: 3 +// Group ID: 0x11 +// Start ID: 0x00 +// Default values: 0x01, 0x2D, 0xD4, +// Descriptions: +// SYNC_CONFIG - Sync Word configuration bits. +// SYNC_BITS_31_24 - Sync word. +// SYNC_BITS_23_16 - Sync word. +*/ +#define RF_SYNC_CONFIG_3 0x11, 0x11, 0x03, 0x00, 0x01, 0xB4, 0x2B + +/* +// Set properties: RF_PKT_CONFIG1_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x06 +// Default values: 0x00, +// Descriptions: +// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet. +*/ +#define RF_PKT_CONFIG1_1 0x11, 0x12, 0x01, 0x06, 0x02 + +/* +// Set properties: RF_PKT_FIELD_1_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x0F +// Default values: 0x00, +// Descriptions: +// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1. +*/ +#define RF_PKT_FIELD_1_CONFIG_1 0x11, 0x12, 0x01, 0x0F, 0x04 + +/* +// Set properties: RF_MODEM_MOD_TYPE_12 +// Number of properties: 12 +// Group ID: 0x20 +// Start ID: 0x00 +// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06, +// Descriptions: +// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation. +// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits. +// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer. +// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word. +// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x03, 0x0D, 0x40, 0x05, 0xC9, 0xC3, 0x80, 0x00, 0x02 + +/* +// Set properties: RF_MODEM_FREQ_DEV_0_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x0C +// Default values: 0xD3, +// Descriptions: +// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0xBB + +/* +// Set properties: RF_MODEM_TX_RAMP_DELAY_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x18 +// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, +// Descriptions: +// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting. +// MODEM_MDM_CTRL - MDM control. +// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation. +// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number). +// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter. +// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter. +*/ +#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0x80, 0x00, 0x30, 0x20 + +/* +// Set properties: RF_MODEM_BCR_OSR_1_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x22 +// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, +// Descriptions: +// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GEAR - RX BCR loop gear control. +// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop. +*/ +#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x01, 0x77, 0x01, 0x5D, 0x86, 0x00, 0xAF, 0x02, 0xC2 + +/* +// Set properties: RF_MODEM_AFC_GEAR_7 +// Number of properties: 7 +// Group ID: 0x20 +// Start ID: 0x2C +// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0, +// Descriptions: +// MODEM_AFC_GEAR - RX AFC loop gear control. +// MODEM_AFC_WAIT - RX AFC loop wait time control. +// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_LIMITER_1 - Set the AFC limiter value. +// MODEM_AFC_LIMITER_0 - Set the AFC limiter value. +// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits. +*/ +#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x0F, 0x15, 0x87, 0x80 + +/* +// Set properties: RF_MODEM_AGC_CONTROL_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x35 +// Default values: 0xE0, +// Descriptions: +// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain. +*/ +#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2 + +/* +// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x38 +// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, +// Descriptions: +// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm. +// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors. +// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors. +// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code. +// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector. +*/ +#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0x52, 0x52, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x2A + +/* +// Set properties: RF_MODEM_OOK_CNT1_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x42 +// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, +// Descriptions: +// MODEM_OOK_CNT1 - OOK control. +// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator. +// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors. +// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode. +// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold. +// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold. +// MODEM_ANT_DIV_MODE - Antenna diversity mode settings. +// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm. +*/ +#define RF_MODEM_OOK_CNT1_8 0x11, 0x20, 0x08, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x00, 0xAD, 0x01, 0x80 + +/* +// Set properties: RF_MODEM_RSSI_COMP_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x4E +// Default values: 0x32, +// Descriptions: +// MODEM_RSSI_COMP - RSSI compensation value. +*/ +#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40 + +/* +// Set properties: RF_MODEM_CLKGEN_BAND_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x51 +// Default values: 0x08, +// Descriptions: +// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band. +*/ +#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x0A + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x00 +// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01 + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x0C +// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9 + +/* +// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x18 +// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, +// Descriptions: +// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F + +/* +// Set properties: RF_PA_MODE_4 +// Number of properties: 4 +// Group ID: 0x22 +// Start ID: 0x00 +// Default values: 0x08, 0x7F, 0x00, 0x5D, +// Descriptions: +// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size). +// PA_PWR_LVL - Configuration of PA output power level. +// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source. +// PA_TC - Configuration of PA ramping parameters. +*/ +#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x3D + +/* +// Set properties: RF_SYNTH_PFDCP_CPFF_7 +// Number of properties: 7 +// Group ID: 0x23 +// Start ID: 0x00 +// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03, +// Descriptions: +// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection. +// SYNTH_PFDCP_CPINT - Integration charge pump current selection. +// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path. +// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter. +// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter. +// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter. +// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter. +*/ +#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03 + +/* +// Set properties: RF_FREQ_CONTROL_INTE_8 +// Number of properties: 8 +// Group ID: 0x40 +// Start ID: 0x00 +// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, +// Descriptions: +// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number. +// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration. +// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode. +*/ +#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x38, 0x0E, 0xEE, 0xEE, 0x44, 0x44, 0x20, 0xFE + + +// AUTOMATICALLY GENERATED CODE! +// DO NOT EDIT/MODIFY BELOW THIS LINE! +// -------------------------------------------- + +#ifndef FIRMWARE_LOAD_COMPILE +#define RADIO_CONFIGURATION_DATA_ARRAY { \ + 0x07, RF_POWER_UP, \ + 0x08, RF_GPIO_PIN_CFG, \ + 0x06, RF_GLOBAL_XO_TUNE_2, \ + 0x05, RF_GLOBAL_CONFIG_1, \ + 0x06, RF_INT_CTL_ENABLE_2, \ + 0x08, RF_FRR_CTL_A_MODE_4, \ + 0x05, RF_PREAMBLE_TX_LENGTH_1, \ + 0x05, RF_PREAMBLE_CONFIG_1, \ + 0x07, RF_SYNC_CONFIG_3, \ + 0x05, RF_PKT_CONFIG1_1, \ + 0x05, RF_PKT_FIELD_1_CONFIG_1, \ + 0x10, RF_MODEM_MOD_TYPE_12, \ + 0x05, RF_MODEM_FREQ_DEV_0_1, \ + 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \ + 0x0D, RF_MODEM_BCR_OSR_1_9, \ + 0x0B, RF_MODEM_AFC_GEAR_7, \ + 0x05, RF_MODEM_AGC_CONTROL_1, \ + 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \ + 0x0C, RF_MODEM_OOK_CNT1_8, \ + 0x05, RF_MODEM_RSSI_COMP_1, \ + 0x05, RF_MODEM_CLKGEN_BAND_1, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \ + 0x08, RF_PA_MODE_4, \ + 0x0B, RF_SYNTH_PFDCP_CPFF_7, \ + 0x0C, RF_FREQ_CONTROL_INTE_8, \ + 0x00 \ + } +#else +#define RADIO_CONFIGURATION_DATA_ARRAY { 0 } +#endif + +// DEFAULT VALUES FOR CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000 + +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { } + +#ifndef RADIO_CONFIGURATION_DATA_ARRAY +#error "This property must be defined!" +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT +#endif + +#define RADIO_CONFIGURATION_DATA { \ + Radio_Configuration_Data_Array, \ + RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \ + RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \ + RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \ + RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \ + } + +#endif /* RADIO_CONFIG_H_ */ diff --git a/RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h b/RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h new file mode 100755 index 0000000..e9aadfd --- /dev/null +++ b/RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h @@ -0,0 +1,516 @@ +/*! @file radio_config.h + * @brief This file contains the automatically generated + * configurations. + * + * @n WDS GUI Version: 3.2.11.0 + * @n Device: Si4464 Rev.: B1 + * + * @b COPYRIGHT + * @n Silicon Laboratories Confidential + * @n Copyright 2017 Silicon Laboratories, Inc. + * @n http://www.silabs.com + */ + +#ifndef RADIO_CONFIG_H_ +#define RADIO_CONFIG_H_ + +// USER DEFINED PARAMETERS +// Define your own parameters here + +// INPUT DATA +/* +// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0 +// MOD_type: 3 Rsymb(sps): 10000 Fdev(Hz): 20000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 3 +// RF Freq.(MHz): 915 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1 +// +// # RX IF frequency is -468750 Hz +// # WB filter 3 (BW = 123.48 kHz); NB-filter 3 (BW = 123.48 kHz) +// +// Modulation index: 4 +*/ + + +// CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000 + + +// CONFIGURATION COMMANDS + +/* +// Command: RF_POWER_UP +// Description: Command to power-up the device and select the operational mode and functionality. +*/ +#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80 + +/* +// Command: RF_GPIO_PIN_CFG +// Description: Configures the GPIO pins. +*/ +#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_GLOBAL_XO_TUNE_2 +// Number of properties: 2 +// Group ID: 0x00 +// Start ID: 0x00 +// Default values: 0x40, 0x00, +// Descriptions: +// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator. +// GLOBAL_CLK_CFG - Clock configuration options. +*/ +#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00 + +/* +// Set properties: RF_GLOBAL_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x00 +// Start ID: 0x03 +// Default values: 0x20, +// Descriptions: +// GLOBAL_CONFIG - Global configuration settings. +*/ +#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60 + +/* +// Set properties: RF_INT_CTL_ENABLE_2 +// Number of properties: 2 +// Group ID: 0x01 +// Start ID: 0x00 +// Default values: 0x04, 0x00, +// Descriptions: +// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin. +// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin. +*/ +#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x20 + +/* +// Set properties: RF_FRR_CTL_A_MODE_4 +// Number of properties: 4 +// Group ID: 0x02 +// Start ID: 0x00 +// Default values: 0x01, 0x02, 0x09, 0x00, +// Descriptions: +// FRR_CTL_A_MODE - Fast Response Register A Configuration. +// FRR_CTL_B_MODE - Fast Response Register B Configuration. +// FRR_CTL_C_MODE - Fast Response Register C Configuration. +// FRR_CTL_D_MODE - Fast Response Register D Configuration. +*/ +#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_PREAMBLE_TX_LENGTH_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x00 +// Default values: 0x08, +// Descriptions: +// PREAMBLE_TX_LENGTH - Configure length of TX Preamble. +*/ +#define RF_PREAMBLE_TX_LENGTH_1 0x11, 0x10, 0x01, 0x00, 0x0A + +/* +// Set properties: RF_PREAMBLE_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x04 +// Default values: 0x21, +// Descriptions: +// PREAMBLE_CONFIG - General configuration bits for the Preamble field. +*/ +#define RF_PREAMBLE_CONFIG_1 0x11, 0x10, 0x01, 0x04, 0x31 + +/* +// Set properties: RF_SYNC_CONFIG_3 +// Number of properties: 3 +// Group ID: 0x11 +// Start ID: 0x00 +// Default values: 0x01, 0x2D, 0xD4, +// Descriptions: +// SYNC_CONFIG - Sync Word configuration bits. +// SYNC_BITS_31_24 - Sync word. +// SYNC_BITS_23_16 - Sync word. +*/ +#define RF_SYNC_CONFIG_3 0x11, 0x11, 0x03, 0x00, 0x01, 0xB4, 0x2B + +/* +// Set properties: RF_PKT_CONFIG1_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x06 +// Default values: 0x00, +// Descriptions: +// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet. +*/ +#define RF_PKT_CONFIG1_1 0x11, 0x12, 0x01, 0x06, 0x02 + +/* +// Set properties: RF_PKT_FIELD_1_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x0F +// Default values: 0x00, +// Descriptions: +// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1. +*/ +#define RF_PKT_FIELD_1_CONFIG_1 0x11, 0x12, 0x01, 0x0F, 0x04 + +/* +// Set properties: RF_MODEM_MOD_TYPE_12 +// Number of properties: 12 +// Group ID: 0x20 +// Start ID: 0x00 +// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06, +// Descriptions: +// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation. +// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits. +// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer. +// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word. +// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x06, 0x1A, 0x80, 0x05, 0xC9, 0xC3, 0x80, 0x00, 0x02 + +/* +// Set properties: RF_MODEM_FREQ_DEV_0_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x0C +// Default values: 0xD3, +// Descriptions: +// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0xBB + +/* +// Set properties: RF_MODEM_TX_RAMP_DELAY_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x18 +// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, +// Descriptions: +// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting. +// MODEM_MDM_CTRL - MDM control. +// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation. +// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number). +// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter. +// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter. +*/ +#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x10 + +/* +// Set properties: RF_MODEM_BCR_OSR_1_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x22 +// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, +// Descriptions: +// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GEAR - RX BCR loop gear control. +// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop. +*/ +#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x01, 0xF4, 0x01, 0x06, 0x25, 0x00, 0x83, 0x02, 0xC2 + +/* +// Set properties: RF_MODEM_AFC_GEAR_7 +// Number of properties: 7 +// Group ID: 0x20 +// Start ID: 0x2C +// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0, +// Descriptions: +// MODEM_AFC_GEAR - RX AFC loop gear control. +// MODEM_AFC_WAIT - RX AFC loop wait time control. +// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_LIMITER_1 - Set the AFC limiter value. +// MODEM_AFC_LIMITER_0 - Set the AFC limiter value. +// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits. +*/ +#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x0F, 0x16, 0xFE, 0x80 + +/* +// Set properties: RF_MODEM_AGC_CONTROL_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x35 +// Default values: 0xE0, +// Descriptions: +// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain. +*/ +#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2 + +/* +// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x38 +// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, +// Descriptions: +// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm. +// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors. +// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors. +// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code. +// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector. +*/ +#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0x6D, 0x6D, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x2A + +/* +// Set properties: RF_MODEM_OOK_CNT1_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x42 +// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, +// Descriptions: +// MODEM_OOK_CNT1 - OOK control. +// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator. +// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors. +// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode. +// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold. +// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold. +// MODEM_ANT_DIV_MODE - Antenna diversity mode settings. +// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm. +*/ +#define RF_MODEM_OOK_CNT1_8 0x11, 0x20, 0x08, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x00, 0x82, 0x01, 0x80 + +/* +// Set properties: RF_MODEM_RSSI_COMP_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x4E +// Default values: 0x32, +// Descriptions: +// MODEM_RSSI_COMP - RSSI compensation value. +*/ +#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40 + +/* +// Set properties: RF_MODEM_CLKGEN_BAND_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x51 +// Default values: 0x08, +// Descriptions: +// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band. +*/ +#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x08 + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x00 +// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xCC, 0xA1, 0x30, 0xA0, 0x21, 0xD1, 0xB9, 0xC9, 0xEA, 0x05, 0x12, 0x11 + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x0C +// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0x0A, 0x04, 0x15, 0xFC, 0x03, 0x00, 0xCC, 0xA1, 0x30, 0xA0, 0x21, 0xD1 + +/* +// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x18 +// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, +// Descriptions: +// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xB9, 0xC9, 0xEA, 0x05, 0x12, 0x11, 0x0A, 0x04, 0x15, 0xFC, 0x03, 0x00 + +/* +// Set properties: RF_PA_MODE_4 +// Number of properties: 4 +// Group ID: 0x22 +// Start ID: 0x00 +// Default values: 0x08, 0x7F, 0x00, 0x5D, +// Descriptions: +// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size). +// PA_PWR_LVL - Configuration of PA output power level. +// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source. +// PA_TC - Configuration of PA ramping parameters. +*/ +#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x3D + +/* +// Set properties: RF_SYNTH_PFDCP_CPFF_7 +// Number of properties: 7 +// Group ID: 0x23 +// Start ID: 0x00 +// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03, +// Descriptions: +// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection. +// SYNTH_PFDCP_CPINT - Integration charge pump current selection. +// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path. +// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter. +// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter. +// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter. +// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter. +*/ +#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03 + +/* +// Set properties: RF_FREQ_CONTROL_INTE_8 +// Number of properties: 8 +// Group ID: 0x40 +// Start ID: 0x00 +// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, +// Descriptions: +// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number. +// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration. +// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode. +*/ +#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x3C, 0x08, 0x00, 0x00, 0x22, 0x22, 0x20, 0xFF + + +// AUTOMATICALLY GENERATED CODE! +// DO NOT EDIT/MODIFY BELOW THIS LINE! +// -------------------------------------------- + +#ifndef FIRMWARE_LOAD_COMPILE +#define RADIO_CONFIGURATION_DATA_ARRAY { \ + 0x07, RF_POWER_UP, \ + 0x08, RF_GPIO_PIN_CFG, \ + 0x06, RF_GLOBAL_XO_TUNE_2, \ + 0x05, RF_GLOBAL_CONFIG_1, \ + 0x06, RF_INT_CTL_ENABLE_2, \ + 0x08, RF_FRR_CTL_A_MODE_4, \ + 0x05, RF_PREAMBLE_TX_LENGTH_1, \ + 0x05, RF_PREAMBLE_CONFIG_1, \ + 0x07, RF_SYNC_CONFIG_3, \ + 0x05, RF_PKT_CONFIG1_1, \ + 0x05, RF_PKT_FIELD_1_CONFIG_1, \ + 0x10, RF_MODEM_MOD_TYPE_12, \ + 0x05, RF_MODEM_FREQ_DEV_0_1, \ + 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \ + 0x0D, RF_MODEM_BCR_OSR_1_9, \ + 0x0B, RF_MODEM_AFC_GEAR_7, \ + 0x05, RF_MODEM_AGC_CONTROL_1, \ + 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \ + 0x0C, RF_MODEM_OOK_CNT1_8, \ + 0x05, RF_MODEM_RSSI_COMP_1, \ + 0x05, RF_MODEM_CLKGEN_BAND_1, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \ + 0x08, RF_PA_MODE_4, \ + 0x0B, RF_SYNTH_PFDCP_CPFF_7, \ + 0x0C, RF_FREQ_CONTROL_INTE_8, \ + 0x00 \ + } +#else +#define RADIO_CONFIGURATION_DATA_ARRAY { 0 } +#endif + +// DEFAULT VALUES FOR CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000 + +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { } + +#ifndef RADIO_CONFIGURATION_DATA_ARRAY +#error "This property must be defined!" +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT +#endif + +#define RADIO_CONFIGURATION_DATA { \ + Radio_Configuration_Data_Array, \ + RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \ + RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \ + RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \ + RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \ + } + +#endif /* RADIO_CONFIG_H_ */ diff --git a/RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h b/RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h new file mode 100755 index 0000000..ffe1de8 --- /dev/null +++ b/RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h @@ -0,0 +1,516 @@ +/*! @file radio_config.h + * @brief This file contains the automatically generated + * configurations. + * + * @n WDS GUI Version: 3.2.11.0 + * @n Device: Si4464 Rev.: B1 + * + * @b COPYRIGHT + * @n Silicon Laboratories Confidential + * @n Copyright 2017 Silicon Laboratories, Inc. + * @n http://www.silabs.com + */ + +#ifndef RADIO_CONFIG_H_ +#define RADIO_CONFIG_H_ + +// USER DEFINED PARAMETERS +// Define your own parameters here + +// INPUT DATA +/* +// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0 +// MOD_type: 3 Rsymb(sps): 5000 Fdev(Hz): 10000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 3 +// RF Freq.(MHz): 915 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1 +// +// # RX IF frequency is -468750 Hz +// # WB filter 2 (BW = 103.06 kHz); NB-filter 2 (BW = 103.06 kHz) +// +// Modulation index: 4 +*/ + + +// CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000 + + +// CONFIGURATION COMMANDS + +/* +// Command: RF_POWER_UP +// Description: Command to power-up the device and select the operational mode and functionality. +*/ +#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80 + +/* +// Command: RF_GPIO_PIN_CFG +// Description: Configures the GPIO pins. +*/ +#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_GLOBAL_XO_TUNE_2 +// Number of properties: 2 +// Group ID: 0x00 +// Start ID: 0x00 +// Default values: 0x40, 0x00, +// Descriptions: +// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator. +// GLOBAL_CLK_CFG - Clock configuration options. +*/ +#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00 + +/* +// Set properties: RF_GLOBAL_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x00 +// Start ID: 0x03 +// Default values: 0x20, +// Descriptions: +// GLOBAL_CONFIG - Global configuration settings. +*/ +#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x60 + +/* +// Set properties: RF_INT_CTL_ENABLE_2 +// Number of properties: 2 +// Group ID: 0x01 +// Start ID: 0x00 +// Default values: 0x04, 0x00, +// Descriptions: +// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin. +// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin. +*/ +#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0x20 + +/* +// Set properties: RF_FRR_CTL_A_MODE_4 +// Number of properties: 4 +// Group ID: 0x02 +// Start ID: 0x00 +// Default values: 0x01, 0x02, 0x09, 0x00, +// Descriptions: +// FRR_CTL_A_MODE - Fast Response Register A Configuration. +// FRR_CTL_B_MODE - Fast Response Register B Configuration. +// FRR_CTL_C_MODE - Fast Response Register C Configuration. +// FRR_CTL_D_MODE - Fast Response Register D Configuration. +*/ +#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + +/* +// Set properties: RF_PREAMBLE_TX_LENGTH_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x00 +// Default values: 0x08, +// Descriptions: +// PREAMBLE_TX_LENGTH - Configure length of TX Preamble. +*/ +#define RF_PREAMBLE_TX_LENGTH_1 0x11, 0x10, 0x01, 0x00, 0x0A + +/* +// Set properties: RF_PREAMBLE_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x10 +// Start ID: 0x04 +// Default values: 0x21, +// Descriptions: +// PREAMBLE_CONFIG - General configuration bits for the Preamble field. +*/ +#define RF_PREAMBLE_CONFIG_1 0x11, 0x10, 0x01, 0x04, 0x31 + +/* +// Set properties: RF_SYNC_CONFIG_3 +// Number of properties: 3 +// Group ID: 0x11 +// Start ID: 0x00 +// Default values: 0x01, 0x2D, 0xD4, +// Descriptions: +// SYNC_CONFIG - Sync Word configuration bits. +// SYNC_BITS_31_24 - Sync word. +// SYNC_BITS_23_16 - Sync word. +*/ +#define RF_SYNC_CONFIG_3 0x11, 0x11, 0x03, 0x00, 0x01, 0xB4, 0x2B + +/* +// Set properties: RF_PKT_CONFIG1_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x06 +// Default values: 0x00, +// Descriptions: +// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet. +*/ +#define RF_PKT_CONFIG1_1 0x11, 0x12, 0x01, 0x06, 0x02 + +/* +// Set properties: RF_PKT_FIELD_1_CONFIG_1 +// Number of properties: 1 +// Group ID: 0x12 +// Start ID: 0x0F +// Default values: 0x00, +// Descriptions: +// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1. +*/ +#define RF_PKT_FIELD_1_CONFIG_1 0x11, 0x12, 0x01, 0x0F, 0x04 + +/* +// Set properties: RF_MODEM_MOD_TYPE_12 +// Number of properties: 12 +// Group ID: 0x20 +// Start ID: 0x00 +// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06, +// Descriptions: +// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation. +// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits. +// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer. +// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate +// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus. +// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word. +// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x03, 0x00, 0x07, 0x03, 0x0D, 0x40, 0x05, 0xC9, 0xC3, 0x80, 0x00, 0x01 + +/* +// Set properties: RF_MODEM_FREQ_DEV_0_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x0C +// Default values: 0xD3, +// Descriptions: +// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word. +*/ +#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0x5E + +/* +// Set properties: RF_MODEM_TX_RAMP_DELAY_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x18 +// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, +// Descriptions: +// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting. +// MODEM_MDM_CTRL - MDM control. +// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation. +// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number). +// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number). +// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter. +// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter. +*/ +#define RF_MODEM_TX_RAMP_DELAY_8 0x11, 0x20, 0x08, 0x18, 0x01, 0x80, 0x08, 0x03, 0xC0, 0x00, 0x20, 0x20 + +/* +// Set properties: RF_MODEM_BCR_OSR_1_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x22 +// Default values: 0x00, 0x4B, 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, +// Descriptions: +// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number). +// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number). +// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value. +// MODEM_BCR_GEAR - RX BCR loop gear control. +// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop. +*/ +#define RF_MODEM_BCR_OSR_1_9 0x11, 0x20, 0x09, 0x22, 0x02, 0xEE, 0x00, 0xAE, 0xC3, 0x00, 0x57, 0x02, 0xC2 + +/* +// Set properties: RF_MODEM_AFC_GEAR_7 +// Number of properties: 7 +// Group ID: 0x20 +// Start ID: 0x2C +// Default values: 0x00, 0x23, 0x83, 0x69, 0x00, 0x40, 0xA0, +// Descriptions: +// MODEM_AFC_GEAR - RX AFC loop gear control. +// MODEM_AFC_WAIT - RX AFC loop wait time control. +// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality. +// MODEM_AFC_LIMITER_1 - Set the AFC limiter value. +// MODEM_AFC_LIMITER_0 - Set the AFC limiter value. +// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits. +*/ +#define RF_MODEM_AFC_GEAR_7 0x11, 0x20, 0x07, 0x2C, 0x04, 0x36, 0x80, 0x07, 0x29, 0x3F, 0x80 + +/* +// Set properties: RF_MODEM_AGC_CONTROL_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x35 +// Default values: 0xE0, +// Descriptions: +// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain. +*/ +#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE2 + +/* +// Set properties: RF_MODEM_AGC_WINDOW_SIZE_9 +// Number of properties: 9 +// Group ID: 0x20 +// Start ID: 0x38 +// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, +// Descriptions: +// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm. +// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors. +// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors. +// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression. +// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold. +// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code. +// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector. +*/ +#define RF_MODEM_AGC_WINDOW_SIZE_9 0x11, 0x20, 0x09, 0x38, 0x11, 0xA4, 0xA4, 0x00, 0x1A, 0xFF, 0xFF, 0x00, 0x2B + +/* +// Set properties: RF_MODEM_OOK_CNT1_8 +// Number of properties: 8 +// Group ID: 0x20 +// Start ID: 0x42 +// Default values: 0xA4, 0x03, 0x56, 0x02, 0x00, 0xA3, 0x02, 0x80, +// Descriptions: +// MODEM_OOK_CNT1 - OOK control. +// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator. +// MODEM_RAW_SEARCH - Defines and controls the search period length for the Moving Average and Min-Max detectors. +// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode. +// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold. +// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold. +// MODEM_ANT_DIV_MODE - Antenna diversity mode settings. +// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm. +*/ +#define RF_MODEM_OOK_CNT1_8 0x11, 0x20, 0x08, 0x42, 0xA4, 0x02, 0xD6, 0x83, 0x00, 0x57, 0x01, 0x80 + +/* +// Set properties: RF_MODEM_RSSI_COMP_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x4E +// Default values: 0x32, +// Descriptions: +// MODEM_RSSI_COMP - RSSI compensation value. +*/ +#define RF_MODEM_RSSI_COMP_1 0x11, 0x20, 0x01, 0x4E, 0x40 + +/* +// Set properties: RF_MODEM_CLKGEN_BAND_1 +// Number of properties: 1 +// Group ID: 0x20 +// Start ID: 0x51 +// Default values: 0x08, +// Descriptions: +// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band. +*/ +#define RF_MODEM_CLKGEN_BAND_1 0x11, 0x20, 0x01, 0x51, 0x08 + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x00 +// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C + +/* +// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x0C +// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5, +// Descriptions: +// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5 + +/* +// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 +// Number of properties: 12 +// Group ID: 0x21 +// Start ID: 0x18 +// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00, +// Descriptions: +// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients. +// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients. +*/ +#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00 + +/* +// Set properties: RF_PA_MODE_4 +// Number of properties: 4 +// Group ID: 0x22 +// Start ID: 0x00 +// Default values: 0x08, 0x7F, 0x00, 0x5D, +// Descriptions: +// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size). +// PA_PWR_LVL - Configuration of PA output power level. +// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source. +// PA_TC - Configuration of PA ramping parameters. +*/ +#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x3D + +/* +// Set properties: RF_SYNTH_PFDCP_CPFF_7 +// Number of properties: 7 +// Group ID: 0x23 +// Start ID: 0x00 +// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03, +// Descriptions: +// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection. +// SYNTH_PFDCP_CPINT - Integration charge pump current selection. +// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path. +// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter. +// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter. +// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter. +// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter. +*/ +#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03 + +/* +// Set properties: RF_FREQ_CONTROL_INTE_8 +// Number of properties: 8 +// Group ID: 0x40 +// Start ID: 0x00 +// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, +// Descriptions: +// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number. +// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number. +// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size. +// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration. +// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode. +*/ +#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x3C, 0x08, 0x00, 0x00, 0x22, 0x22, 0x20, 0xFF + + +// AUTOMATICALLY GENERATED CODE! +// DO NOT EDIT/MODIFY BELOW THIS LINE! +// -------------------------------------------- + +#ifndef FIRMWARE_LOAD_COMPILE +#define RADIO_CONFIGURATION_DATA_ARRAY { \ + 0x07, RF_POWER_UP, \ + 0x08, RF_GPIO_PIN_CFG, \ + 0x06, RF_GLOBAL_XO_TUNE_2, \ + 0x05, RF_GLOBAL_CONFIG_1, \ + 0x06, RF_INT_CTL_ENABLE_2, \ + 0x08, RF_FRR_CTL_A_MODE_4, \ + 0x05, RF_PREAMBLE_TX_LENGTH_1, \ + 0x05, RF_PREAMBLE_CONFIG_1, \ + 0x07, RF_SYNC_CONFIG_3, \ + 0x05, RF_PKT_CONFIG1_1, \ + 0x05, RF_PKT_FIELD_1_CONFIG_1, \ + 0x10, RF_MODEM_MOD_TYPE_12, \ + 0x05, RF_MODEM_FREQ_DEV_0_1, \ + 0x0C, RF_MODEM_TX_RAMP_DELAY_8, \ + 0x0D, RF_MODEM_BCR_OSR_1_9, \ + 0x0B, RF_MODEM_AFC_GEAR_7, \ + 0x05, RF_MODEM_AGC_CONTROL_1, \ + 0x0D, RF_MODEM_AGC_WINDOW_SIZE_9, \ + 0x0C, RF_MODEM_OOK_CNT1_8, \ + 0x05, RF_MODEM_RSSI_COMP_1, \ + 0x05, RF_MODEM_CLKGEN_BAND_1, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \ + 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \ + 0x08, RF_PA_MODE_4, \ + 0x0B, RF_SYNTH_PFDCP_CPFF_7, \ + 0x0C, RF_FREQ_CONTROL_INTE_8, \ + 0x00 \ + } +#else +#define RADIO_CONFIGURATION_DATA_ARRAY { 0 } +#endif + +// DEFAULT VALUES FOR CONFIGURATION PARAMETERS +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10 +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01 +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000 + +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00 +#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { } + +#ifndef RADIO_CONFIGURATION_DATA_ARRAY +#error "This property must be defined!" +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ +#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER +#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH +#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP +#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT +#endif + +#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET +#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT +#endif + +#define RADIO_CONFIGURATION_DATA { \ + Radio_Configuration_Data_Array, \ + RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \ + RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \ + RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \ + RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \ + } + +#endif /* RADIO_CONFIG_H_ */ diff --git a/RHCRC.cpp b/RHCRC.cpp new file mode 100644 index 0000000..b636bab --- /dev/null +++ b/RHCRC.cpp @@ -0,0 +1,104 @@ +/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz + Copyright (c) 2005, 2007 Joerg Wunsch + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +// Port to Energia / MPS430 by Yannick DEVOS XV4Y - (c) 2013 +// http://xv4y.radioclub.asia/ +// + +// Adapted to RadioHead use by Mike McCauley 2014 +// This is to prevent name collisions with other similar library functions +// and to provide a consistent API amonng all processors +// + +/* $Id: RHCRC.cpp,v 1.1 2014/06/24 02:40:12 mikem Exp $ */ + +#include + +#define lo8(x) ((x)&0xff) +#define hi8(x) ((x)>>8) + +uint16_t RHcrc16_update(uint16_t crc, uint8_t a) +{ + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} + +uint16_t RHcrc_xmodem_update (uint16_t crc, uint8_t data) +{ + int i; + + crc = crc ^ ((uint16_t)data << 8); + for (i=0; i<8; i++) + { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + } + + return crc; +} + +uint16_t RHcrc_ccitt_update (uint16_t crc, uint8_t data) +{ + data ^= lo8 (crc); + data ^= data << 4; + + return ((((uint16_t)data << 8) | hi8 (crc)) ^ (uint8_t)(data >> 4) + ^ ((uint16_t)data << 3)); +} + +uint8_t RHcrc_ibutton_update(uint8_t crc, uint8_t data) +{ + uint8_t i; + + crc = crc ^ data; + for (i = 0; i < 8; i++) + { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + + return crc; +} + + diff --git a/RHCRC.h b/RHCRC.h new file mode 100644 index 0000000..42aeb4d --- /dev/null +++ b/RHCRC.h @@ -0,0 +1,19 @@ +// RHCRC.h +// +// Definitions for RadioHead compatible CRC outines. +// +// These routines originally derived from Arduino source code. See RHCRC.cpp +// for copyright information +// $Id: RHCRC.h,v 1.1 2014/06/24 02:40:12 mikem Exp $ + +#ifndef RHCRC_h +#define RHCRC_h + +#include + +extern uint16_t RHcrc16_update(uint16_t crc, uint8_t a); +extern uint16_t RHcrc_xmodem_update (uint16_t crc, uint8_t data); +extern uint16_t RHcrc_ccitt_update (uint16_t crc, uint8_t data); +extern uint8_t RHcrc_ibutton_update(uint8_t crc, uint8_t data); + +#endif diff --git a/RHDatagram.cpp b/RHDatagram.cpp new file mode 100644 index 0000000..325e12d --- /dev/null +++ b/RHDatagram.cpp @@ -0,0 +1,123 @@ +// RHDatagram.cpp +// +// Copyright (C) 2011 Mike McCauley +// $Id: RHDatagram.cpp,v 1.6 2014/05/23 02:20:17 mikem Exp $ + +#include + +RHDatagram::RHDatagram(RHGenericDriver& driver, uint8_t thisAddress) + : + _driver(driver), + _thisAddress(thisAddress) +{ +} + +//////////////////////////////////////////////////////////////////// +// Public methods +bool RHDatagram::init() +{ + bool ret = _driver.init(); + if (ret) + setThisAddress(_thisAddress); + return ret; +} + +void RHDatagram::setThisAddress(uint8_t thisAddress) +{ + _driver.setThisAddress(thisAddress); + // Use this address in the transmitted FROM header + setHeaderFrom(thisAddress); + _thisAddress = thisAddress; +} + +bool RHDatagram::sendto(uint8_t* buf, uint8_t len, uint8_t address) +{ + setHeaderTo(address); + return _driver.send(buf, len); +} + +bool RHDatagram::recvfrom(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) +{ + if (_driver.recv(buf, len)) + { + if (from) *from = headerFrom(); + if (to) *to = headerTo(); + if (id) *id = headerId(); + if (flags) *flags = headerFlags(); + return true; + } + return false; +} + +bool RHDatagram::available() +{ + return _driver.available(); +} + +void RHDatagram::waitAvailable() +{ + _driver.waitAvailable(); +} + +bool RHDatagram::waitPacketSent() +{ + return _driver.waitPacketSent(); +} + +bool RHDatagram::waitPacketSent(uint16_t timeout) +{ + return _driver.waitPacketSent(timeout); +} + +bool RHDatagram::waitAvailableTimeout(uint16_t timeout) +{ + return _driver.waitAvailableTimeout(timeout); +} + +uint8_t RHDatagram::thisAddress() +{ + return _thisAddress; +} + +void RHDatagram::setHeaderTo(uint8_t to) +{ + _driver.setHeaderTo(to); +} + +void RHDatagram::setHeaderFrom(uint8_t from) +{ + _driver.setHeaderFrom(from); +} + +void RHDatagram::setHeaderId(uint8_t id) +{ + _driver.setHeaderId(id); +} + +void RHDatagram::setHeaderFlags(uint8_t set, uint8_t clear) +{ + _driver.setHeaderFlags(set, clear); +} + +uint8_t RHDatagram::headerTo() +{ + return _driver.headerTo(); +} + +uint8_t RHDatagram::headerFrom() +{ + return _driver.headerFrom(); +} + +uint8_t RHDatagram::headerId() +{ + return _driver.headerId(); +} + +uint8_t RHDatagram::headerFlags() +{ + return _driver.headerFlags(); +} + + + diff --git a/RHDatagram.h b/RHDatagram.h new file mode 100644 index 0000000..fbc844f --- /dev/null +++ b/RHDatagram.h @@ -0,0 +1,162 @@ +// RHDatagram.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHDatagram.h,v 1.14 2015/08/12 23:18:51 mikem Exp $ + +#ifndef RHDatagram_h +#define RHDatagram_h + +#include + +// This is the maximum possible message size for radios supported by RadioHead. +// Not all radios support this length, and many are much smaller +#define RH_MAX_MESSAGE_LEN 255 + +///////////////////////////////////////////////////////////////////// +/// \class RHDatagram RHDatagram.h +/// \brief Manager class for addressed, unreliable messages +/// +/// Every RHDatagram node has an 8 bit address (defaults to 0). +/// Addresses (DEST and SRC) are 8 bit integers with an address of RH_BROADCAST_ADDRESS (0xff) +/// reserved for broadcast. +/// +/// \par Media Access Strategy +/// +/// RHDatagram and the underlying drivers always transmit as soon as sendto() is called. +/// +/// \par Message Lengths +/// +/// Not all Radio drivers supported by RadioHead can handle the same message lengths. Some radios can handle +/// up to 255 octets, and some as few as 28. If you attempt to send a message that is too long for +/// the underlying driver, sendTo() will return false and will not transmit the message. +/// It is the programmers responsibility to make +/// sure that messages passed to sendto() do not exceed the capability of the radio. You can use the +/// *_MAX_MESSAGE_LENGTH definitions or driver->maxMessageLength() to help. +/// +/// \par Headers +/// +/// Each message sent and received by a RadioHead driver includes 4 headers:
+/// \b TO The node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted)
+/// \b FROM The node address of the sending node
+/// \b ID A message ID, distinct (over short time scales) for each message sent by a particilar node
+/// \b FLAGS A bitmask of flags. The most significant 4 bits are reserved for use by RadioHead. The least +/// significant 4 bits are reserved for applications.
+/// +class RHDatagram +{ +public: + /// Constructor. + /// \param[in] driver The RadioHead driver to use to transport messages. + /// \param[in] thisAddress The address to assign to this node. Defaults to 0 + RHDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0); + + /// Initialise this instance and the + /// driver connected to it. + bool init(); + + /// Sets the address of this node. Defaults to 0. + /// This will be used to set the FROM address of all messages sent by this node. + /// In a conventional multinode system, all nodes will have a unique address + /// (which you could store in EEPROM). + /// \param[in] thisAddress The address of this node + void setThisAddress(uint8_t thisAddress); + + /// Sends a message to the node(s) with the given address + /// RH_BROADCAST_ADDRESS is a valid address which will cause the message + /// to be accepted by all RHDatagram nodes within range. + /// \param[in] buf Pointer to the binary message to send + /// \param[in] len Number of octets to send (> 0) + /// \param[in] address The address to send the message to. + /// \return true if the message not too loing fot eh driver, and the message was transmitted. + bool sendto(uint8_t* buf, uint8_t len, uint8_t address); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available for this node, copy it to buf and return true + /// The SRC address is placed in *from if present and not NULL. + /// The DEST address is placed in *to if present and not NULL. + /// If a message is copied, *len is set to the length. + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the FROM address + /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the TO address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// (not just those addressed to this node). + /// \return true if a valid message was copied to buf + bool recvfrom(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received bythe transport, when it will be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop. + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + bool available(); + + /// Starts the Driver receiver and blocks until a valid received + /// message is available. + void waitAvailable(); + + /// Blocks until the transmitter + /// is no longer transmitting. + bool waitPacketSent(); + + /// Blocks until the transmitter is no longer transmitting. + /// or until the timeout occuers, whichever happens first + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if the radio completed transmission within the timeout period. False if it timed out. + bool waitPacketSent(uint16_t timeout); + + /// Starts the Driver receiver and blocks until a received message is available or a timeout + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if a message is available + bool waitAvailableTimeout(uint16_t timeout); + + /// Sets the TO header to be sent in all subsequent messages + /// \param[in] to The new TO header value + void setHeaderTo(uint8_t to); + + /// Sets the FROM header to be sent in all subsequent messages + /// \param[in] from The new FROM header value + void setHeaderFrom(uint8_t from); + + /// Sets the ID header to be sent in all subsequent messages + /// \param[in] id The new ID header value + void setHeaderId(uint8_t id); + + /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages + /// \param[in] set bitmask of bits to be set + /// \param[in] clear bitmask of flags to clear + void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_NONE); + + /// Returns the TO header of the last received message + /// \return The TO header of the most recently received message. + uint8_t headerTo(); + + /// Returns the FROM header of the last received message + /// \return The FROM header of the most recently received message. + uint8_t headerFrom(); + + /// Returns the ID header of the last received message + /// \return The ID header of the most recently received message. + uint8_t headerId(); + + /// Returns the FLAGS header of the last received message + /// \return The FLAGS header of the most recently received message. + uint8_t headerFlags(); + + /// Returns the address of this node. + /// \return The address of this node + uint8_t thisAddress(); + +protected: + /// The Driver we are to use + RHGenericDriver& _driver; + + /// The address of this node + uint8_t _thisAddress; +}; + +#endif diff --git a/RHEncryptedDriver.cpp b/RHEncryptedDriver.cpp new file mode 100644 index 0000000..87be402 --- /dev/null +++ b/RHEncryptedDriver.cpp @@ -0,0 +1,156 @@ +// RHEncryptedDriver.cpp +// +// Author: Philippe.Rochat'at'gmail.com +// Contributed to the RadioHead project by the author +// $Id: RHEncryptedDriver.cpp,v 1.6 2020/07/05 08:52:21 mikem Exp mikem $ + +#include +#ifdef RH_ENABLE_ENCRYPTION_MODULE +#include + +RHEncryptedDriver::RHEncryptedDriver(RHGenericDriver& driver, BlockCipher& blockcipher) + : _driver(driver), + _blockcipher(blockcipher) +{ + _buffer = (uint8_t *)calloc(_driver.maxMessageLength(), sizeof(uint8_t)); +} + +bool RHEncryptedDriver::recv(uint8_t* buf, uint8_t* len) +{ + int h = 0; // Index of output _buffer + + bool status = _driver.recv(_buffer, len); + if (status && buf && len) + { + int blockSize = _blockcipher.blockSize(); // Size of blocks used by encryption + int nbBlocks = *len / blockSize; // Number of blocks in that message + if (nbBlocks * blockSize == *len) + { + // Or we have a missmatch ... this is probably not symetrically encrypted + for (int k = 0; k < nbBlocks; k++) + { + // Decrypt each block + _blockcipher.decryptBlock(&buf[h], &_buffer[k*blockSize]); // Decrypt that block into buf + h += blockSize; +#ifdef STRICT_CONTENT_LEN + if (k == 0) + { +// if (buf[0] > *len - 1) +// return false; // Bogus payload length + *len = buf[0]; // First byte contains length + h--; // First block is of length-- + memmove(buf, buf+1, blockSize - 1); + } +#endif + } + } + } + + return status; +} + +bool RHEncryptedDriver::send(const uint8_t* data, uint8_t len) +{ + if (len > maxMessageLength()) + return false; + + bool status = true; + int blockSize = _blockcipher.blockSize(); // Size of blocks used by encryption + + if (len == 0) // PassThru + return _driver.send(data, len); + + if (_cipheringBlocks.blockSize != blockSize) + { + // Cipher has changed it's block size + _cipheringBlocks.inputBlock = (uint8_t *)realloc(_cipheringBlocks.inputBlock, blockSize); + _cipheringBlocks.blockSize = blockSize; + } + + int max_message_length = maxMessageLength(); +#ifdef STRICT_CONTENT_LEN + uint8_t nbBlocks = len / blockSize + 1; // How many blocks do we need for that message + uint8_t nbBpM = (max_message_length + 1) / blockSize; // Max number of blocks per message +#else + uint8_t nbBlocks = (len - 1) / blockSize + 1; // How many blocks do we need for that message + uint8_t nbBpM = max_message_length / blockSize; // Max number of blocks per message +#endif + int k = 0, j = 0; // k is block index, j is original message index +#ifndef ALLOW_MULTIPLE_MSG +#ifdef STRICT_CONTENT_LEN + for (k = 0; k < nbBpM && k * blockSize < len + 1; k++) +#else + for (k = 0; k < nbBpM && k * blockSize < len; k++) +#endif + { + // k blocks in that message + int h = 0; // h is block content index +#ifdef STRICT_CONTENT_LEN + if (k == 0) + _cipheringBlocks.inputBlock[h++] = len; // put in first byte of first block the message length +#endif + while (h < blockSize) + { + // Copy each msg byte into inputBlock, and trail with 0 if necessary + if (j < len) + _cipheringBlocks.inputBlock[h++] = data[j++]; + else + _cipheringBlocks.inputBlock[h++] = 0; // Completing with trailing 0 + } + _blockcipher.encryptBlock(&_buffer[k * blockSize], _cipheringBlocks.inputBlock); // Cipher that message into _buffer + } +// Serial.println(max_message_length); +// Serial.println(nbBlocks); +// Serial.println(nbBpM); +// Serial.println(k); +// Serial.println(blockSize); +// printBuffer("single send", _buffer, k * blockSize); + if (!_driver.send(_buffer, k*blockSize)) // We now send that message with it's new length + status = false; +#else + uint8_t nbMsg = (nbBlocks * blockSize) / max_message_length + 1; // How many message do we need + + for (int i = 0; i < nbMsg; i++) + { + // i is message index + for (k = 0; k < nbBpM && k * blockSize < len ; k++) + { + // k blocks in that message + int h = 0; +#ifdef STRICT_CONTENT_LEN + if (k == 0 && i == 0) + _cipheringBlocks.inputBlock[h++] = len; // put in first byte of first block of first message the message length +#endif + while (h < blockSize) + { + // Copy each msg byte into inputBlock, and trail with 0 if necessary + if (j < len) + _cipheringBlocks.inputBlock[h++] = data[j++]; + else + _cipheringBlocks.inputBlock[h++] = 0; + } + _blockcipher.encryptBlock(&_buffer[k * blockSize], _cipheringBlocks.inputBlock); // Cipher that message into buffer + } +// printBuffer("multiple send", _buffer, k * blockSize); + if (!_driver.send(_buffer, k * blockSize)) // We now send that message with it's new length + status = false; + } +#endif + return status; +} + +uint8_t RHEncryptedDriver::maxMessageLength() +{ + int driver_len = _driver.maxMessageLength(); + +#ifndef ALLOW_MULTIPLE_MSG + driver_len = ((int)(driver_len/_blockcipher.blockSize()) ) * _blockcipher.blockSize(); +#endif + +#ifdef STRICT_CONTENT_LEN + driver_len--; +#endif + return driver_len; +} + +#endif diff --git a/RHEncryptedDriver.h b/RHEncryptedDriver.h new file mode 100644 index 0000000..72bb40e --- /dev/null +++ b/RHEncryptedDriver.h @@ -0,0 +1,251 @@ +// RHEncryptedDriver.h + +// Generic encryption layer that could use any driver +// But will encrypt all data. +// Requires the Arduinolibs/Crypto library: +// https://github.com/rweather/arduinolibs +// +// Author: Philippe.Rochat'at'gmail.com +// Contributed to the RadioHead project by the author +// $Id: RHEncryptedDriver.h,v 1.4 2020/07/05 08:52:21 mikem Exp mikem $ + +#ifndef RHEncryptedDriver_h +#define RHEncryptedDriver_h + +#include +#ifdef RH_ENABLE_ENCRYPTION_MODULE +#include + +// Undef this if trailing 0 on each enrypted message is ok. +// This defined means a first byte of the payload is used to encode content length +// And the received message content is trimmed to this length +#define STRICT_CONTENT_LEN + +// Define this to allow encrypted content to span over 2 messages +// STRICT_CONTENT_LEN and ALLOW_MULTIPLE_MSG aren't compatible !!! +// With STRICT_CONTENT_LEN, receiver will try to extract length from every message !!!! +//#define ALLOW_MULTIPLE_MSG + +///////////////////////////////////////////////////////////////////// +/// \class RHEncryptedDriver RHEncryptedDriver +/// \brief Virtual Driver to encrypt/decrypt data. Can be used with any other RadioHead driver. +/// +/// This driver acts as a wrapper for any other RadioHead driver, adding encryption and decryption of +/// messages that are passed to and from the actual radio driver. Only the message payload is encrypted, +/// and not the to/from address or flags. Any of the encryption ciphers supported by +/// ArduinoLibs Cryptographic Library http://rweather.github.io/arduinolibs/crypto.html may be used. +/// +/// For successful communications, both sender and receiver must use the same cipher and the same key. +/// +/// In order to enable this module you must uncomment #define RH_ENABLE_ENCRYPTION_MODULE at the bottom of RadioHead.h +/// But ensure you have installed the Crypto directory from arduinolibs first: +/// http://rweather.github.io/arduinolibs/index.html + +class RHEncryptedDriver : public RHGenericDriver +{ +public: + /// Constructor. + /// Adds a ciphering layer to messages sent and received by the actual transport driver. + /// \param[in] driver The RadioHead driver to use to transport messages. + /// \param[in] blockcipher The blockcipher (from arduinolibs) that crypt/decrypt data. Ensure that + /// the blockcipher has had its key set before sending or receiving messages. + RHEncryptedDriver(RHGenericDriver& driver, BlockCipher& blockcipher); + + /// Calls the real driver's init() + /// \return The value returned from the driver init() method; + virtual bool init() { return _driver.init();}; + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it wil be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available() { return _driver.available();}; + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then optionally waits for Channel Activity Detection (CAD) + /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send + /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting. + /// \return true if the message length was valid and it was correctly queued for transmit. Return false + /// if CAD was requested and the CAD timeout timed out before clear channel was detected. + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver, which depends on the maximum length supported by the underlying transport driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// Blocks until the transmitter + /// is no longer transmitting. + virtual bool waitPacketSent() { return _driver.waitPacketSent();} ; + + /// Blocks until the transmitter is no longer transmitting. + /// or until the timeout occuers, whichever happens first + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if the radio completed transmission within the timeout period. False if it timed out. + virtual bool waitPacketSent(uint16_t timeout) {return _driver.waitPacketSent(timeout);} ; + + /// Starts the receiver and blocks until a received message is available or a timeout + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if a message is available + virtual bool waitAvailableTimeout(uint16_t timeout) {return _driver.waitAvailableTimeout(timeout);}; + + /// Calls the waitCAD method in the driver + /// \return The return value from teh drivers waitCAD() method + virtual bool waitCAD() { return _driver.waitCAD();}; + + /// Sets the Channel Activity Detection timeout in milliseconds to be used by waitCAD(). + /// The default is 0, which means do not wait for CAD detection. + /// CAD detection depends on support for isChannelActive() by your particular radio. + void setCADTimeout(unsigned long cad_timeout) {_driver.setCADTimeout(cad_timeout);}; + + /// Determine if the currently selected radio channel is active. + /// This is expected to be subclassed by specific radios to implement their Channel Activity Detection + /// if supported. If the radio does not support CAD, returns true immediately. If a RadioHead radio + /// supports isChannelActive() it will be documented in the radio specific documentation. + /// This is called automatically by waitCAD(). + /// \return true if the radio-specific CAD (as returned by override of isChannelActive()) shows the + /// current radio channel as active, else false. If there is no radio-specific CAD, returns false. + virtual bool isChannelActive() { return _driver.isChannelActive();}; + + /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. + /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, + /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. + /// In promiscuous mode, all messages will be accepted regardless of the TO header. + /// In a conventional multinode system, all nodes will have a unique address + /// (which you could store in EEPROM). + /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, + /// allowing the possibilty of address spoofing). + /// \param[in] thisAddress The address of this node. + virtual void setThisAddress(uint8_t thisAddress) { _driver.setThisAddress(thisAddress);}; + + /// Sets the TO header to be sent in all subsequent messages + /// \param[in] to The new TO header value + virtual void setHeaderTo(uint8_t to){ _driver.setHeaderTo(to);}; + + /// Sets the FROM header to be sent in all subsequent messages + /// \param[in] from The new FROM header value + virtual void setHeaderFrom(uint8_t from){ _driver.setHeaderFrom(from);}; + + /// Sets the ID header to be sent in all subsequent messages + /// \param[in] id The new ID header value + virtual void setHeaderId(uint8_t id){ _driver.setHeaderId(id);}; + + /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages + /// First it clears he FLAGS according to the clear argument, then sets the flags according to the + /// set argument. The default for clear always clears the application specific flags. + /// \param[in] set bitmask of bits to be set. Flags are cleared with the clear mask before being set. + /// \param[in] clear bitmask of flags to clear. Defaults to RH_FLAGS_APPLICATION_SPECIFIC + /// which clears the application specific flags, resulting in new application specific flags + /// identical to the set. + virtual void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_APPLICATION_SPECIFIC) { _driver.setHeaderFlags(set, clear);}; + + /// Tells the receiver to accept messages with any TO address, not just messages + /// addressed to thisAddress or the broadcast address + /// \param[in] promiscuous true if you wish to receive messages with any TO address + virtual void setPromiscuous(bool promiscuous){ _driver.setPromiscuous(promiscuous);}; + + /// Returns the TO header of the last received message + /// \return The TO header + virtual uint8_t headerTo() { return _driver.headerTo();}; + + /// Returns the FROM header of the last received message + /// \return The FROM header + virtual uint8_t headerFrom() { return _driver.headerFrom();}; + + /// Returns the ID header of the last received message + /// \return The ID header + virtual uint8_t headerId() { return _driver.headerId();}; + + /// Returns the FLAGS header of the last received message + /// \return The FLAGS header + virtual uint8_t headerFlags() { return _driver.headerFlags();}; + + /// Returns the most recent RSSI (Receiver Signal Strength Indicator). + /// Usually it is the RSSI of the last received message, which is measured when the preamble is received. + /// If you called readRssi() more recently, it will return that more recent value. + /// \return The most recent RSSI measurement in dBm. + int16_t lastRssi() { return _driver.lastRssi();}; + + /// Returns the operating mode of the library. + /// \return the current mode, one of RF69_MODE_* + RHMode mode() { return _driver.mode();}; + + /// Sets the operating mode of the transport. + void setMode(RHMode mode) { _driver.setMode(mode);}; + + /// Sets the transport hardware into low-power sleep mode + /// (if supported). May be overridden by specific drivers to initialte sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// \return true if sleep mode is supported by transport hardware and the RadioHead driver, and if sleep mode + /// was successfully entered. If sleep mode is not suported, return false. + virtual bool sleep() { return _driver.sleep();}; + + /// Returns the count of the number of bad received packets (ie packets with bad lengths, checksum etc) + /// which were rejected and not delivered to the application. + /// Caution: not all drivers can correctly report this count. Some underlying hardware only report + /// good packets. + /// \return The number of bad packets received. + virtual uint16_t rxBad() { return _driver.rxBad();}; + + /// Returns the count of the number of + /// good received packets + /// \return The number of good packets received. + virtual uint16_t rxGood() { return _driver.rxGood();}; + + /// Returns the count of the number of + /// packets successfully transmitted (though not necessarily received by the destination) + /// \return The number of packets successfully transmitted + virtual uint16_t txGood() { return _driver.txGood();}; + +private: + /// The underlying transport river we are to use + RHGenericDriver& _driver; + + /// The CipherBlock we are to use for encrypting/decrypting + BlockCipher& _blockcipher; + + /// Struct for with buffers for ciphering + typedef struct + { + size_t blockSize = 0; + uint8_t *inputBlock = NULL; + //uint8_t *outputBlock = NULL; + } CipherBlocks; + + CipherBlocks _cipheringBlocks; + + /// Buffer to store encrypted/decrypted message + uint8_t* _buffer; +}; + +/// @example nrf24_encrypted_client.pde +/// @example nrf24_encrypted_server.pde +/// @example rf95_encrypted_client.pde +/// @example rf95_encrypted_server.pde +/// @example serial_encrypted_reliable_datagram_client.pde +/// @example serial_encrypted_reliable_datagram_server.pde + + +#else // RH_ENABLE_ENCRYPTION_MODULE +#error "You have included RHEncryptedDriver.h, but not enabled RH_ENABLE_ENCRYPTION_MODULE in RadioHead.h" +#endif + +#endif diff --git a/RHGenericDriver.cpp b/RHGenericDriver.cpp new file mode 100644 index 0000000..6beb422 --- /dev/null +++ b/RHGenericDriver.cpp @@ -0,0 +1,221 @@ +// RHGenericDriver.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RHGenericDriver.cpp,v 1.24 2020/01/07 23:35:02 mikem Exp $ + +#include + +RHGenericDriver::RHGenericDriver() + : + _mode(RHModeInitialising), + _thisAddress(RH_BROADCAST_ADDRESS), + _txHeaderTo(RH_BROADCAST_ADDRESS), + _txHeaderFrom(RH_BROADCAST_ADDRESS), + _txHeaderId(0), + _txHeaderFlags(0), + _rxBad(0), + _rxGood(0), + _txGood(0), + _cad_timeout(0) +{ +} + +bool RHGenericDriver::init() +{ + return true; +} + +// Blocks until a valid message is received +void RHGenericDriver::waitAvailable() +{ + while (!available()) + YIELD; +} + +// Blocks until a valid message is received or timeout expires +// Return true if there is a message available +// Works correctly even on millis() rollover +bool RHGenericDriver::waitAvailableTimeout(uint16_t timeout) +{ + unsigned long starttime = millis(); + while ((millis() - starttime) < timeout) + { + if (available()) + { + return true; + } + YIELD; + } + return false; +} + +bool RHGenericDriver::waitPacketSent() +{ + while (_mode == RHModeTx) + YIELD; // Wait for any previous transmit to finish + return true; +} + +bool RHGenericDriver::waitPacketSent(uint16_t timeout) +{ + unsigned long starttime = millis(); + while ((millis() - starttime) < timeout) + { + if (_mode != RHModeTx) // Any previous transmit finished? + return true; + YIELD; + } + return false; +} + +// Wait until no channel activity detected or timeout +bool RHGenericDriver::waitCAD() +{ + if (!_cad_timeout) + return true; + + // Wait for any channel activity to finish or timeout + // Sophisticated DCF function... + // DCF : BackoffTime = random() x aSlotTime + // 100 - 1000 ms + // 10 sec timeout + unsigned long t = millis(); + while (isChannelActive()) + { + if (millis() - t > _cad_timeout) + return false; +#if (RH_PLATFORM == RH_PLATFORM_STM32) // stdlib on STMF103 gets confused if random is redefined + delay(_random(1, 10) * 100); +#else + delay(random(1, 10) * 100); // Should these values be configurable? Macros? +#endif + } + + return true; +} + +// subclasses are expected to override if CAD is available for that radio +bool RHGenericDriver::isChannelActive() +{ + return false; +} + +void RHGenericDriver::setPromiscuous(bool promiscuous) +{ + _promiscuous = promiscuous; +} + +void RHGenericDriver::setThisAddress(uint8_t address) +{ + _thisAddress = address; +} + +void RHGenericDriver::setHeaderTo(uint8_t to) +{ + _txHeaderTo = to; +} + +void RHGenericDriver::setHeaderFrom(uint8_t from) +{ + _txHeaderFrom = from; +} + +void RHGenericDriver::setHeaderId(uint8_t id) +{ + _txHeaderId = id; +} + +void RHGenericDriver::setHeaderFlags(uint8_t set, uint8_t clear) +{ + _txHeaderFlags &= ~clear; + _txHeaderFlags |= set; +} + +uint8_t RHGenericDriver::headerTo() +{ + return _rxHeaderTo; +} + +uint8_t RHGenericDriver::headerFrom() +{ + return _rxHeaderFrom; +} + +uint8_t RHGenericDriver::headerId() +{ + return _rxHeaderId; +} + +uint8_t RHGenericDriver::headerFlags() +{ + return _rxHeaderFlags; +} + +int16_t RHGenericDriver::lastRssi() +{ + return _lastRssi; +} + +RHGenericDriver::RHMode RHGenericDriver::mode() +{ + return _mode; +} + +void RHGenericDriver::setMode(RHMode mode) +{ + _mode = mode; +} + +bool RHGenericDriver::sleep() +{ + return false; +} + +// Diagnostic help +void RHGenericDriver::printBuffer(const char* prompt, const uint8_t* buf, uint8_t len) +{ +#ifdef RH_HAVE_SERIAL + Serial.println(prompt); + uint8_t i; + for (i = 0; i < len; i++) + { + if (i % 16 == 15) + Serial.println(buf[i], HEX); + else + { + Serial.print(buf[i], HEX); + Serial.print(' '); + } + } + Serial.println(""); +#endif +} + +uint16_t RHGenericDriver::rxBad() +{ + return _rxBad; +} + +uint16_t RHGenericDriver::rxGood() +{ + return _rxGood; +} + +uint16_t RHGenericDriver::txGood() +{ + return _txGood; +} + +void RHGenericDriver::setCADTimeout(unsigned long cad_timeout) +{ + _cad_timeout = cad_timeout; +} + +#if (RH_PLATFORM == RH_PLATFORM_ATTINY) +// Tinycore does not have __cxa_pure_virtual, so without this we +// get linking complaints from the default code generated for pure virtual functions +extern "C" void __cxa_pure_virtual() +{ + while (1); +} +#endif diff --git a/RHGenericDriver.h b/RHGenericDriver.h new file mode 100644 index 0000000..f3d6967 --- /dev/null +++ b/RHGenericDriver.h @@ -0,0 +1,310 @@ +// RHGenericDriver.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RHGenericDriver.h,v 1.24 2020/04/09 23:40:34 mikem Exp $ + +#ifndef RHGenericDriver_h +#define RHGenericDriver_h + +#include + +// Defines bits of the FLAGS header reserved for use by the RadioHead library and +// the flags available for use by applications +#define RH_FLAGS_RESERVED 0xf0 +#define RH_FLAGS_APPLICATION_SPECIFIC 0x0f +#define RH_FLAGS_NONE 0 + +// Default timeout for waitCAD() in ms +#define RH_CAD_DEFAULT_TIMEOUT 10000 + +///////////////////////////////////////////////////////////////////// +/// \class RHGenericDriver RHGenericDriver.h +/// \brief Abstract base class for a RadioHead driver. +/// +/// This class defines the functions that must be provided by any RadioHead driver. +/// Different types of driver will implement all the abstract functions, and will perhaps override +/// other functions in this subclass, or perhaps add new functions specifically required by that driver. +/// Do not directly instantiate this class: it is only to be subclassed by driver classes. +/// +/// Subclasses are expected to implement a half-duplex, unreliable, error checked, unaddressed packet transport. +/// They are expected to carry a message payload with an appropriate maximum length for the transport hardware +/// and to also carry unaltered 4 message headers: TO, FROM, ID, FLAGS +/// +/// \par Headers +/// +/// Each message sent and received by a RadioHead driver includes 4 headers: +/// -TO The node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted) +/// -FROM The node address of the sending node +/// -ID A message ID, distinct (over short time scales) for each message sent by a particilar node +/// -FLAGS A bitmask of flags. The most significant 4 bits are reserved for use by RadioHead. The least +/// significant 4 bits are reserved for applications. +class RHGenericDriver +{ +public: + /// \brief Defines different operating modes for the transport hardware + /// + /// These are the different values that can be adopted by the _mode variable and + /// returned by the mode() member function, + typedef enum + { + RHModeInitialising = 0, ///< Transport is initialising. Initial default value until init() is called.. + RHModeSleep, ///< Transport hardware is in low power sleep mode (if supported) + RHModeIdle, ///< Transport is idle. + RHModeTx, ///< Transport is in the process of transmitting a message. + RHModeRx, ///< Transport is in the process of receiving a message. + RHModeCad ///< Transport is in the process of detecting channel activity (if supported) + } RHMode; + + /// Constructor + RHGenericDriver(); + + /// Generic destructor to prevent warnings when objects are dynamically allocated + virtual ~RHGenericDriver() {}; + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, if there is an uncollected received message, and there is no message + /// currently bing transmitted, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it will be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop. + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv(). + virtual bool available() = 0; + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len) = 0; + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then optionally waits for Channel Activity Detection (CAD) + /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will + /// return false and will not send the message. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting. + /// \return true if the message length was valid and it was correctly queued for transmit. Return false + /// if CAD was requested and the CAD timeout timed out before clear channel was detected. + virtual bool send(const uint8_t* data, uint8_t len) = 0; + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength() = 0; + + /// Starts the receiver and blocks until a valid received + /// message is available. + virtual void waitAvailable(); + + /// Blocks until the transmitter + /// is no longer transmitting. + virtual bool waitPacketSent(); + + /// Blocks until the transmitter is no longer transmitting. + /// or until the timeout occuers, whichever happens first + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if the radio completed transmission within the timeout period. False if it timed out. + virtual bool waitPacketSent(uint16_t timeout); + + /// Starts the receiver and blocks until a received message is available or a timeout + /// \param[in] timeout Maximum time to wait in milliseconds. + /// \return true if a message is available + virtual bool waitAvailableTimeout(uint16_t timeout); + + // Bent G Christensen (bentor@gmail.com), 08/15/2016 + /// Channel Activity Detection (CAD). + /// Blocks until channel activity is finished or CAD timeout occurs. + /// Uses the radio's CAD function (if supported) to detect channel activity. + /// Implements random delays of 100 to 1000ms while activity is detected and until timeout. + /// Caution: the random() function is not seeded. If you want non-deterministic behaviour, consider + /// using something like randomSeed(analogRead(A0)); in your sketch. + /// Permits the implementation of listen-before-talk mechanism (Collision Avoidance). + /// Calls the isChannelActive() member function for the radio (if supported) + /// to determine if the channel is active. If the radio does not support isChannelActive(), + /// always returns true immediately + /// \return true if the radio-specific CAD (as returned by isChannelActive()) + /// shows the channel is clear within the timeout period (or the timeout period is 0), else returns false. + virtual bool waitCAD(); + + /// Sets the Channel Activity Detection timeout in milliseconds to be used by waitCAD(). + /// The default is 0, which means do not wait for CAD detection. + /// CAD detection depends on support for isChannelActive() by your particular radio. + void setCADTimeout(unsigned long cad_timeout); + + /// Determine if the currently selected radio channel is active. + /// This is expected to be subclassed by specific radios to implement their Channel Activity Detection + /// if supported. If the radio does not support CAD, returns true immediately. If a RadioHead radio + /// supports isChannelActive() it will be documented in the radio specific documentation. + /// This is called automatically by waitCAD(). + /// \return true if the radio-specific CAD (as returned by override of isChannelActive()) shows the + /// current radio channel as active, else false. If there is no radio-specific CAD, returns false. + virtual bool isChannelActive(); + + /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. + /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, + /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. + /// In promiscuous mode, all messages will be accepted regardless of the TO header. + /// In a conventional multinode system, all nodes will have a unique address + /// (which you could store in EEPROM). + /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, + /// allowing the possibilty of address spoofing). + /// \param[in] thisAddress The address of this node. + virtual void setThisAddress(uint8_t thisAddress); + + /// Sets the TO header to be sent in all subsequent messages + /// \param[in] to The new TO header value + virtual void setHeaderTo(uint8_t to); + + /// Sets the FROM header to be sent in all subsequent messages + /// \param[in] from The new FROM header value + virtual void setHeaderFrom(uint8_t from); + + /// Sets the ID header to be sent in all subsequent messages + /// \param[in] id The new ID header value + virtual void setHeaderId(uint8_t id); + + /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages + /// First it clears he FLAGS according to the clear argument, then sets the flags according to the + /// set argument. The default for clear always clears the application specific flags. + /// \param[in] set bitmask of bits to be set. Flags are cleared with the clear mask before being set. + /// \param[in] clear bitmask of flags to clear. Defaults to RH_FLAGS_APPLICATION_SPECIFIC + /// which clears the application specific flags, resulting in new application specific flags + /// identical to the set. + virtual void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_APPLICATION_SPECIFIC); + + /// Tells the receiver to accept messages with any TO address, not just messages + /// addressed to thisAddress or the broadcast address + /// \param[in] promiscuous true if you wish to receive messages with any TO address + virtual void setPromiscuous(bool promiscuous); + + /// Returns the TO header of the last received message + /// \return The TO header + virtual uint8_t headerTo(); + + /// Returns the FROM header of the last received message + /// \return The FROM header + virtual uint8_t headerFrom(); + + /// Returns the ID header of the last received message + /// \return The ID header + virtual uint8_t headerId(); + + /// Returns the FLAGS header of the last received message + /// \return The FLAGS header + virtual uint8_t headerFlags(); + + /// Returns the most recent RSSI (Receiver Signal Strength Indicator). + /// Usually it is the RSSI of the last received message, which is measured when the preamble is received. + /// If you called readRssi() more recently, it will return that more recent value. + /// \return The most recent RSSI measurement in dBm. + virtual int16_t lastRssi(); + + /// Returns the operating mode of the library. + /// \return the current mode, one of RF69_MODE_* + virtual RHMode mode(); + + /// Sets the operating mode of the transport. + virtual void setMode(RHMode mode); + + /// Sets the transport hardware into low-power sleep mode + /// (if supported). May be overridden by specific drivers to initialte sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// \return true if sleep mode is supported by transport hardware and the RadioHead driver, and if sleep mode + /// was successfully entered. If sleep mode is not suported, return false. + virtual bool sleep(); + + /// Prints a data buffer in HEX. + /// For diagnostic use + /// \param[in] prompt string to preface the print + /// \param[in] buf Location of the buffer to print + /// \param[in] len Length of the buffer in octets. + static void printBuffer(const char* prompt, const uint8_t* buf, uint8_t len); + + /// Returns the count of the number of bad received packets (ie packets with bad lengths, checksum etc) + /// which were rejected and not delivered to the application. + /// Caution: not all drivers can correctly report this count. Some underlying hardware only report + /// good packets. + /// \return The number of bad packets received. + virtual uint16_t rxBad(); + + /// Returns the count of the number of + /// good received packets + /// \return The number of good packets received. + virtual uint16_t rxGood(); + + /// Returns the count of the number of + /// packets successfully transmitted (though not necessarily received by the destination) + /// \return The number of packets successfully transmitted + virtual uint16_t txGood(); + +protected: + + /// The current transport operating mode + volatile RHMode _mode; + + /// This node id + uint8_t _thisAddress; + + /// Whether the transport is in promiscuous mode + bool _promiscuous; + + /// TO header in the last received mesasge + volatile uint8_t _rxHeaderTo; + + /// FROM header in the last received mesasge + volatile uint8_t _rxHeaderFrom; + + /// ID header in the last received mesasge + volatile uint8_t _rxHeaderId; + + /// FLAGS header in the last received mesasge + volatile uint8_t _rxHeaderFlags; + + /// TO header to send in all messages + uint8_t _txHeaderTo; + + /// FROM header to send in all messages + uint8_t _txHeaderFrom; + + /// ID header to send in all messages + uint8_t _txHeaderId; + + /// FLAGS header to send in all messages + uint8_t _txHeaderFlags; + + /// The value of the last received RSSI value, in some transport specific units + volatile int16_t _lastRssi; + + /// Count of the number of bad messages (eg bad checksum etc) received + volatile uint16_t _rxBad; + + /// Count of the number of successfully transmitted messaged + volatile uint16_t _rxGood; + + /// Count of the number of bad messages (correct checksum etc) received + volatile uint16_t _txGood; + + /// Channel activity detected + volatile bool _cad; + + /// Channel activity timeout in ms + unsigned int _cad_timeout; + +private: + +}; + +#endif diff --git a/RHGenericSPI.cpp b/RHGenericSPI.cpp new file mode 100644 index 0000000..ec43cd4 --- /dev/null +++ b/RHGenericSPI.cpp @@ -0,0 +1,31 @@ +// RHGenericSPI.cpp +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// Contributed by Joanna Rutkowska +// $Id: RHGenericSPI.cpp,v 1.2 2014/04/12 05:26:05 mikem Exp $ + +#include + +RHGenericSPI::RHGenericSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode) + : + _frequency(frequency), + _bitOrder(bitOrder), + _dataMode(dataMode) +{ +} + +void RHGenericSPI::setBitOrder(BitOrder bitOrder) +{ + _bitOrder = bitOrder; +} + +void RHGenericSPI::setDataMode(DataMode dataMode) +{ + _dataMode = dataMode; +} + +void RHGenericSPI::setFrequency(Frequency frequency) +{ + _frequency = frequency; +} + diff --git a/RHGenericSPI.h b/RHGenericSPI.h new file mode 100644 index 0000000..df6fbd9 --- /dev/null +++ b/RHGenericSPI.h @@ -0,0 +1,183 @@ +// RHGenericSPI.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// Contributed by Joanna Rutkowska +// $Id: RHGenericSPI.h,v 1.9 2020/01/05 07:02:23 mikem Exp $ + +#ifndef RHGenericSPI_h +#define RHGenericSPI_h + +#include + +///////////////////////////////////////////////////////////////////// +/// \class RHGenericSPI RHGenericSPI.h +/// \brief Base class for SPI interfaces +/// +/// This generic abstract class is used to encapsulate hardware or software SPI interfaces for +/// a variety of platforms. +/// The intention is so that driver classes can be configured to use hardware or software SPI +/// without changing the main code. +/// +/// You must provide a subclass of this class to driver constructors that require SPI. +/// A concrete subclass that encapsualates the standard Arduino hardware SPI and a bit-banged +/// software implementation is included. +/// +/// Do not directly use this class: it must be subclassed and the following abstract functions at least +/// must be implmented: +/// - begin() +/// - end() +/// - transfer() +class RHGenericSPI +{ +public: + + /// \brief Defines constants for different SPI modes + /// + /// Defines constants for different SPI modes + /// that can be passed to the constructor or setMode() + /// We need to define these in a device and platform independent way, because the + /// SPI implementation is different on each platform. + typedef enum + { + DataMode0 = 0, ///< SPI Mode 0: CPOL = 0, CPHA = 0 + DataMode1, ///< SPI Mode 1: CPOL = 0, CPHA = 1 + DataMode2, ///< SPI Mode 2: CPOL = 1, CPHA = 0 + DataMode3, ///< SPI Mode 3: CPOL = 1, CPHA = 1 + } DataMode; + + /// \brief Defines constants for different SPI bus frequencies + /// + /// Defines constants for different SPI bus frequencies + /// that can be passed to setFrequency(). + /// The frequency you get may not be exactly the one according to the name. + /// We need to define these in a device and platform independent way, because the + /// SPI implementation is different on each platform. + typedef enum + { + Frequency1MHz = 0, ///< SPI bus frequency close to 1MHz + Frequency2MHz, ///< SPI bus frequency close to 2MHz + Frequency4MHz, ///< SPI bus frequency close to 4MHz + Frequency8MHz, ///< SPI bus frequency close to 8MHz + Frequency16MHz ///< SPI bus frequency close to 16MHz + } Frequency; + + /// \brief Defines constants for different SPI endianness + /// + /// Defines constants for different SPI endianness + /// that can be passed to setBitOrder() + /// We need to define these in a device and platform independent way, because the + /// SPI implementation is different on each platform. + typedef enum + { + BitOrderMSBFirst = 0, ///< SPI MSB first + BitOrderLSBFirst, ///< SPI LSB first + } BitOrder; + + /// Constructor + /// Creates an instance of an abstract SPI interface. + /// Do not use this contructor directly: you must instead use on of the concrete subclasses provided + /// such as RHHardwareSPI or RHSoftwareSPI + /// \param[in] frequency One of RHGenericSPI::Frequency to select the SPI bus frequency. The frequency + /// is mapped to the closest available bus frequency on the platform. + /// \param[in] bitOrder Select the SPI bus bit order, one of RHGenericSPI::BitOrderMSBFirst or + /// RHGenericSPI::BitOrderLSBFirst. + /// \param[in] dataMode Selects the SPI bus data mode. One of RHGenericSPI::DataMode + RHGenericSPI(Frequency frequency = Frequency1MHz, BitOrder bitOrder = BitOrderMSBFirst, DataMode dataMode = DataMode0); + + /// Transfer a single octet to and from the SPI interface + /// \param[in] data The octet to send + /// \return The octet read from SPI while the data octet was sent + virtual uint8_t transfer(uint8_t data) = 0; + +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + /// Transfer up to 2 bytes on the SPI interface + /// \param[in] byte0 The first byte to be sent on the SPI interface + /// \param[in] byte1 The second byte to be sent on the SPI interface + /// \return The second byte clocked in as the second byte is sent. + virtual uint8_t transfer2B(uint8_t byte0, uint8_t byte1) = 0; + + /// Read a number of bytes on the SPI interface from an NRF device + /// \param[in] reg The NRF device register to read + /// \param[out] dest The buffer to hold the bytes read + /// \param[in] len The number of bytes to read + /// \return The NRF status byte + virtual uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len) = 0; + + /// Wrte a number of bytes on the SPI interface to an NRF device + /// \param[in] reg The NRF device register to read + /// \param[out] src The buffer to hold the bytes write + /// \param[in] len The number of bytes to write + /// \return The NRF status byte + virtual uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len) = 0; + +#endif + + /// SPI Configuration methods + /// Enable SPI interrupts (if supported) + /// This can be used in an SPI slave to indicate when an SPI message has been received + virtual void attachInterrupt() {}; + + /// Disable SPI interrupts (if supported) + /// This can be used to diable the SPI interrupt in slaves where that is supported. + virtual void detachInterrupt() {}; + + /// Initialise the SPI library. + /// Call this after configuring and before using the SPI library + virtual void begin() = 0; + + /// Disables the SPI bus (leaving pin modes unchanged). + /// Call this after you have finished using the SPI interface + virtual void end() = 0; + + /// Sets the bit order the SPI interface will use + /// Sets the order of the bits shifted out of and into the SPI bus, either + /// LSBFIRST (least-significant bit first) or MSBFIRST (most-significant bit first). + /// \param[in] bitOrder Bit order to be used: one of RHGenericSPI::BitOrder + virtual void setBitOrder(BitOrder bitOrder); + + /// Sets the SPI data mode: that is, clock polarity and phase. + /// See the Wikipedia article on SPI for details. + /// \param[in] dataMode The mode to use: one of RHGenericSPI::DataMode + virtual void setDataMode(DataMode dataMode); + + /// Sets the SPI clock divider relative to the system clock. + /// On AVR based boards, the dividers available are 2, 4, 8, 16, 32, 64 or 128. + /// The default setting is SPI_CLOCK_DIV4, which sets the SPI clock to one-quarter + /// the frequency of the system clock (4 Mhz for the boards at 16 MHz). + /// \param[in] frequency The data rate to use: one of RHGenericSPI::Frequency + virtual void setFrequency(Frequency frequency); + + /// Signal the start of an SPI transaction that must not be interrupted by other SPI actions + /// In subclasses that support transactions this will ensure that other SPI transactions + /// are blocked until this one is completed by endTransaction(). + /// Base does nothing + /// Might be overridden in subclass + virtual void beginTransaction(){} + + /// Signal the end of an SPI transaction + /// Base does nothing + /// Might be overridden in subclass + virtual void endTransaction(){} + + /// Specify the interrupt number of the interrupt that will use SPI transactions + /// Tells the SPI support software that SPI transactions will occur with the interrupt + /// handler assocated with interruptNumber + /// Base does nothing + /// Might be overridden in subclass + /// \param[in] interruptNumber The number of the interrupt + virtual void usingInterrupt(uint8_t interruptNumber){ + (void)interruptNumber; + } + +protected: + + /// The configure SPI Bus frequency, one of RHGenericSPI::Frequency + Frequency _frequency; // Bus frequency, one of RHGenericSPI::Frequency + + /// Bit order, one of RHGenericSPI::BitOrder + BitOrder _bitOrder; + + /// SPI bus mode, one of RHGenericSPI::DataMode + DataMode _dataMode; +}; +#endif diff --git a/RHHardwareSPI.cpp b/RHHardwareSPI.cpp new file mode 100644 index 0000000..aaeae77 --- /dev/null +++ b/RHHardwareSPI.cpp @@ -0,0 +1,505 @@ +// RHHardwareSPI.cpp +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// Contributed by Joanna Rutkowska +// $Id: RHHardwareSPI.cpp,v 1.28 2020/06/15 23:39:39 mikem Exp $ + +#include + +#ifdef RH_HAVE_HARDWARE_SPI + +// Declare a single default instance of the hardware SPI interface class +RHHardwareSPI hardware_spi; + + +#if (RH_PLATFORM == RH_PLATFORM_STM32) // Maple etc +// Declare an SPI interface to use +HardwareSPI SPI(1); +#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32F4 Discovery +// Declare an SPI interface to use +HardwareSPI SPI(1); +#elif (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) // Mongoose OS platform +HardwareSPI SPI(1); +#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) && (defined STM32L082xx || defined STM32L072xx) + extern SPIClass radio_spi; // Created in RH_ABZ.cpp + #define SPI radio_spi +#endif + + +// Arduino Due has default SPI pins on central SPI headers, and not on 10, 11, 12, 13 +// as per other Arduinos +// http://21stdigitalhome.blogspot.com.au/2013/02/arduino-due-hardware-spi.html +#if defined (__arm__) && !defined(CORE_TEENSY) && !defined(SPI_CLOCK_DIV16) && !defined(RH_PLATFORM_NRF52) + // Arduino Due in 1.5.5 has no definitions for SPI dividers + // SPI clock divider is based on MCK of 84MHz + #define SPI_CLOCK_DIV16 (VARIANT_MCK/84000000) // 1MHz + #define SPI_CLOCK_DIV8 (VARIANT_MCK/42000000) // 2MHz + #define SPI_CLOCK_DIV4 (VARIANT_MCK/21000000) // 4MHz + #define SPI_CLOCK_DIV2 (VARIANT_MCK/10500000) // 8MHz + #define SPI_CLOCK_DIV1 (VARIANT_MCK/5250000) // 16MHz +#endif + +RHHardwareSPI::RHHardwareSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode) + : + RHGenericSPI(frequency, bitOrder, dataMode) +{ +} + +uint8_t RHHardwareSPI::transfer(uint8_t data) +{ + return SPI.transfer(data); +} + +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) +uint8_t RHHardwareSPI::transfer2B(uint8_t byte0, uint8_t byte1) +{ + return SPI.transfer2B(byte0, byte1); +} + +uint8_t RHHardwareSPI::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len) +{ + return SPI.spiBurstRead(reg, dest, len); +} + +uint8_t RHHardwareSPI::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len) +{ + uint8_t status = SPI.spiBurstWrite(reg, src, len); + return status; +} +#endif + +void RHHardwareSPI::attachInterrupt() +{ +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO || RH_PLATFORM == RH_PLATFORM_NRF52) + SPI.attachInterrupt(); +#endif +} + +void RHHardwareSPI::detachInterrupt() +{ +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO || RH_PLATFORM == RH_PLATFORM_NRF52) + SPI.detachInterrupt(); +#endif +} + +void RHHardwareSPI::begin() +{ +#if defined(SPI_HAS_TRANSACTION) + // Perhaps this is a uniform interface for SPI? + // Currently Teensy and ESP32 only + uint32_t frequency; + if (_frequency == Frequency16MHz) + frequency = 16000000; + else if (_frequency == Frequency8MHz) + frequency = 8000000; + else if (_frequency == Frequency4MHz) + frequency = 4000000; + else if (_frequency == Frequency2MHz) + frequency = 2000000; + else + frequency = 1000000; + +#if ((RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_SAM_DUE) || defined(ARDUINO_ARCH_SAMD))) || defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32L0) || defined(NRF52) + // Arduino Due in 1.5.5 has its own BitOrder :-( + // So too does Arduino Zero + // So too does rogerclarkmelbourne/Arduino_STM32 + // So too does GrumpyOldPizza/ArduinoCore-stm32l0 + ::BitOrder bitOrder; +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA) + ::BitOrder bitOrder; +#else + uint8_t bitOrder; +#endif + + if (_bitOrder == BitOrderLSBFirst) + bitOrder = LSBFIRST; + else + bitOrder = MSBFIRST; + + uint8_t dataMode; + if (_dataMode == DataMode0) + dataMode = SPI_MODE0; + else if (_dataMode == DataMode1) + dataMode = SPI_MODE1; + else if (_dataMode == DataMode2) + dataMode = SPI_MODE2; + else if (_dataMode == DataMode3) + dataMode = SPI_MODE3; + else + dataMode = SPI_MODE0; + + // Save the settings for use in transactions + _settings = SPISettings(frequency, bitOrder, dataMode); + SPI.begin(); + +#else // SPI_HAS_TRANSACTION + + // Sigh: there are no common symbols for some of these SPI options across all platforms +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_UNO32) || (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE || RH_PLATFORM == RH_PLATFORM_NRF52) + uint8_t dataMode; + if (_dataMode == DataMode0) + dataMode = SPI_MODE0; + else if (_dataMode == DataMode1) + dataMode = SPI_MODE1; + else if (_dataMode == DataMode2) + dataMode = SPI_MODE2; + else if (_dataMode == DataMode3) + dataMode = SPI_MODE3; + else + dataMode = SPI_MODE0; +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(CORE_TEENSY) + // Temporary work-around due to problem where avr_emulation.h does not work properly for the setDataMode() cal + SPCR &= ~SPI_MODE_MASK; +#else + #if ((RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && defined(ARDUINO_ARCH_SAMD)) || defined(ARDUINO_ARCH_NRF52) + // Zero requires begin() before anything else :-) + SPI.begin(); + #endif + + SPI.setDataMode(dataMode); +#endif + +#if ((RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_SAM_DUE) || defined(ARDUINO_ARCH_SAMD))) || defined(ARDUINO_ARCH_NRF52) || defined (ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_STM32F1) + // Arduino Due in 1.5.5 has its own BitOrder :-( + // So too does Arduino Zero + // So too does rogerclarkmelbourne/Arduino_STM32 + // So too does stm32duino F1, F4 + ::BitOrder bitOrder; +#else + uint8_t bitOrder; +#endif + if (_bitOrder == BitOrderLSBFirst) + bitOrder = LSBFIRST; + else + bitOrder = MSBFIRST; + SPI.setBitOrder(bitOrder); + uint8_t divider; + switch (_frequency) + { + case Frequency1MHz: + default: +#if F_CPU == 8000000 + divider = SPI_CLOCK_DIV8; +#else + divider = SPI_CLOCK_DIV16; +#endif + break; + + case Frequency2MHz: +#if F_CPU == 8000000 + divider = SPI_CLOCK_DIV4; +#else + divider = SPI_CLOCK_DIV8; +#endif + break; + + case Frequency4MHz: +#if F_CPU == 8000000 + divider = SPI_CLOCK_DIV2; +#else + divider = SPI_CLOCK_DIV4; +#endif + break; + + case Frequency8MHz: + divider = SPI_CLOCK_DIV2; // 4MHz on an 8MHz Arduino + break; + + case Frequency16MHz: + divider = SPI_CLOCK_DIV2; // Not really 16MHz, only 8MHz. 4MHz on an 8MHz Arduino + break; + + } + SPI.setClockDivider(divider); + SPI.begin(); + // Teensy requires it to be set _after_ begin() + SPI.setClockDivider(divider); + +#elif (RH_PLATFORM == RH_PLATFORM_STM32) // Maple etc + spi_mode dataMode; + // Hmmm, if we do this as a switch, GCC on maple gets v confused! + if (_dataMode == DataMode0) + dataMode = SPI_MODE_0; + else if (_dataMode == DataMode1) + dataMode = SPI_MODE_1; + else if (_dataMode == DataMode2) + dataMode = SPI_MODE_2; + else if (_dataMode == DataMode3) + dataMode = SPI_MODE_3; + else + dataMode = SPI_MODE_0; + + uint32 bitOrder; + if (_bitOrder == BitOrderLSBFirst) + bitOrder = LSBFIRST; + else + bitOrder = MSBFIRST; + + SPIFrequency frequency; // Yes, I know these are not exact equivalents. + switch (_frequency) + { + case Frequency1MHz: + default: + frequency = SPI_1_125MHZ; + break; + + case Frequency2MHz: + frequency = SPI_2_25MHZ; + break; + + case Frequency4MHz: + frequency = SPI_4_5MHZ; + break; + + case Frequency8MHz: + frequency = SPI_9MHZ; + break; + + case Frequency16MHz: + frequency = SPI_18MHZ; + break; + + } + SPI.begin(frequency, bitOrder, dataMode); + +#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32F4 discovery + uint8_t dataMode; + if (_dataMode == DataMode0) + dataMode = SPI_MODE0; + else if (_dataMode == DataMode1) + dataMode = SPI_MODE1; + else if (_dataMode == DataMode2) + dataMode = SPI_MODE2; + else if (_dataMode == DataMode3) + dataMode = SPI_MODE3; + else + dataMode = SPI_MODE0; + + uint32_t bitOrder; + if (_bitOrder == BitOrderLSBFirst) + bitOrder = LSBFIRST; + else + bitOrder = MSBFIRST; + + SPIFrequency frequency; // Yes, I know these are not exact equivalents. + switch (_frequency) + { + case Frequency1MHz: + default: + frequency = SPI_1_3125MHZ; + break; + + case Frequency2MHz: + frequency = SPI_2_625MHZ; + break; + + case Frequency4MHz: + frequency = SPI_5_25MHZ; + break; + + case Frequency8MHz: + frequency = SPI_10_5MHZ; + break; + + case Frequency16MHz: + frequency = SPI_21_0MHZ; + break; + + } + SPI.begin(frequency, bitOrder, dataMode); + +#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Photon + uint8_t dataMode; + if (_dataMode == DataMode0) + dataMode = SPI_MODE0; + else if (_dataMode == DataMode1) + dataMode = SPI_MODE1; + else if (_dataMode == DataMode2) + dataMode = SPI_MODE2; + else if (_dataMode == DataMode3) + dataMode = SPI_MODE3; + else + dataMode = SPI_MODE0; + SPI.setDataMode(dataMode); + if (_bitOrder == BitOrderLSBFirst) + SPI.setBitOrder(LSBFIRST); + else + SPI.setBitOrder(MSBFIRST); + + switch (_frequency) + { + case Frequency1MHz: + default: + SPI.setClockSpeed(1, MHZ); + break; + + case Frequency2MHz: + SPI.setClockSpeed(2, MHZ); + break; + + case Frequency4MHz: + SPI.setClockSpeed(4, MHZ); + break; + + case Frequency8MHz: + SPI.setClockSpeed(8, MHZ); + break; + + case Frequency16MHz: + SPI.setClockSpeed(16, MHZ); + break; + } + +// SPI.setClockDivider(SPI_CLOCK_DIV4); // 72MHz / 4MHz = 18MHz +// SPI.setClockSpeed(1, MHZ); + SPI.begin(); + +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) + // Requires SPI driver for ESP8266 from https://github.com/esp8266/Arduino/tree/master/libraries/SPI + // Which ppears to be in Arduino Board Manager ESP8266 Community version 2.1.0 + // Contributed by David Skinner + // begin comes first + SPI.begin(); + + // datamode + switch ( _dataMode ) + { + case DataMode1: + SPI.setDataMode ( SPI_MODE1 ); + break; + case DataMode2: + SPI.setDataMode ( SPI_MODE2 ); + break; + case DataMode3: + SPI.setDataMode ( SPI_MODE3 ); + break; + case DataMode0: + default: + SPI.setDataMode ( SPI_MODE0 ); + break; + } + + // bitorder + SPI.setBitOrder(_bitOrder == BitOrderLSBFirst ? LSBFIRST : MSBFIRST); + + // frequency (this sets the divider) + switch (_frequency) + { + case Frequency1MHz: + default: + SPI.setFrequency(1000000); + break; + case Frequency2MHz: + SPI.setFrequency(2000000); + break; + case Frequency4MHz: + SPI.setFrequency(4000000); + break; + case Frequency8MHz: + SPI.setFrequency(8000000); + break; + case Frequency16MHz: + SPI.setFrequency(16000000); + break; + } + +#elif (RH_PLATFORM == RH_PLATFORM_RASPI) // Raspberry PI + uint8_t dataMode; + if (_dataMode == DataMode0) + dataMode = BCM2835_SPI_MODE0; + else if (_dataMode == DataMode1) + dataMode = BCM2835_SPI_MODE1; + else if (_dataMode == DataMode2) + dataMode = BCM2835_SPI_MODE2; + else if (_dataMode == DataMode3) + dataMode = BCM2835_SPI_MODE3; + + uint8_t bitOrder; + if (_bitOrder == BitOrderLSBFirst) + bitOrder = BCM2835_SPI_BIT_ORDER_LSBFIRST; + else + bitOrder = BCM2835_SPI_BIT_ORDER_MSBFIRST; + + uint32_t divider; + switch (_frequency) + { + case Frequency1MHz: + default: + divider = BCM2835_SPI_CLOCK_DIVIDER_256; + break; + case Frequency2MHz: + divider = BCM2835_SPI_CLOCK_DIVIDER_128; + break; + case Frequency4MHz: + divider = BCM2835_SPI_CLOCK_DIVIDER_64; + break; + case Frequency8MHz: + divider = BCM2835_SPI_CLOCK_DIVIDER_32; + break; + case Frequency16MHz: + divider = BCM2835_SPI_CLOCK_DIVIDER_16; + break; + } + SPI.begin(divider, bitOrder, dataMode); +#elif (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + uint8_t dataMode = SPI_MODE0; + uint32_t frequency = 4000000; //!!! ESP32/NRF902 works ok at 4MHz but not at 8MHz SPI clock. + uint32_t bitOrder = MSBFIRST; + + if (_dataMode == DataMode0) { + dataMode = SPI_MODE0; + } else if (_dataMode == DataMode1) { + dataMode = SPI_MODE1; + } else if (_dataMode == DataMode2) { + dataMode = SPI_MODE2; + } else if (_dataMode == DataMode3) { + dataMode = SPI_MODE3; + } + + if (_bitOrder == BitOrderLSBFirst) { + bitOrder = LSBFIRST; + } + + if (_frequency == Frequency4MHz) + frequency = 4000000; + else if (_frequency == Frequency2MHz) + frequency = 2000000; + else + frequency = 1000000; + + SPI.begin(frequency, bitOrder, dataMode); +#else + #warning RHHardwareSPI does not support this platform yet. Consider adding it and contributing a patch. +#endif + +#endif // SPI_HAS_TRANSACTION +} + +void RHHardwareSPI::end() +{ + return SPI.end(); +} + +void RHHardwareSPI::beginTransaction() +{ +#if defined(SPI_HAS_TRANSACTION) + SPI.beginTransaction(_settings); +#endif +} + +void RHHardwareSPI::endTransaction() +{ +#if defined(SPI_HAS_TRANSACTION) + SPI.endTransaction(); +#endif +} + +void RHHardwareSPI::usingInterrupt(uint8_t interrupt) +{ +#if defined(SPI_HAS_TRANSACTION) && !defined(RH_MISSING_SPIUSINGINTERRUPT) + SPI.usingInterrupt(interrupt); +#endif + (void)interrupt; +} + +#endif diff --git a/RHHardwareSPI.h b/RHHardwareSPI.h new file mode 100644 index 0000000..952f953 --- /dev/null +++ b/RHHardwareSPI.h @@ -0,0 +1,116 @@ +// RHHardwareSPI.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// Contributed by Joanna Rutkowska +// $Id: RHHardwareSPI.h,v 1.12 2020/01/05 07:02:23 mikem Exp $ + +#ifndef RHHardwareSPI_h +#define RHHardwareSPI_h + +#include + +///////////////////////////////////////////////////////////////////// +/// \class RHHardwareSPI RHHardwareSPI.h +/// \brief Encapsulate a hardware SPI bus interface +/// +/// This concrete subclass of GenericSPIClass encapsulates the standard Arduino hardware and other +/// hardware SPI interfaces. +/// +/// SPI transactions are supported in development environments that support it with SPI_HAS_TRANSACTION. +class RHHardwareSPI : public RHGenericSPI +{ +#ifdef RH_HAVE_HARDWARE_SPI +public: + /// Constructor + /// Creates an instance of a hardware SPI interface, using whatever SPI hardware is available on + /// your processor platform. On Arduino and Uno32, uses SPI. On Maple, uses HardwareSPI. + /// \param[in] frequency One of RHGenericSPI::Frequency to select the SPI bus frequency. The frequency + /// is mapped to the closest available bus frequency on the platform. + /// \param[in] bitOrder Select the SPI bus bit order, one of RHGenericSPI::BitOrderMSBFirst or + /// RHGenericSPI::BitOrderLSBFirst. + /// \param[in] dataMode Selects the SPI bus data mode. One of RHGenericSPI::DataMode + RHHardwareSPI(Frequency frequency = Frequency1MHz, BitOrder bitOrder = BitOrderMSBFirst, DataMode dataMode = DataMode0); + + /// Transfer a single octet to and from the SPI interface + /// \param[in] data The octet to send + /// \return The octet read from SPI while the data octet was sent + uint8_t transfer(uint8_t data); + +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + /// Transfer (write) 2 bytes on the SPI interface to an NRF device + /// \param[in] byte0 The first byte to be sent on the SPI interface + /// \param[in] byte1 The second byte to be sent on the SPI interface + /// \return The second byte clocked in as the second byte is sent. + uint8_t transfer2B(uint8_t byte0, uint8_t byte1); + + /// Read a number of bytes on the SPI interface from an NRF device + /// \param[in] reg The NRF device register to read + /// \param[out] dest The buffer to hold the bytes read + /// \param[in] len The number of bytes to read + /// \return The NRF status byte + uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len); + + /// Wrte a number of bytes on the SPI interface to an NRF device + /// \param[in] reg The NRF device register to read + /// \param[out] src The buffer to hold the bytes write + /// \param[in] len The number of bytes to write + /// \return The NRF status byte + uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len); + +#endif + + // SPI Configuration methods + /// Enable SPI interrupts + /// This can be used in an SPI slave to indicate when an SPI message has been received + /// It will cause the SPI_STC_vect interrupt vectr to be executed + void attachInterrupt(); + + /// Disable SPI interrupts + /// This can be used to diable the SPI interrupt in slaves where that is supported. + void detachInterrupt(); + + /// Initialise the SPI library + /// Call this after configuring the SPI interface and before using it to transfer data. + /// Initializes the SPI bus by setting SCK, MOSI, and SS to outputs, pulling SCK and MOSI low, and SS high. + void begin(); + + /// Disables the SPI bus (leaving pin modes unchanged). + /// Call this after you have finished using the SPI interface. + void end(); +#else + // not supported on ATTiny etc + uint8_t transfer(uint8_t /*data*/) {return 0;} + void begin(){} + void end(){} + +#endif + + /// Signal the start of an SPI transaction that must not be interrupted by other SPI actions + /// In subclasses that support transactions this will ensure that other SPI transactions + /// are blocked until this one is completed by endTransaction(). + /// Uses the underlying SPI transaction support if available as specified by SPI_HAS_TRANSACTION. + virtual void beginTransaction(); + + /// Signal the end of an SPI transaction + /// Uses the underlying SPI transaction support if available as specified by SPI_HAS_TRANSACTION. + virtual void endTransaction(); + + /// Specify the interrupt number of the interrupt that will use SPI transactions + /// Tells the SPI support software that SPI transactions will occur with the interrupt + /// handler assocated with interruptNumber + /// Uses the underlying SPI transaction support if available as specified by SPI_HAS_TRANSACTION. + /// \param[in] interruptNumber The number of the interrupt + virtual void usingInterrupt(uint8_t interruptNumber); + +protected: + +#if defined(SPI_HAS_TRANSACTION) + // Storage for SPI settings used in SPI transactions + SPISettings _settings; +#endif +}; + +// Built in default instance +extern RHHardwareSPI hardware_spi; + +#endif diff --git a/RHMesh.cpp b/RHMesh.cpp new file mode 100644 index 0000000..6a6eec7 --- /dev/null +++ b/RHMesh.cpp @@ -0,0 +1,253 @@ +// RHMesh.cpp +// +// Define addressed datagram +// +// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers +// (see http://www.hoperf.com) +// RHDatagram will be received only by the addressed node or all nodes within range if the +// to address is RH_BROADCAST_ADDRESS +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHMesh.cpp,v 1.11 2019/09/06 04:40:40 mikem Exp $ + +#include + +uint8_t RHMesh::_tmpMessage[RH_ROUTER_MAX_MESSAGE_LEN]; + +//////////////////////////////////////////////////////////////////// +// Constructors +RHMesh::RHMesh(RHGenericDriver& driver, uint8_t thisAddress) + : RHRouter(driver, thisAddress) +{ +} + +//////////////////////////////////////////////////////////////////// +// Public methods + +//////////////////////////////////////////////////////////////////// +// Discovers a route to the destination (if necessary), sends and +// waits for delivery to the next hop (but not for delivery to the final destination) +uint8_t RHMesh::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address, uint8_t flags) +{ + if (len > RH_MESH_MAX_MESSAGE_LEN) + return RH_ROUTER_ERROR_INVALID_LENGTH; + + if (address != RH_BROADCAST_ADDRESS) + { + RoutingTableEntry* route = getRouteTo(address); + if (!route && !doArp(address)) + return RH_ROUTER_ERROR_NO_ROUTE; + } + + // Now have a route. Contruct an application layer message and send it via that route + MeshApplicationMessage* a = (MeshApplicationMessage*)&_tmpMessage; + a->header.msgType = RH_MESH_MESSAGE_TYPE_APPLICATION; + memcpy(a->data, buf, len); + return RHRouter::sendtoWait(_tmpMessage, sizeof(RHMesh::MeshMessageHeader) + len, address, flags); +} + +//////////////////////////////////////////////////////////////////// +bool RHMesh::doArp(uint8_t address) +{ + // Need to discover a route + // Broadcast a route discovery message with nothing in it + MeshRouteDiscoveryMessage* p = (MeshRouteDiscoveryMessage*)&_tmpMessage; + p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST; + p->destlen = 1; + p->dest = address; // Who we are looking for + uint8_t error = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 2, RH_BROADCAST_ADDRESS); + if (error != RH_ROUTER_ERROR_NONE) + return false; + + // Wait for a reply, which will be unicast back to us + // It will contain the complete route to the destination + uint8_t messageLen = sizeof(_tmpMessage); + // FIXME: timeout should be configurable + unsigned long starttime = millis(); + int32_t timeLeft; + while ((timeLeft = RH_MESH_ARP_TIMEOUT - (millis() - starttime)) > 0) + { + if (waitAvailableTimeout(timeLeft)) + { + if (RHRouter::recvfromAck(_tmpMessage, &messageLen)) + { + if ( messageLen > 1 + && p->header.msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE) + { + // Got a reply, now add the next hop to the dest to the routing table + // The first hop taken is the first octet + addRouteTo(address, headerFrom()); + return true; + } + } + } + YIELD; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Called by RHRouter::recvfromAck whenever a message goes past +void RHMesh::peekAtMessage(RoutedMessage* message, uint8_t messageLen) +{ + MeshMessageHeader* m = (MeshMessageHeader*)message->data; + if ( messageLen > 1 + && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE) + { + // This is a unicast RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE messages + // being routed back to the originator here. Want to scrape some routing data out of the response + // We can find the routes to all the nodes between here and the responding node + MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)message->data; + addRouteTo(d->dest, headerFrom()); + uint8_t numRoutes = messageLen - sizeof(RoutedMessageHeader) - sizeof(MeshMessageHeader) - 2; + uint8_t i; + // Find us in the list of nodes that were traversed to get to the responding node + for (i = 0; i < numRoutes; i++) + if (d->route[i] == _thisAddress) + break; + i++; + while (i < numRoutes) + addRouteTo(d->route[i++], headerFrom()); + } + else if ( messageLen > 1 + && m->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE) + { + MeshRouteFailureMessage* d = (MeshRouteFailureMessage*)message->data; + deleteRouteTo(d->dest); + } +} + +//////////////////////////////////////////////////////////////////// +// This is called when a message is to be delivered to the next hop +uint8_t RHMesh::route(RoutedMessage* message, uint8_t messageLen) +{ + uint8_t from = headerFrom(); // Might get clobbered during call to superclass route() + uint8_t ret = RHRouter::route(message, messageLen); + if ( ret == RH_ROUTER_ERROR_NO_ROUTE + || ret == RH_ROUTER_ERROR_UNABLE_TO_DELIVER) + { + // Cant deliver to the next hop. Delete the route + deleteRouteTo(message->header.dest); + if (message->header.source != _thisAddress) + { + // This is being proxied, so tell the originator about it + MeshRouteFailureMessage* p = (MeshRouteFailureMessage*)&_tmpMessage; + p->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE; + p->dest = message->header.dest; // Who you were trying to deliver to + // Make sure there is a route back towards whoever sent the original message + addRouteTo(message->header.source, from); + ret = RHRouter::sendtoWait((uint8_t*)p, sizeof(RHMesh::MeshMessageHeader) + 1, message->header.source); + } + } + return ret; +} + +//////////////////////////////////////////////////////////////////// +// Subclasses may want to override +bool RHMesh::isPhysicalAddress(uint8_t* address, uint8_t addresslen) +{ + // Can only handle physical addresses 1 octet long, which is the physical node address + return addresslen == 1 && address[0] == _thisAddress; +} + +//////////////////////////////////////////////////////////////////// +bool RHMesh::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags, uint8_t* hops) +{ + uint8_t tmpMessageLen = sizeof(_tmpMessage); + uint8_t _source; + uint8_t _dest; + uint8_t _id; + uint8_t _flags; + uint8_t _hops; + if (RHRouter::recvfromAck(_tmpMessage, &tmpMessageLen, &_source, &_dest, &_id, &_flags, &_hops)) + { + MeshMessageHeader* p = (MeshMessageHeader*)&_tmpMessage; + + if ( tmpMessageLen >= 1 + && p->msgType == RH_MESH_MESSAGE_TYPE_APPLICATION) + { + MeshApplicationMessage* a = (MeshApplicationMessage*)p; + // Handle application layer messages, presumably for our caller + if (source) *source = _source; + if (dest) *dest = _dest; + if (id) *id = _id; + if (flags) *flags = _flags; + if (hops) *hops = _hops; + uint8_t msgLen = tmpMessageLen - sizeof(MeshMessageHeader); + if (*len > msgLen) + *len = msgLen; + memcpy(buf, a->data, *len); + + return true; + } + else if ( _dest == RH_BROADCAST_ADDRESS + && tmpMessageLen > 1 + && p->msgType == RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST) + { + MeshRouteDiscoveryMessage* d = (MeshRouteDiscoveryMessage*)p; + // Handle Route discovery requests + // Message is an array of node addresses the route request has already passed through + // If it originally came from us, ignore it + if (_source == _thisAddress) + return false; + + uint8_t numRoutes = tmpMessageLen - sizeof(MeshMessageHeader) - 2; + uint8_t i; + // Are we already mentioned? + for (i = 0; i < numRoutes; i++) + if (d->route[i] == _thisAddress) + return false; // Already been through us. Discard + + + addRouteTo(_source, headerFrom()); // The originator needs to be added regardless of node type + + // Hasnt been past us yet, record routes back to the earlier nodes + // No need to waste memory if we are not participating in routing + if (_isa_router) + { + for (i = 0; i < numRoutes; i++) + addRouteTo(d->route[i], headerFrom()); + } + + if (isPhysicalAddress(&d->dest, d->destlen)) + { + // This route discovery is for us. Unicast the whole route back to the originator + // as a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE + // We are certain to have a route there, because we just got it + d->header.msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE; + RHRouter::sendtoWait((uint8_t*)d, tmpMessageLen, _source); + } + else if ((i < _max_hops) && _isa_router) + { + // Its for someone else, rebroadcast it, after adding ourselves to the list + d->route[numRoutes] = _thisAddress; + tmpMessageLen++; + // Have to impersonate the source + // REVISIT: if this fails what can we do? + RHRouter::sendtoFromSourceWait(_tmpMessage, tmpMessageLen, RH_BROADCAST_ADDRESS, _source); + } + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +bool RHMesh::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags, uint8_t* hops) +{ + unsigned long starttime = millis(); + int32_t timeLeft; + while ((timeLeft = timeout - (millis() - starttime)) > 0) + { + if (waitAvailableTimeout(timeLeft)) + { + if (recvfromAck(buf, len, from, to, id, flags, hops)) + return true; + YIELD; + } + } + return false; +} + + + diff --git a/RHMesh.h b/RHMesh.h new file mode 100644 index 0000000..9ab6c83 --- /dev/null +++ b/RHMesh.h @@ -0,0 +1,264 @@ +// RHMesh.h +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHMesh.h,v 1.16 2018/09/23 23:54:01 mikem Exp $ + +#ifndef RHMesh_h +#define RHMesh_h + +#include + +// Types of RHMesh message, used to set msgType in the RHMeshHeader +#define RH_MESH_MESSAGE_TYPE_APPLICATION 0 +#define RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST 1 +#define RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE 2 +#define RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE 3 + +// Timeout for address resolution in milliecs +#define RH_MESH_ARP_TIMEOUT 4000 + +///////////////////////////////////////////////////////////////////// +/// \class RHMesh RHMesh.h +/// \brief RHRouter subclass for sending addressed, optionally acknowledged datagrams +/// multi-hop routed across a network, with automatic route discovery +/// +/// Manager class that extends RHRouter to add automatic route discovery within a mesh of adjacent nodes, +/// and route signalling. +/// +/// Unlike RHRouter, RHMesh can be used in networks where the network topology is fluid, or unknown, +/// or if nodes can mode around or go in or out of service. When a node wants to send a +/// message to another node, it will automatically discover a route to the destination node and use it. +/// If the route becomes unavailable, a new route will be discovered. +/// +/// \par Route Discovery +/// +/// When a RHMesh mesh node is initialised, it doe not know any routes to any other nodes +/// (see RHRouter for details on route and the routing table). +/// When you attempt to send a message with sendtoWait, will first check to see if there is a route to the +/// destinastion node in the routing tabl;e. If not, it wil initialite 'Route Discovery'. +/// When a node needs to discover a route to another node, it broadcasts MeshRouteDiscoveryMessage +/// with a message type of RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST. +/// Any node that receives such a request checks to see if it is a request for a route to itself +/// (in which case it makes a unicast reply to the originating node with a +/// MeshRouteDiscoveryMessage +/// with a message type of RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE) +/// otherwise it rebroadcasts the request, after adding itself to the list of nodes visited so +/// far by the request. +/// +/// If a node receives a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST that already has itself +/// listed in the visited nodes, it knows it has already seen and rebroadcast this request, +/// and threfore ignores it. This prevents broadcast storms. +/// When a node receives a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST it can use the list of +/// nodes aready visited to deduce routes back towards the originating (requesting node). +/// This also means that when the destination node of the request is reached, it (and all +/// the previous nodes the request visited) will have a route back to the originating node. +/// This means the unicast RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE +/// reply will be routed successfully back to the original route requester. +/// +/// The RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE sent back by the destination node contains +/// the full list of nodes that were visited on the way to the destination. +/// Therefore, intermediate nodes that route the reply back towards the originating node can use the +/// node list in the reply to deduce routes to all the nodes between it and the destination node. +/// +/// Therefore, RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST and +/// RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE together ensure the original requester and all +/// the intermediate nodes know how to route to the source and destination nodes and every node along the path. +/// +/// Note that there is a race condition here that can effect routing on multipath routes. For example, +/// if the route to the destination can traverse several paths, last reply from the destination +/// will be the one used. +/// +/// \par Route Failure +/// +/// RHRouter (and therefore RHMesh) use reliable hop-to-hop delivery of messages using +/// hop-to-hop acknowledgements, but not end-to-end acknowledgements. When sendtoWait() returns, +/// you know that the message has been delivered to the next hop, but not if it is (or even if it can be) +/// delivered to the destination node. If during the course of hop-to-hop routing of a message, +/// one of the intermediate RHMesh nodes finds it cannot deliver to the next hop +/// (say due to a lost route or no acknwledgement from the next hop), it replies to the +/// originator with a unicast MeshRouteFailureMessage RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE message. +/// Intermediate nodes (on the way beack to the originator) +/// and the originating node use this message to delete the route to the destination +/// node of the original message. This means that if a route to a destination becomes unusable +/// (either because an intermediate node is off the air, or has moved out of range) a new route +/// will be established the next time a message is to be sent. +/// +/// \par Message Format +/// +/// RHMesh uses a number of message formats layered on top of RHRouter: +/// - MeshApplicationMessage (message type RH_MESH_MESSAGE_TYPE_APPLICATION). +/// Carries an application layer message for the caller of RHMesh +/// - MeshRouteDiscoveryMessage (message types RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST +/// and RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_RESPONSE). Carries Route Discovery messages +/// (broadcast) and replies (unicast). +/// - MeshRouteFailureMessage (message type RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE) Informs nodes of +/// route failures. +/// +/// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers +/// (see http://www.hoperf.com) +/// +/// \par Memory +/// +/// RHMesh programs require significant amount of SRAM, often approaching 2kbytes, +/// which is beyond or at the limits of some Arduinos and other processors. Programs +/// with additional software besides basic RHMesh programs may well require even more. If you have insufficient +/// SRAM for your program, it may result in failure to run, or wierd crashes and other hard to trace behaviour. +/// In this event you should consider a processor with more SRAM, such as the MotienoMEGA with 16k +/// (https://lowpowerlab.com/shop/moteinomega) or others. +/// +/// \par Performance +/// This class (in the interests of simple implemtenation and low memory use) does not have +/// message queueing. This means that only one message at a time can be handled. Message transmission +/// failures can have a severe impact on network performance. +/// If you need high performance mesh networking under all conditions consider XBee or similar. +class RHMesh : public RHRouter +{ +public: + + /// The maximum length permitted for the application payload data in a RHMesh message + #define RH_MESH_MAX_MESSAGE_LEN (RH_ROUTER_MAX_MESSAGE_LEN - sizeof(RHMesh::MeshMessageHeader)) + + /// Structure of the basic RHMesh header. + typedef struct + { + uint8_t msgType; ///< Type of RHMesh message, one of RH_MESH_MESSAGE_TYPE_* + } MeshMessageHeader; + + /// Signals an application layer message for the caller of RHMesh + typedef struct + { + MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_APPLICATION + uint8_t data[RH_MESH_MAX_MESSAGE_LEN]; ///< Application layer payload data + } MeshApplicationMessage; + + /// Signals a route discovery request or reply (At present only supports physical dest addresses of length 1 octet) + typedef struct + { + MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_* + uint8_t destlen; ///< Reserved. Must be 1 + uint8_t dest; ///< The address of the destination node whose route is being sought + uint8_t route[RH_MESH_MAX_MESSAGE_LEN - 2]; ///< List of node addresses visited so far. Length is implcit + } MeshRouteDiscoveryMessage; + + /// Signals a route failure + typedef struct + { + MeshMessageHeader header; ///< msgType = RH_MESH_MESSAGE_TYPE_ROUTE_FAILURE + uint8_t dest; ///< The address of the destination towards which the route failed + } MeshRouteFailureMessage; + + /// Constructor. + /// \param[in] driver The RadioHead driver to use to transport messages. + /// \param[in] thisAddress The address to assign to this node. Defaults to 0 + RHMesh(RHGenericDriver& driver, uint8_t thisAddress = 0); + + /// Sends a message to the destination node. Initialises the RHRouter message header + /// (the SOURCE address is set to the address of this node, HOPS to 0) and calls + /// route() which looks up in the routing table the next hop to deliver to. + /// If no route is known, initiates route discovery and waits for a reply. + /// Then sends the message to the next hop + /// Then waits for an acknowledgement from the next hop + /// (but not from the destination node (if that is different). + /// \param [in] buf The application message data + /// \param [in] len Number of octets in the application message data. 0 is permitted + /// \param [in] dest The destination node address. If the address is RH_BROADCAST_ADDRESS (255) + /// the message will be broadcast to all the nearby nodes, but not routed or relayed. + /// \param [in] flags Optional flags for use by subclasses or application layer, + /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck(). + /// \return The result code: + /// - RH_ROUTER_ERROR_NONE Message was routed and delivered to the next hop + /// (not necessarily to the final dest address) + /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table + /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Not able to deliver to the next hop + /// (usually because it dod not acknowledge due to being off the air or out of range + uint8_t sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags = 0); + + /// Starts the receiver if it is not running already, processes and possibly routes any received messages + /// addressed to other nodes + /// and delivers any messages addressed to this node. + /// If there is a valid application layer message available for this node (or RH_BROADCAST_ADDRESS), + /// send an acknowledgement to the last hop + /// address (blocking until this is complete), then copy the application message payload data + /// to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length.. + /// If from is not NULL, the originator SOURCE address is placed in *source. + /// If to is not NULL, the DEST address is placed in *dest. This might be this nodes address or + /// RH_BROADCAST_ADDRESS. + /// This is the preferred function for getting messages addressed to this node. + /// If the message is not a broadcast, acknowledge to the sender before returning. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address + /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// \param[in] hops If present and not NULL, the referenced uint8_t will be set to the HOPS + /// (not just those addressed to this node). + /// \return true if a valid message was received for this node and copied to buf + bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL, uint8_t* hops = NULL); + + /// Starts the receiver if it is not running already. + /// Similar to recvfromAck(), this will block until either a valid application layer + /// message available for this node + /// or the timeout expires. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] timeout Maximum time to wait in milliseconds + /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address + /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// \param[in] hops If present and not NULL, the referenced uint8_t will be set to the HOPS + /// (not just those addressed to this node). + /// \return true if a valid message was copied to buf + bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL, uint8_t* hops = NULL); + +protected: + + /// Internal function that inspects messages being received and adjusts the routing table if necessary. + /// Called by recvfromAck() immediately after it gets the message from RHReliableDatagram + /// \param [in] message Pointer to the RHRouter message that was received. + /// \param [in] messageLen Length of message in octets + virtual void peekAtMessage(RoutedMessage* message, uint8_t messageLen); + + /// Internal function that inspects messages being received and adjusts the routing table if necessary. + /// This is virtual, which lets subclasses override or intercept the route() function. + /// Called by sendtoWait after the message header has been filled in. + /// \param [in] message Pointer to the RHRouter message to be sent. + /// \param [in] messageLen Length of message in octets + virtual uint8_t route(RoutedMessage* message, uint8_t messageLen); + + /// Try to resolve a route for the given address. Blocks while discovering the route + /// which may take up to 4000 msec. + /// Virtual so subclasses can override. + /// \param [in] address The physical address to resolve + /// \return true if the address was resolved and added to the local routing table + virtual bool doArp(uint8_t address); + + /// Tests if the given address of length addresslen is indentical to the + /// physical address of this node. + /// RHMesh always implements physical addresses as the 1 octet address of the node + /// given by _thisAddress + /// Called by recvfromAck() to test whether a RH_MESH_MESSAGE_TYPE_ROUTE_DISCOVERY_REQUEST + /// is for this node. + /// Subclasses may want to override to implement more complicated or longer physical addresses + /// \param [in] address Address of the pyysical addres being tested + /// \param [in] addresslen Lengthof the address in bytes + /// \return true if the physical address of this node is identical to address + virtual bool isPhysicalAddress(uint8_t* address, uint8_t addresslen); + +private: + /// Temporary message buffer + static uint8_t _tmpMessage[RH_ROUTER_MAX_MESSAGE_LEN]; + +}; + +/// @example rf22_mesh_client.pde +/// @example rf22_mesh_server1.pde +/// @example rf22_mesh_server2.pde +/// @example rf22_mesh_server3.pde + +#endif + diff --git a/RHNRFSPIDriver.cpp b/RHNRFSPIDriver.cpp new file mode 100644 index 0000000..2e4f62b --- /dev/null +++ b/RHNRFSPIDriver.cpp @@ -0,0 +1,137 @@ +// RHNRFSPIDriver.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RHNRFSPIDriver.cpp,v 1.5 2020/01/05 07:02:23 mikem Exp $ + +#include + +RHNRFSPIDriver::RHNRFSPIDriver(uint8_t slaveSelectPin, RHGenericSPI& spi) + : + _spi(spi), + _slaveSelectPin(slaveSelectPin) +{ +} + +bool RHNRFSPIDriver::init() +{ + // start the SPI library with the default speeds etc: + // On Arduino Due this defaults to SPI1 on the central group of 6 SPI pins + _spi.begin(); + + // Initialise the slave select pin + // On Maple, this must be _after_ spi.begin + pinMode(_slaveSelectPin, OUTPUT); + digitalWrite(_slaveSelectPin, HIGH); + + delay(100); + return true; +} + +// Low level commands for interfacing with the device +uint8_t RHNRFSPIDriver::spiCommand(uint8_t command) +{ + uint8_t status=0; + ATOMIC_BLOCK_START; +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + status = _spi.transfer(command); +#else + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + status = _spi.transfer(command); + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); +#endif + ATOMIC_BLOCK_END; + return status; +} + +uint8_t RHNRFSPIDriver::spiRead(uint8_t reg) +{ + uint8_t val=0; + ATOMIC_BLOCK_START; +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + val = _spi.transfer2B(reg, 0); // Send the address, discard the status, The written value is ignored, reg value is read +#else + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + _spi.transfer(reg); // Send the address, discard the status + val = _spi.transfer(0); // The written value is ignored, reg value is read + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); +#endif + ATOMIC_BLOCK_END; + return val; +} + +uint8_t RHNRFSPIDriver::spiWrite(uint8_t reg, uint8_t val) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + status = _spi.transfer2B(reg, val); +#else + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + status = _spi.transfer(reg); // Send the address + _spi.transfer(val); // New value follows +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(CORE_TEENSY) + // Sigh: some devices, such as MRF89XA dont work properly on Teensy 3.1: + // At 1MHz, the clock returns low _after_ slave select goes high, which prevents SPI + // write working. This delay gixes time for the clock to return low. +delayMicroseconds(5); +#endif + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); +#endif + ATOMIC_BLOCK_END; + return status; +} + +uint8_t RHNRFSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + status = _spi.spiBurstRead(reg, dest, len); +#else + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + status = _spi.transfer(reg); // Send the start address + while (len--) + *dest++ = _spi.transfer(0); + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); +#endif + ATOMIC_BLOCK_END; + return status; +} + +uint8_t RHNRFSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; +#if (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + status = _spi.spiBurstWrite(reg, src, len); +#else + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + status = _spi.transfer(reg); // Send the start address + while (len--) + _spi.transfer(*src++); + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); +#endif + ATOMIC_BLOCK_END; + return status; +} + +void RHNRFSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin) +{ + _slaveSelectPin = slaveSelectPin; +} + +void RHNRFSPIDriver::spiUsingInterrupt(uint8_t interruptNumber) +{ + _spi.usingInterrupt(interruptNumber); +} + diff --git a/RHNRFSPIDriver.h b/RHNRFSPIDriver.h new file mode 100644 index 0000000..b93557d --- /dev/null +++ b/RHNRFSPIDriver.h @@ -0,0 +1,101 @@ +// RHNRFSPIDriver.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RHNRFSPIDriver.h,v 1.5 2017/11/06 00:04:08 mikem Exp $ + +#ifndef RHNRFSPIDriver_h +#define RHNRFSPIDriver_h + +#include +#include + +class RHGenericSPI; + +///////////////////////////////////////////////////////////////////// +/// \class RHNRFSPIDriver RHNRFSPIDriver.h +/// \brief Base class for RadioHead drivers that use the SPI bus +/// to communicate with its NRF family transport hardware. +/// +/// This class can be subclassed by Drivers that require to use the SPI bus. +/// It can be configured to use either the RHHardwareSPI class (if there is one available on the platform) +/// of the bitbanged RHSoftwareSPI class. The dfault behaviour is to use a pre-instantiated built-in RHHardwareSPI +/// interface. +/// +/// SPI bus access is protected by ATOMIC_BLOCK_START and ATOMIC_BLOCK_END, which will ensure interrupts +/// are disabled during access. +/// +/// The read and write routines use SPI conventions as used by Nordic NRF radios and otehr devices, +/// but these can be overriden +/// in subclasses if necessary. +/// +/// Application developers are not expected to instantiate this class directly: +/// it is for the use of Driver developers. +class RHNRFSPIDriver : public RHGenericDriver +{ +public: + /// Constructor + /// \param[in] slaveSelectPin The controller pin to use to select the desired SPI device. This pin will be driven LOW + /// during SPI communications with the SPI device that uis iused by this Driver. + /// \param[in] spi Reference to the SPI interface to use. The default is to use a default built-in Hardware interface. + RHNRFSPIDriver(uint8_t slaveSelectPin = SS, RHGenericSPI& spi = hardware_spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + bool init(); + + /// Sends a single command to the device + /// \param[in] command The command code to send to the device. + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiCommand(uint8_t command); + + /// Reads a single register from the SPI device + /// \param[in] reg Register number + /// \return The value of the register + uint8_t spiRead(uint8_t reg); + + /// Writes a single byte to the SPI device + /// \param[in] reg Register number + /// \param[in] val The value to write + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiWrite(uint8_t reg, uint8_t val); + + /// Reads a number of consecutive registers from the SPI device using burst read mode + /// \param[in] reg Register number of the first register + /// \param[in] dest Array to write the register values to. Must be at least len bytes + /// \param[in] len Number of bytes to read + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len); + + /// Write a number of consecutive registers using burst write mode + /// \param[in] reg Register number of the first register + /// \param[in] src Array of new register values to write. Must be at least len bytes + /// \param[in] len Number of bytes to write + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len); + + /// Set or change the pin to be used for SPI slave select. + /// This can be called at any time to change the + /// pin that will be used for slave select in subsquent SPI operations. + /// \param[in] slaveSelectPin The pin to use + void setSlaveSelectPin(uint8_t slaveSelectPin); + + /// Set the SPI interrupt number + /// If SPI transactions can occur within an interrupt, tell the low level SPI + /// interface which interrupt is used + /// \param[in] interruptNumber the interrupt number + void spiUsingInterrupt(uint8_t interruptNumber); + +protected: + /// Reference to the RHGenericSPI instance to use to trasnfer data with teh SPI device + RHGenericSPI& _spi; + + /// The pin number of the Slave Select pin that is used to select the desired device. + uint8_t _slaveSelectPin; +}; + +#endif diff --git a/RHReliableDatagram.cpp b/RHReliableDatagram.cpp new file mode 100644 index 0000000..be1bec3 --- /dev/null +++ b/RHReliableDatagram.cpp @@ -0,0 +1,209 @@ +// RHReliableDatagram.cpp +// +// Define addressed datagram +// +// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers +// (see http://www.hoperf.com) +// RHDatagram will be received only by the addressed node or all nodes within range if the +// to address is RH_BROADCAST_ADDRESS +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHReliableDatagram.cpp,v 1.18 2018/11/08 02:31:43 mikem Exp $ + +#include + +//////////////////////////////////////////////////////////////////// +// Constructors +RHReliableDatagram::RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress) + : RHDatagram(driver, thisAddress) +{ + _retransmissions = 0; + _lastSequenceNumber = 0; + _timeout = RH_DEFAULT_TIMEOUT; + _retries = RH_DEFAULT_RETRIES; + memset(_seenIds, 0, sizeof(_seenIds)); +} + +//////////////////////////////////////////////////////////////////// +// Public methods +void RHReliableDatagram::setTimeout(uint16_t timeout) +{ + _timeout = timeout; +} + +//////////////////////////////////////////////////////////////////// +void RHReliableDatagram::setRetries(uint8_t retries) +{ + _retries = retries; +} + +//////////////////////////////////////////////////////////////////// +uint8_t RHReliableDatagram::retries() +{ + return _retries; +} + +//////////////////////////////////////////////////////////////////// +bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address) +{ + // Assemble the message + uint8_t thisSequenceNumber = ++_lastSequenceNumber; + uint8_t retries = 0; + while (retries++ <= _retries) + { + setHeaderId(thisSequenceNumber); + + // Set and clear header flags depending on if this is an + // initial send or a retry. + uint8_t headerFlagsToSet = RH_FLAGS_NONE; + // Always clear the ACK flag + uint8_t headerFlagsToClear = RH_FLAGS_ACK; + if (retries == 1) { + // On an initial send, clear the RETRY flag in case + // it was previously set + headerFlagsToClear |= RH_FLAGS_RETRY; + } else { + // Not an initial send, set the RETRY flag + headerFlagsToSet = RH_FLAGS_RETRY; + } + setHeaderFlags(headerFlagsToSet, headerFlagsToClear); + + sendto(buf, len, address); + waitPacketSent(); + + // Never wait for ACKS to broadcasts: + if (address == RH_BROADCAST_ADDRESS) + return true; + + if (retries > 1) + _retransmissions++; + unsigned long thisSendTime = millis(); // Timeout does not include original transmit time + + // Compute a new timeout, random between _timeout and _timeout*2 + // This is to prevent collisions on every retransmit + // if 2 nodes try to transmit at the same time +#if (RH_PLATFORM == RH_PLATFORM_RASPI) // use standard library random(), bugs in random(min, max) + uint16_t timeout = _timeout + (_timeout * (random() & 0xFF) / 256); +#else + uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256); +#endif + int32_t timeLeft; + while ((timeLeft = timeout - (millis() - thisSendTime)) > 0) + { + if (waitAvailableTimeout(timeLeft)) + { + uint8_t from, to, id, flags; + if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message + { + // Now have a message: is it our ACK? + if ( from == address + && to == _thisAddress + && (flags & RH_FLAGS_ACK) + && (id == thisSequenceNumber)) + { + // Its the ACK we are waiting for + return true; + } + else if ( !(flags & RH_FLAGS_ACK) + && (id == _seenIds[from])) + { + // This is a request we have already received. ACK it again + acknowledge(id, from); + } + // Else discard it + } + } + // Not the one we are waiting for, maybe keep waiting until timeout exhausted + YIELD; + } + // Timeout exhausted, maybe retry + YIELD; + } + // Retries exhausted + return false; +} + +//////////////////////////////////////////////////////////////////// +bool RHReliableDatagram::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) +{ + uint8_t _from; + uint8_t _to; + uint8_t _id; + uint8_t _flags; + // Get the message before its clobbered by the ACK (shared rx and tx buffer in some drivers + if (available() && recvfrom(buf, len, &_from, &_to, &_id, &_flags)) + { + // Never ACK an ACK + if (!(_flags & RH_FLAGS_ACK)) + { + // Its a normal message not an ACK + if (_to ==_thisAddress) + { + // Its for this node and + // Its not a broadcast, so ACK it + // Acknowledge message with ACK set in flags and ID set to received ID + acknowledge(_id, _from); + } + // Filter out retried messages that we have seen before. This explicitly + // only filters out messages that are marked as retries to protect against + // the scenario where a transmitting device sends just one message and + // shuts down between transmissions. Devices that do this will report the + // the same ID each time since their internal sequence number will reset + // to zero each time the device starts up. + if ((RH_ENABLE_EXPLICIT_RETRY_DEDUP && !(_flags & RH_FLAGS_RETRY)) || _id != _seenIds[_from]) + { + if (from) *from = _from; + if (to) *to = _to; + if (id) *id = _id; + if (flags) *flags = _flags; + _seenIds[_from] = _id; + return true; + } + // Else just re-ack it and wait for a new one + } + } + // No message for us available + return false; +} + +bool RHReliableDatagram::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from, uint8_t* to, uint8_t* id, uint8_t* flags) +{ + unsigned long starttime = millis(); + int32_t timeLeft; + while ((timeLeft = timeout - (millis() - starttime)) > 0) + { + if (waitAvailableTimeout(timeLeft)) + { + if (recvfromAck(buf, len, from, to, id, flags)) + return true; + } + YIELD; + } + return false; +} + +uint32_t RHReliableDatagram::retransmissions() +{ + return _retransmissions; +} + +void RHReliableDatagram::resetRetransmissions() +{ + _retransmissions = 0; +} + +void RHReliableDatagram::acknowledge(uint8_t id, uint8_t from) +{ + setHeaderId(id); + setHeaderFlags(RH_FLAGS_ACK); + // We would prefer to send a zero length ACK, + // but if an RH_RF22 receives a 0 length message with a CRC error, it will never receive + // a 0 length message again, until its reset, which makes everything hang :-( + // So we send an ACK of 1 octet + // REVISIT: should we send the RSSI for the information of the sender? + uint8_t ack = '!'; + sendto(&ack, sizeof(ack), from); + waitPacketSent(); +} + diff --git a/RHReliableDatagram.h b/RHReliableDatagram.h new file mode 100644 index 0000000..d0e3e14 --- /dev/null +++ b/RHReliableDatagram.h @@ -0,0 +1,221 @@ +// RHReliableDatagram.h +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHReliableDatagram.h,v 1.19 2019/07/14 00:18:48 mikem Exp $ + +#ifndef RHReliableDatagram_h +#define RHReliableDatagram_h + +#include + +/// The acknowledgement bit in the header FLAGS. This indicates if the payload is for an +/// ack for a successfully received message. +#define RH_FLAGS_ACK 0x80 +/// The retry bit in the header FLAGS. This indicates that the payload is a retry for a +/// previously sent message. +#define RH_FLAGS_RETRY 0x40 + +/// This macro enables enhanced message deduplication behavior. This currently defaults +/// to 0 (off), but this may change to default to 1 (on) in future releases. Consumers who +/// want to enable this behavior should override this macro in their code and set it to 1. +/// It is most useful where a transmitter periodically wakes up and starts to transmit +/// starting again from the first sequence number. +/// +/// Enhanced deduplication: Only messages containing the retry bit in the header +/// FLAGS will be evaluated for deduplication. This ensures that only messages that are +/// genuine retries will potentially be deduped. Note that this should not be enabled +/// if you will receive messages from devices using older versions of this library that +/// do not support the RETRY header. If you do, deduping of messages will be broken. +#define RH_ENABLE_EXPLICIT_RETRY_DEDUP 0 + +/// the default retry timeout in milliseconds +#define RH_DEFAULT_TIMEOUT 200 + +/// The default number of retries +#define RH_DEFAULT_RETRIES 3 + +///////////////////////////////////////////////////////////////////// +/// \class RHReliableDatagram RHReliableDatagram.h +/// \brief RHDatagram subclass for sending addressed, acknowledged, retransmitted datagrams. +/// +/// Manager class that extends RHDatagram to define addressed, reliable datagrams with acknowledgement and retransmission. +/// Based on RHDatagram, adds flags and sequence numbers. RHReliableDatagram is reliable in the sense +/// that messages are acknowledged by the recipient, and unacknowledged messages are retransmitted until acknowledged or the +/// retries are exhausted. +/// When addressed messages are sent (by sendtoWait()), it will wait for an ack, and retransmit +/// after timeout until an ack is received or retries are exhausted. +/// When addressed messages are collected by the application (by recvfromAck()), +/// an acknowledgement is automatically sent to the sender. +/// +/// You can use RHReliableDatagram to send broadcast messages, with a TO address of RH_BROADCAST_ADDRESS, +/// however broadcasts are not acknowledged or retransmitted and are therefore NOT actually reliable. +/// +/// The retransmit timeout is randomly varied between timeout and timeout*2 to prevent collisions on all +/// retries when 2 nodes happen to start sending at the same time . +/// +/// Each new message sent by sendtoWait() has its ID incremented. +/// +/// An ack consists of a message with: +/// - TO set to the from address of the original message +/// - FROM set to this node address +/// - ID set to the ID of the original message +/// - FLAGS with the RH_FLAGS_ACK bit set +/// - 1 octet of payload containing ASCII '!' (since some drivers cannot handle 0 length payloads) +/// +/// \par Media Access Strategy +/// +/// RHReliableDatagram and the underlying drivers always transmit as soon as +/// sendtoWait() is called. RHReliableDatagram waits for an acknowledgement, +/// and if one is not received after a timeout period the message is +/// transmitted again. If no acknowledgement is received after several +/// retries, the transmissions is deemed to have failed. +/// No contention for media is detected. +/// This will be recognised as "pure ALOHA". +/// The addition of Clear Channel Assessment (CCA) is desirable and planned. +/// +/// There is no message queuing or threading in RHReliableDatagram. +/// sendtoWait() waits until an acknowledgement is received, retransmitting +/// up to (by default) 3 retries time with a default 200ms timeout. +/// During this transmit-acknowledge phase, any received message (other than the expected +/// acknowledgement) will be ignored. Your sketch will be unresponsive to new messages +/// until an acknowledgement is received or the retries are exhausted. +/// Central server-type sketches should be very cautious about their +/// retransmit strategy and configuration lest they hang for a long time +/// trying to reply to clients that are unreachable. +/// +/// Caution: if you have a radio network with a mixture of slow and fast +/// processors and ReliableDatagrams, you may be affected by race conditions +/// where the fast processor acknowledges a message before the sender is ready +/// to process the acknowledgement. Best practice is to use the same processors (and +/// radios) throughout your network. +/// +class RHReliableDatagram : public RHDatagram +{ +public: + /// Constructor. + /// \param[in] driver The RadioHead driver to use to transport messages. + /// \param[in] thisAddress The address to assign to this node. Defaults to 0 + RHReliableDatagram(RHGenericDriver& driver, uint8_t thisAddress = 0); + + /// Sets the minimum retransmit timeout. If sendtoWait is waiting for an ack + /// longer than this time (in milliseconds), + /// it will retransmit the message. Defaults to 200ms. The timeout is measured from the end of + /// transmission of the message. It must be at least longer than the the transmit + /// time of the acknowledgement (preamble+6 octets) plus the latency/poll time of the receiver. + /// For fast modulation schemes you can considerably shorten this time. + /// Caution: if you are using slow packet rates and long packets + /// you may need to change the timeout for reliable operations. + /// The actual timeout is randomly varied between timeout and timeout*2. + /// \param[in] timeout The new timeout period in milliseconds + void setTimeout(uint16_t timeout); + + /// Sets the maximum number of retries. Defaults to 3 at construction time. + /// If set to 0, each message will only ever be sent once. + /// sendtoWait will give up and return false if there is no ack received after all transmissions time out + /// and the retries count is exhausted. + /// param[in] retries The maximum number a retries. + void setRetries(uint8_t retries); + + /// Returns the currently configured maximum retries count. + /// Can be changed with setRetries(). + /// \return The currently configured maximum number of retries. + uint8_t retries(); + + /// Send the message (with retries) and waits for an ack. Returns true if an acknowledgement is received. + /// Synchronous: any message other than the desired ACK received while waiting is discarded. + /// Blocks until an ACK is received or all retries are exhausted (ie up to retries*timeout milliseconds). + /// If the destination address is the broadcast address RH_BROADCAST_ADDRESS (255), the message will + /// be sent as a broadcast, but receiving nodes do not acknowledge, and sendtoWait() returns true immediately + /// without waiting for any acknowledgements. + /// \param[in] address The address to send the message to. + /// \param[in] buf Pointer to the binary message to send + /// \param[in] len Number of octets to send + /// \return true if the message was transmitted and an acknowledgement was received. + bool sendtoWait(uint8_t* buf, uint8_t len, uint8_t address); + + /// If there is a valid message available for this node, send an acknowledgement to the SRC + /// address (blocking until this is complete), then copy the message to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length. + /// If from is not NULL, the SRC address is placed in *from. + /// If to is not NULL, the DEST address is placed in *to. + /// This is the preferred function for getting messages addressed to this node. + /// If the message is not a broadcast, acknowledge to the sender before returning. + /// You should be sure to call this function frequently enough to not miss any messages. + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address + /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// (not just those addressed to this node). + /// \return true if a valid message was copied to buf. False if + /// - 1. There was no message received and waiting to be collected, or + /// - 2. There was a message received but it was not addressed to this node, or + /// - 3. There was a correctly addressed message but it was a duplicate of an earlier correctly received message + bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL); + + /// Similar to recvfromAck(), this will block until either a valid message available for this node + /// or the timeout expires. Starts the receiver automatically. + /// You should be sure to call this function frequently enough to not miss any messages. + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] timeout Maximum time to wait in milliseconds + /// \param[in] from If present and not NULL, the referenced uint8_t will be set to the SRC address + /// \param[in] to If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// (not just those addressed to this node). + /// \return true if a valid message was copied to buf + bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* from = NULL, uint8_t* to = NULL, uint8_t* id = NULL, uint8_t* flags = NULL); + + /// Returns the number of retransmissions + /// we have had to send since starting or since the last call to resetRetransmissions(). + /// \return The number of retransmissions since initialisation. + uint32_t retransmissions(); + + /// Resets the count of the number of retransmissions + /// to 0. + void resetRetransmissions(); + +protected: + /// Send an ACK for the message id to the given from address + /// Blocks until the ACK has been sent + void acknowledge(uint8_t id, uint8_t from); + + /// Checks whether the message currently in the Rx buffer is a new message, not previously received + /// based on the from address and the sequence. If it is new, it is acknowledged and returns true + /// \return true if there is a message received and it is a new message + bool haveNewMessage(); + +private: + /// Count of retransmissions we have had to send + uint32_t _retransmissions; + + /// The last sequence number to be used + /// Defaults to 0 + uint8_t _lastSequenceNumber; + + // Retransmit timeout (milliseconds) + /// Defaults to 200 + uint16_t _timeout; + + // Retries (0 means one try only) + /// Defaults to 3 + uint8_t _retries; + + /// Array of the last seen sequence number indexed by node address that sent it + /// It is used for duplicate detection. Duplicated messages are re-acknowledged when received + /// (this is generally due to lost ACKs, causing the sender to retransmit, even though we have already + /// received that message) + uint8_t _seenIds[256]; +}; + +/// @example rf22_reliable_datagram_client.pde +/// @example rf22_reliable_datagram_server.pde + +#endif + diff --git a/RHRouter.cpp b/RHRouter.cpp new file mode 100644 index 0000000..4241425 --- /dev/null +++ b/RHRouter.cpp @@ -0,0 +1,318 @@ +// RHRouter.cpp +// +// Define addressed datagram +// +// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers +// (see http://www.hoperf.com) +// RHDatagram will be received only by the addressed node or all nodes within range if the +// to address is RH_BROADCAST_ADDRESS +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHRouter.cpp,v 1.9 2019/09/06 04:40:40 mikem Exp $ + +#include + +RHRouter::RoutedMessage RHRouter::_tmpMessage; + +//////////////////////////////////////////////////////////////////// +// Constructors +RHRouter::RHRouter(RHGenericDriver& driver, uint8_t thisAddress) + : RHReliableDatagram(driver, thisAddress) +{ + _max_hops = RH_DEFAULT_MAX_HOPS; + _isa_router = true; + clearRoutingTable(); +} + +//////////////////////////////////////////////////////////////////// +// Public methods +bool RHRouter::init() +{ + bool ret = RHReliableDatagram::init(); + if (ret) + _max_hops = RH_DEFAULT_MAX_HOPS; + return ret; +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::setMaxHops(uint8_t max_hops) +{ + _max_hops = max_hops; +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::setIsaRouter(bool isa_router) +{ + _isa_router = isa_router; +} +//////////////////////////////////////////////////////////////////// +void RHRouter::addRouteTo(uint8_t dest, uint8_t next_hop, uint8_t state) +{ + uint8_t i; + + // First look for an existing entry we can update + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + { + if (_routes[i].dest == dest) + { + _routes[i].dest = dest; + _routes[i].next_hop = next_hop; + _routes[i].state = state; + return; + } + } + + // Look for an invalid entry we can use + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + { + if (_routes[i].state == Invalid) + { + _routes[i].dest = dest; + _routes[i].next_hop = next_hop; + _routes[i].state = state; + return; + } + } + + // Need to make room for a new one + retireOldestRoute(); + // Should be an invalid slot now + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + { + if (_routes[i].state == Invalid) + { + _routes[i].dest = dest; + _routes[i].next_hop = next_hop; + _routes[i].state = state; + } + } +} + +//////////////////////////////////////////////////////////////////// +RHRouter::RoutingTableEntry* RHRouter::getRouteTo(uint8_t dest) +{ + uint8_t i; + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + if (_routes[i].dest == dest && _routes[i].state != Invalid) + return &_routes[i]; + return NULL; +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::deleteRoute(uint8_t index) +{ + // Delete a route by copying following routes on top of it + memcpy(&_routes[index], &_routes[index+1], + sizeof(RoutingTableEntry) * (RH_ROUTING_TABLE_SIZE - index - 1)); + _routes[RH_ROUTING_TABLE_SIZE - 1].state = Invalid; +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::printRoutingTable() +{ +#ifdef RH_HAVE_SERIAL + uint8_t i; + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + { + Serial.print(i, DEC); + Serial.print(" Dest: "); + Serial.print(_routes[i].dest, DEC); + Serial.print(" Next Hop: "); + Serial.print(_routes[i].next_hop, DEC); + Serial.print(" State: "); + Serial.println(_routes[i].state, DEC); + } +#endif +} + +//////////////////////////////////////////////////////////////////// +bool RHRouter::deleteRouteTo(uint8_t dest) +{ + uint8_t i; + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + { + if (_routes[i].dest == dest) + { + deleteRoute(i); + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::retireOldestRoute() +{ + // We just obliterate the first in the table and clear the last + deleteRoute(0); +} + +//////////////////////////////////////////////////////////////////// +void RHRouter::clearRoutingTable() +{ + uint8_t i; + for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++) + _routes[i].state = Invalid; +} + + +uint8_t RHRouter::sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags) +{ + return sendtoFromSourceWait(buf, len, dest, _thisAddress, flags); +} + +//////////////////////////////////////////////////////////////////// +// Waits for delivery to the next hop (but not for delivery to the final destination) +uint8_t RHRouter::sendtoFromSourceWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t source, uint8_t flags) +{ + if (((uint16_t)len + sizeof(RoutedMessageHeader)) > _driver.maxMessageLength()) + return RH_ROUTER_ERROR_INVALID_LENGTH; + + // Construct a RH RouterMessage message + _tmpMessage.header.source = source; + _tmpMessage.header.dest = dest; + _tmpMessage.header.hops = 0; + _tmpMessage.header.id = _lastE2ESequenceNumber++; + _tmpMessage.header.flags = flags; + memcpy(_tmpMessage.data, buf, len); + + return route(&_tmpMessage, sizeof(RoutedMessageHeader)+len); +} + +//////////////////////////////////////////////////////////////////// +uint8_t RHRouter::route(RoutedMessage* message, uint8_t messageLen) +{ + // Reliably deliver it if possible. See if we have a route: + uint8_t next_hop = RH_BROADCAST_ADDRESS; + if (message->header.dest != RH_BROADCAST_ADDRESS) + { + RoutingTableEntry* route = getRouteTo(message->header.dest); + if (!route) + return RH_ROUTER_ERROR_NO_ROUTE; + next_hop = route->next_hop; + } + + if (!RHReliableDatagram::sendtoWait((uint8_t*)message, messageLen, next_hop)) + return RH_ROUTER_ERROR_UNABLE_TO_DELIVER; + + return RH_ROUTER_ERROR_NONE; +} + +//////////////////////////////////////////////////////////////////// +// Subclasses may want to override this to peek at messages going past +void RHRouter::peekAtMessage(RoutedMessage* message, uint8_t messageLen) +{ + // Default does nothing + (void)message; // Not used + (void)messageLen; // Not used +} + +//////////////////////////////////////////////////////////////////// +bool RHRouter::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags, uint8_t* hops) +{ + uint8_t tmpMessageLen = sizeof(_tmpMessage); + uint8_t _from; + uint8_t _to; + uint8_t _id; + uint8_t _flags; + if (RHReliableDatagram::recvfromAck((uint8_t*)&_tmpMessage, &tmpMessageLen, &_from, &_to, &_id, &_flags)) + { + // Here we simulate networks with limited visibility between nodes + // so we can test routing +#ifdef RH_TEST_NETWORK + if ( +#if RH_TEST_NETWORK==1 + // This network looks like 1-2-3-4 + (_thisAddress == 1 && _from == 2) + || (_thisAddress == 2 && (_from == 1 || _from == 3)) + || (_thisAddress == 3 && (_from == 2 || _from == 4)) + || (_thisAddress == 4 && _from == 3) + +#elif RH_TEST_NETWORK==2 + // This network looks like 1-2-4 + // | | | + // --3-- + (_thisAddress == 1 && (_from == 2 || _from == 3)) + || _thisAddress == 2 + || _thisAddress == 3 + || (_thisAddress == 4 && (_from == 2 || _from == 3)) + +#elif RH_TEST_NETWORK==3 + // This network looks like 1-2-4 + // | | + // --3-- + (_thisAddress == 1 && (_from == 2 || _from == 3)) + || (_thisAddress == 2 && (_from == 1 || _from == 4)) + || (_thisAddress == 3 && (_from == 1 || _from == 4)) + || (_thisAddress == 4 && (_from == 2 || _from == 3)) + +#elif RH_TEST_NETWORK==4 + // This network looks like 1-2-3 + // | + // 4 + (_thisAddress == 1 && _from == 2) + || _thisAddress == 2 + || (_thisAddress == 3 && _from == 2) + || (_thisAddress == 4 && _from == 2) + +#endif + ) + { + // OK + } + else + { + return false; // Pretend we got nothing + } +#endif + + peekAtMessage(&_tmpMessage, tmpMessageLen); + // See if its for us or has to be routed + if (_tmpMessage.header.dest == _thisAddress || _tmpMessage.header.dest == RH_BROADCAST_ADDRESS) + { + // Deliver it here + if (source) *source = _tmpMessage.header.source; + if (dest) *dest = _tmpMessage.header.dest; + if (id) *id = _tmpMessage.header.id; + if (flags) *flags = _tmpMessage.header.flags; + if (hops) *hops = _tmpMessage.header.hops; + uint8_t msgLen = tmpMessageLen - sizeof(RoutedMessageHeader); + if (*len > msgLen) + *len = msgLen; + memcpy(buf, _tmpMessage.data, *len); + return true; // Its for you! + } + else if ( _tmpMessage.header.dest != RH_BROADCAST_ADDRESS + && _tmpMessage.header.hops++ < _max_hops) + { + // Maybe it has to be routed to the next hop + // REVISIT: if it fails due to no route or unable to deliver to the next hop, + // tell the originator. BUT HOW? + + // If we are forwarding packets, do so. Otherwise, drop. + if (_isa_router) + route(&_tmpMessage, tmpMessageLen); + } + // Discard it and maybe wait for another + } + return false; +} + +//////////////////////////////////////////////////////////////////// +bool RHRouter::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags, uint8_t* hops) +{ + unsigned long starttime = millis(); + int32_t timeLeft; + while ((timeLeft = timeout - (millis() - starttime)) > 0) + { + if (waitAvailableTimeout(timeLeft)) + { + if (recvfromAck(buf, len, source, dest, id, flags, hops)) + return true; + } + YIELD; + } + return false; +} + diff --git a/RHRouter.h b/RHRouter.h new file mode 100644 index 0000000..cecbb8c --- /dev/null +++ b/RHRouter.h @@ -0,0 +1,342 @@ +// RHRouter.h +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RHRouter.h,v 1.12 2020/04/09 23:40:34 mikem Exp $ + +#ifndef RHRouter_h +#define RHRouter_h + +#include + +// Default max number of hops we will route +#define RH_DEFAULT_MAX_HOPS 30 + +// The default size of the routing table we keep +#define RH_ROUTING_TABLE_SIZE 10 + +// Error codes +#define RH_ROUTER_ERROR_NONE 0 +#define RH_ROUTER_ERROR_INVALID_LENGTH 1 +#define RH_ROUTER_ERROR_NO_ROUTE 2 +#define RH_ROUTER_ERROR_TIMEOUT 3 +#define RH_ROUTER_ERROR_NO_REPLY 4 +#define RH_ROUTER_ERROR_UNABLE_TO_DELIVER 5 + +// This size of RH_ROUTER_MAX_MESSAGE_LEN is OK for Arduino Mega, but too big for +// Duemilanove. Size of 50 works with the sample router programs on Duemilanove. +#define RH_ROUTER_MAX_MESSAGE_LEN (RH_MAX_MESSAGE_LEN - sizeof(RHRouter::RoutedMessageHeader)) +//#define RH_ROUTER_MAX_MESSAGE_LEN 50 + +// These allow us to define a simulated network topology for testing purposes +// See RHRouter.cpp for details +//#define RH_TEST_NETWORK 1 +//#define RH_TEST_NETWORK 2 +//#define RH_TEST_NETWORK 3 +//#define RH_TEST_NETWORK 4 + +///////////////////////////////////////////////////////////////////// +/// \class RHRouter RHRouter.h +/// \brief RHReliableDatagram subclass for sending addressed, optionally acknowledged datagrams +/// multi-hop routed across a network. +/// +/// This is a Manager class that extends RHReliableDatagram to handle addressed messages +/// that are reliably transmitted and routed across a network of multiple RHRouter nodes. +/// Each message is transmitted reliably +/// between each hop in order to get from the source node to the destination node. +/// +/// With RHRouter, routes are hard wired. This means that each node must have programmed +/// in it how to reach each of the other nodes it will be trying to communicate with. +/// This means you must specify the next-hop node address for each of the destination nodes, +/// using the addRouteTo() function. +/// +/// When sendtoWait() is called with a new message to deliver, and the destination address, +/// RHRouter looks up the next hop node for the destination node. It then uses +/// RHReliableDatagram to (reliably) deliver the message to the next hop +/// (which is expected also to be running an RHRouter). If that next-hop node is not +/// the final destination, it will also look up the next hop for the destination node and +/// (reliably) deliver the message to the next hop. By this method, messages can be delivered +/// across a network of nodes, even if each node cannot hear all of the others in the network. +/// Each time a message is received for another node and retransmitted to the next hop, +/// the HOPS field in the header is incremented. If a message is received for routing to another node +/// which has exceed the routers max_hops, the message will be dropped and ignored. +/// This helps prevent infinite routing loops. +/// +/// RHRouter supports messages with a dest of RH_BROADCAST_ADDRESS. Such messages are not routed, +/// and are broadcast (once) to all nodes within range. +/// +/// The recvfromAck() function is responsible not just for receiving and delivering +/// messages addressed to this node (or RH_BROADCAST_ADDRESS), but +/// it is also responsible for routing other message to their next hop. This means that it is important to +/// call recvfromAck() or recvfromAckTimeout() frequently in your main loop. recvfromAck() will return +/// false if it receives a message but it is not for this node. +/// +/// RHRouter does not provide reliable end-to-end delivery, but uses reliable hop-to-hop delivery. +/// If a message is unable to be delivered to an end node during to a delivery failure between 2 hops, +/// the source node will not be told about it. +/// +/// Note: This class is most useful for networks of nodes that are essentially static +/// (i.e. the nodes dont move around), and for which the +/// routing never changes. If that is not the case for your proposed network, see RHMesh instead. +/// +/// \par The Routing Table +/// +/// The routing table is a local table in RHRouter that holds the information about the next hop node +/// address for each destination address you may want to send a message to. It is your responsibility +/// to make sure every node in an RHRouter network has been configured with a unique address and the +/// routing information so that messages are correctly routed across the network from source node to +/// destination node. This is usually done once in setup() by calling addRouteTo(). +/// The hardwired routing will in general be different on each node, and will depend on the physical +/// topololgy of the network. +/// You can also use addRouteTo() to change a route and +/// deleteRouteTo() to delete a route at run time. Youcan also clear the entire routing table +/// +/// The Routing Table has limited capacity for entries (defined by RH_ROUTING_TABLE_SIZE, which is 10) +/// if more than RH_ROUTING_TABLE_SIZE are added, the oldest (first) one will be removed by calling +/// retireOldestRoute() +/// +/// \par Message Format +/// +/// RHRouter add to the lower level RHReliableDatagram (and even lower level RH) class message formats. +/// In those lower level classes, the hop-to-hop message headers are in the RH message headers, +/// and are handled automcatically by tyhe RH hardware. +/// RHRouter and its subclasses add an end-to-end addressing header in the payload of the RH message, +/// and before the RHRouter application data. +/// - 1 octet DEST, the destination node address (ie the address of the final +/// destination node for this message) +/// - 1 octet SOURCE, the source node address (ie the address of the originating node that first sent +/// the message). +/// - 1 octet HOPS, the number of hops this message has traversed so far. +/// - 1 octet ID, an incrementing message ID for end-to-end message tracking for use by subclasses. +/// Not used by RHRouter. +/// - 1 octet FLAGS, a bitmask for use by subclasses. Not used by RHRouter. +/// - 0 or more octets DATA, the application payload data. The length of this data is implicit +/// in the length of the entire message. +/// +/// You should be careful to note that there are ID and FLAGS fields in the low level per-hop +/// message header too. These are used only for hop-to-hop, and in general will be different to +/// the ones at the RHRouter level. +/// +/// \par Testing +/// +/// Bench testing of such networks is notoriously difficult, especially simulating limited radio +/// connectivity between some nodes. +/// To assist testing (both during RH development and for your own networks) +/// RHRouter.cpp has the ability to +/// simulate a number of different small network topologies. Each simulated network supports 4 nodes with +/// addresses 1 to 4. It operates by pretending to not hear RH messages from certain other nodes. +/// You can enable testing with a \#define TEST_NETWORK in RHRouter.h +/// The sample programs rf22_mesh_* rely on this feature. +/// +/// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers +/// (see http://www.hoperf.com) +class RHRouter : public RHReliableDatagram +{ +public: + + /// Defines the structure of the RHRouter message header, used to keep track of end-to-end delivery parameters + typedef struct + { + uint8_t dest; ///< Destination node address + uint8_t source; ///< Originator node address + uint8_t hops; ///< Hops traversed so far + uint8_t id; ///< Originator sequence number + uint8_t flags; ///< Originator flags + // Data follows, Length is implicit in the overall message length + } RoutedMessageHeader; + + /// Defines the structure of a RHRouter message + typedef struct + { + RoutedMessageHeader header; ///< end-to-end delivery header + uint8_t data[RH_ROUTER_MAX_MESSAGE_LEN]; ///< Application payload data + } RoutedMessage; + + /// Values for the possible states for routes + typedef enum + { + Invalid = 0, ///< No valid route is known + Discovering, ///< Discovering a route (not currently used) + Valid ///< Route is valid + } RouteState; + + /// Defines an entry in the routing table + typedef struct + { + uint8_t dest; ///< Destination node address + uint8_t next_hop; ///< Send via this next hop address + uint8_t state; ///< State of this route, one of RouteState + } RoutingTableEntry; + + /// Constructor. + /// \param[in] driver The RadioHead driver to use to transport messages. + /// \param[in] thisAddress The address to assign to this node. Defaults to 0 + RHRouter(RHGenericDriver& driver, uint8_t thisAddress = 0); + + /// Initialises this instance and the radio module connected to it. + /// Overrides the init() function in RH. + /// Sets max_hops to the default of RH_DEFAULT_MAX_HOPS (30) + bool init(); + + + /// Sets the flag determining if the node will participate in routing. + /// if isa_router is true, the node will be a full participant. If false the node + /// will only respond to + /// packets directed to its address. The default is true. + /// \param[in] isa_router true or false + void setIsaRouter(bool isa_router); + + /// Sets the max_hops to the given value + /// This controls the maximum number of hops allowed between source and destination nodes + /// Messages that are not delivered by the time their HOPS field exceeds max_hops on a + /// routing node will be dropped and ignored. + /// \param [in] max_hops The new value for max_hops + void setMaxHops(uint8_t max_hops); + + /// Adds a route to the local routing table, or updates it if already present. + /// If there is not enough room the oldest (first) route will be deleted by calling retireOldestRoute(). + /// \param [in] dest The destination node address. RH_BROADCAST_ADDRESS is permitted. + /// \param [in] next_hop The address of the next hop to send messages destined for dest + /// \param [in] state The satte of the route. Defaults to Valid + void addRouteTo(uint8_t dest, uint8_t next_hop, uint8_t state = Valid); + + /// Finds and returns a RoutingTableEntry for the given destination node + /// \param [in] dest The desired destination node address. + /// \return pointer to a RoutingTableEntry for dest + RoutingTableEntry* getRouteTo(uint8_t dest); + + /// Deletes from the local routing table any route for the destination node. + /// \param [in] dest The destination node address + /// \return true if the route was present + bool deleteRouteTo(uint8_t dest); + + /// Deletes the oldest (first) route from the + /// local routing table + void retireOldestRoute(); + + /// Clears all entries from the + /// local routing table + void clearRoutingTable(); + + /// If RH_HAVE_SERIAL is defined, this will print out the contents of the local + /// routing table using Serial + void printRoutingTable(); + + /// Sends a message to the destination node. Initialises the RHRouter message header + /// (the SOURCE address is set to the address of this node, HOPS to 0) and calls + /// route() which looks up in the routing table the next hop to deliver to and sends the + /// message to the next hop. Waits for an acknowledgement from the next hop + /// (but not from the destination node (if that is different). + /// \param [in] buf The application message data + /// \param [in] len Number of octets in the application message data. 0 is permitted + /// \param [in] dest The destination node address + /// \param [in] flags Optional flags for use by subclasses or application layer, + /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck(). + /// \return The result code: + /// - RH_ROUTER_ERROR_NONE Message was routed and delivered to the next hop + /// (not necessarily to the final dest address) + /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table + /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Not able to deliver to the next hop + /// (usually because it dod not acknowledge due to being off the air or out of range + uint8_t sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags = 0); + + /// Similar to sendtoWait() above, but spoofs the source address. + /// For internal use only during routing + /// \param [in] buf The application message data. + /// \param [in] len Number of octets in the application message data. 0 is permitted. + /// \param [in] dest The destination node address. + /// \param [in] source The (fake) originating node address. + /// \param [in] flags Optional flags for use by subclasses or application layer, + /// delivered end-to-end to the dest address. The receiver can recover the flags with recvFromAck(). + /// \return The result code: + /// - RH_ROUTER_ERROR_NONE Message was routed and deliverd to the next hop + /// (not necessarily to the final dest address) + /// - RH_ROUTER_ERROR_NO_ROUTE There was no route for dest in the local routing table + /// - RH_ROUTER_ERROR_UNABLE_TO_DELIVER Noyt able to deliver to the next hop + /// (usually because it dod not acknowledge due to being off the air or out of range + uint8_t sendtoFromSourceWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t source, uint8_t flags = 0); + + /// Starts the receiver if it is not running already. + /// If there is a valid message available for this node (or RH_BROADCAST_ADDRESS), + /// send an acknowledgement to the last hop + /// address (blocking until this is complete), then copy the application message payload data + /// to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length.. + /// If from is not NULL, the originator SOURCE address is placed in *source. + /// If to is not NULL, the DEST address is placed in *dest. This might be this nodes address or + /// RH_BROADCAST_ADDRESS. + /// This is the preferred function for getting messages addressed to this node. + /// If the message is not a broadcast, acknowledge to the sender before returning. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address + /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// \param[in] hops If present and not NULL, the referenced uint8_t will be set to the HOPS + /// (not just those addressed to this node). + /// \return true if a valid message was recvived for this node copied to buf + bool recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL, uint8_t* hops = NULL); + + /// Starts the receiver if it is not running already. + /// Similar to recvfromAck(), this will block until either a valid message available for this node + /// or the timeout expires. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Available space in buf. Set to the actual number of octets copied. + /// \param[in] timeout Maximum time to wait in milliseconds + /// \param[in] source If present and not NULL, the referenced uint8_t will be set to the SOURCE address + /// \param[in] dest If present and not NULL, the referenced uint8_t will be set to the DEST address + /// \param[in] id If present and not NULL, the referenced uint8_t will be set to the ID + /// \param[in] flags If present and not NULL, the referenced uint8_t will be set to the FLAGS + /// \param[in] hops If present and not NULL, the referenced uint8_t will be set to the HOPS + /// (not just those addressed to this node). + /// \return true if a valid message was copied to buf + bool recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source = NULL, uint8_t* dest = NULL, uint8_t* id = NULL, uint8_t* flags = NULL, uint8_t* hops = NULL); + +protected: + + /// Lets sublasses peek at messages going + /// past before routing or local delivery. + /// Called by recvfromAck() immediately after it gets the message from RHReliableDatagram + /// \param [in] message Pointer to the RHRouter message that was received. + /// \param [in] messageLen Length of message in octets + virtual void peekAtMessage(RoutedMessage* message, uint8_t messageLen); + + /// Finds the next-hop route and sends the message via RHReliableDatagram::sendtoWait(). + /// This is virtual, which lets subclasses override or intercept the route() function. + /// Called by sendtoWait after the message header has been filled in. + /// \param [in] message Pointer to the RHRouter message to be sent. + /// \param [in] messageLen Length of message in octets + virtual uint8_t route(RoutedMessage* message, uint8_t messageLen); + + /// Deletes a specific rout entry from therouting table + /// \param [in] index The 0 based index of the routing table entry to delete + void deleteRoute(uint8_t index); + + /// The last end-to-end sequence number to be used + /// Defaults to 0 + uint8_t _lastE2ESequenceNumber; + + /// The maximum number of hops permitted in routed messages. + /// If a routed message would exceed this number of hops it is dropped and ignored. + uint8_t _max_hops; + + /// Flag to set if packets are forwarded or not + bool _isa_router; + +private: + + /// Temporary mesage buffer + static RoutedMessage _tmpMessage; + + /// Local routing table + RoutingTableEntry _routes[RH_ROUTING_TABLE_SIZE]; +}; + +/// @example rf22_router_client.pde +/// @example rf22_router_server1.pde +/// @example rf22_router_server2.pde +/// @example rf22_router_server3.pde +#endif + diff --git a/RHSPIDriver.cpp b/RHSPIDriver.cpp new file mode 100644 index 0000000..ff3ec87 --- /dev/null +++ b/RHSPIDriver.cpp @@ -0,0 +1,120 @@ +// RHSPIDriver.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RHSPIDriver.cpp,v 1.12 2020/06/15 23:39:39 mikem Exp $ + +#include + +// Some platforms may need special slave select driving + +RHSPIDriver::RHSPIDriver(uint8_t slaveSelectPin, RHGenericSPI& spi) + : + _spi(spi), + _slaveSelectPin(slaveSelectPin) +{ +} + +bool RHSPIDriver::init() +{ + // start the SPI library with the default speeds etc: + // On Arduino Due this defaults to SPI1 on the central group of 6 SPI pins + _spi.begin(); + + // Initialise the slave select pin + // On Maple, this must be _after_ spi.begin + + // Sometimes we dont want to work the _slaveSelectPin here + if (_slaveSelectPin != 0xff) + pinMode(_slaveSelectPin, OUTPUT); + + deselectSlave(); + + // This delay is needed for ATMega and maybe some others, but + // 100ms is too long for STM32L0, and somehow can cause the USB interface to fail + // in some versions of the core. +#if (RH_PLATFORM == RH_PLATFORM_STM32L0) && (defined STM32L082xx || defined STM32L072xx) + delay(10); +#else + delay(100); +#endif + + return true; +} + +uint8_t RHSPIDriver::spiRead(uint8_t reg) +{ + uint8_t val=0; + ATOMIC_BLOCK_START; + _spi.beginTransaction(); + selectSlave(); + _spi.transfer(reg & ~RH_SPI_WRITE_MASK); // Send the address with the write mask off + val = _spi.transfer(0); // The written value is ignored, reg value is read + deselectSlave(); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return val; +} + +uint8_t RHSPIDriver::spiWrite(uint8_t reg, uint8_t val) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; + _spi.beginTransaction(); + selectSlave(); + status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the address with the write mask on + _spi.transfer(val); // New value follows + deselectSlave(); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return status; +} + +uint8_t RHSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; + _spi.beginTransaction(); + selectSlave(); + status = _spi.transfer(reg & ~RH_SPI_WRITE_MASK); // Send the start address with the write mask off + while (len--) + *dest++ = _spi.transfer(0); + deselectSlave(); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return status; +} + +uint8_t RHSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len) +{ + uint8_t status = 0; + ATOMIC_BLOCK_START; + _spi.beginTransaction(); + selectSlave(); + status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the start address with the write mask on + while (len--) + _spi.transfer(*src++); + deselectSlave(); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return status; +} + +void RHSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin) +{ + _slaveSelectPin = slaveSelectPin; +} + +void RHSPIDriver::spiUsingInterrupt(uint8_t interruptNumber) +{ + _spi.usingInterrupt(interruptNumber); +} + +void RHSPIDriver::selectSlave() +{ + digitalWrite(_slaveSelectPin, LOW); +} + +void RHSPIDriver::deselectSlave() +{ + digitalWrite(_slaveSelectPin, HIGH); +} diff --git a/RHSPIDriver.h b/RHSPIDriver.h new file mode 100644 index 0000000..42ddead --- /dev/null +++ b/RHSPIDriver.h @@ -0,0 +1,109 @@ +// RHSPIDriver.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RHSPIDriver.h,v 1.16 2020/06/15 23:39:39 mikem Exp $ + +#ifndef RHSPIDriver_h +#define RHSPIDriver_h + +#include +#include + +// This is the bit in the SPI address that marks it as a write +#define RH_SPI_WRITE_MASK 0x80 + +class RHGenericSPI; + +///////////////////////////////////////////////////////////////////// +/// \class RHSPIDriver RHSPIDriver.h +/// \brief Base class for RadioHead drivers that use the SPI bus +/// to communicate with its transport hardware. +/// +/// This class can be subclassed by Drivers that require to use the SPI bus. +/// It can be configured to use either the RHHardwareSPI class (if there is one available on the platform) +/// of the bitbanged RHSoftwareSPI class. The default behaviour is to use a pre-instantiated built-in RHHardwareSPI +/// interface. +/// +/// SPI bus access is protected by ATOMIC_BLOCK_START and ATOMIC_BLOCK_END, which will ensure interrupts +/// are disabled during access. +/// +/// The read and write routines implement commonly used SPI conventions: specifically that the MSB +/// of the first byte transmitted indicates that it is a write and the remaining bits indicate the rehgister to access) +/// This can be overriden +/// in subclasses if necessaryor an alternative class, RHNRFSPIDriver can be used to access devices like +/// Nordic NRF series radios, which have different requirements. +/// +/// Application developers are not expected to instantiate this class directly: +/// it is for the use of Driver developers. +class RHSPIDriver : public RHGenericDriver +{ +public: + /// Constructor + /// \param[in] slaveSelectPin The controler pin to use to select the desired SPI device. This pin will be driven LOW + /// during SPI communications with the SPI device that uis iused by this Driver. + /// \param[in] spi Reference to the SPI interface to use. The default is to use a default built-in Hardware interface. + RHSPIDriver(uint8_t slaveSelectPin = SS, RHGenericSPI& spi = hardware_spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + bool init(); + + /// Reads a single register from the SPI device + /// \param[in] reg Register number + /// \return The value of the register + uint8_t spiRead(uint8_t reg); + + /// Writes a single byte to the SPI device + /// \param[in] reg Register number + /// \param[in] val The value to write + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiWrite(uint8_t reg, uint8_t val); + + /// Reads a number of consecutive registers from the SPI device using burst read mode + /// \param[in] reg Register number of the first register + /// \param[in] dest Array to write the register values to. Must be at least len bytes + /// \param[in] len Number of bytes to read + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len); + + /// Write a number of consecutive registers using burst write mode + /// \param[in] reg Register number of the first register + /// \param[in] src Array of new register values to write. Must be at least len bytes + /// \param[in] len Number of bytes to write + /// \return Some devices return a status byte during the first data transfer. This byte is returned. + /// it may or may not be meaningfule depending on the the type of device being accessed. + uint8_t spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len); + + /// Set or change the pin to be used for SPI slave select. + /// This can be called at any time to change the + /// pin that will be used for slave select in subsquent SPI operations. + /// \param[in] slaveSelectPin The pin to use + void setSlaveSelectPin(uint8_t slaveSelectPin); + + /// Set the SPI interrupt number + /// If SPI transactions can occur within an interrupt, tell the low level SPI + /// interface which interrupt is used + /// \param[in] interruptNumber the interrupt number + void spiUsingInterrupt(uint8_t interruptNumber); + + protected: + + // Override this if you need an unusual way of selecting the slave before SPI transactions + // The default uses digitalWrite(_slaveSelectPin, LOW) + virtual void selectSlave(); + + // Override this if you need an unusual way of selecting the slave before SPI transactions + // The default uses digitalWrite(_slaveSelectPin, HIGH) + virtual void deselectSlave(); + + /// Reference to the RHGenericSPI instance to use to transfer data with the SPI device + RHGenericSPI& _spi; + + /// The pin number of the Slave Select pin that is used to select the desired device. + uint8_t _slaveSelectPin; +}; + +#endif diff --git a/RHSoftwareSPI.cpp b/RHSoftwareSPI.cpp new file mode 100644 index 0000000..f1959cb --- /dev/null +++ b/RHSoftwareSPI.cpp @@ -0,0 +1,166 @@ +// SoftwareSPI.cpp +// Author: Chris Lapa (chris@lapa.com.au) +// Copyright (C) 2014 Chris Lapa +// Contributed by Chris Lapa + +#include + +RHSoftwareSPI::RHSoftwareSPI(Frequency frequency, BitOrder bitOrder, DataMode dataMode) + : + RHGenericSPI(frequency, bitOrder, dataMode) +{ + setPins(12, 11, 13); +} + +// Caution: on Arduino Uno and many other CPUs, digitalWrite is quite slow, taking about 4us +// digitalWrite is also slow, taking about 3.5us +// resulting in very slow SPI bus speeds using this technique, up to about 120us per octet of transfer +uint8_t RHSoftwareSPI::transfer(uint8_t data) +{ + uint8_t readData; + uint8_t writeData; + uint8_t builtReturn; + uint8_t mask; + + if (_bitOrder == BitOrderMSBFirst) + { + mask = 0x80; + } + else + { + mask = 0x01; + } + builtReturn = 0; + readData = 0; + + for (uint8_t count=0; count<8; count++) + { + if (data & mask) + { + writeData = HIGH; + } + else + { + writeData = LOW; + } + + if (_clockPhase == 1) + { + // CPHA=1, miso/mosi changing state now + digitalWrite(_mosi, writeData); + digitalWrite(_sck, ~_clockPolarity); + delayPeriod(); + + // CPHA=1, miso/mosi stable now + readData = digitalRead(_miso); + digitalWrite(_sck, _clockPolarity); + delayPeriod(); + } + else + { + // CPHA=0, miso/mosi changing state now + digitalWrite(_mosi, writeData); + digitalWrite(_sck, _clockPolarity); + delayPeriod(); + + // CPHA=0, miso/mosi stable now + readData = digitalRead(_miso); + digitalWrite(_sck, ~_clockPolarity); + delayPeriod(); + } + + if (_bitOrder == BitOrderMSBFirst) + { + mask >>= 1; + builtReturn |= (readData << (7 - count)); + } + else + { + mask <<= 1; + builtReturn |= (readData << count); + } + } + + digitalWrite(_sck, _clockPolarity); + + return builtReturn; +} + +/// Initialise the SPI library +void RHSoftwareSPI::begin() +{ + if (_dataMode == DataMode0 || + _dataMode == DataMode1) + { + _clockPolarity = LOW; + } + else + { + _clockPolarity = HIGH; + } + + if (_dataMode == DataMode0 || + _dataMode == DataMode2) + { + _clockPhase = 0; + } + else + { + _clockPhase = 1; + } + digitalWrite(_sck, _clockPolarity); + + // Caution: these counts assume that digitalWrite is very fast, which is usually not true + switch (_frequency) + { + case Frequency1MHz: + _delayCounts = 8; + break; + + case Frequency2MHz: + _delayCounts = 4; + break; + + case Frequency4MHz: + _delayCounts = 2; + break; + + case Frequency8MHz: + _delayCounts = 1; + break; + + case Frequency16MHz: + _delayCounts = 0; + break; + } +} + +/// Disables the SPI bus usually, in this case +/// there is no hardware controller to disable. +void RHSoftwareSPI::end() { } + +/// Sets the pins used by this SoftwareSPIClass instance. +/// \param[in] miso master in slave out pin used +/// \param[in] mosi master out slave in pin used +/// \param[in] sck clock pin used +void RHSoftwareSPI::setPins(uint8_t miso, uint8_t mosi, uint8_t sck) +{ + _miso = miso; + _mosi = mosi; + _sck = sck; + + pinMode(_miso, INPUT); + pinMode(_mosi, OUTPUT); + pinMode(_sck, OUTPUT); + digitalWrite(_sck, _clockPolarity); +} + + +void RHSoftwareSPI::delayPeriod() +{ + for (uint8_t count = 0; count < _delayCounts; count++) + { + __asm__ __volatile__ ("nop"); + } +} + diff --git a/RHSoftwareSPI.h b/RHSoftwareSPI.h new file mode 100644 index 0000000..5e7e1a5 --- /dev/null +++ b/RHSoftwareSPI.h @@ -0,0 +1,90 @@ +// SoftwareSPI.h +// Author: Chris Lapa (chris@lapa.com.au) +// Copyright (C) 2014 Chris Lapa +// Contributed by Chris Lapa + +#ifndef RHSoftwareSPI_h +#define RHSoftwareSPI_h + +#include + +///////////////////////////////////////////////////////////////////// +/// \class RHSoftwareSPI RHSoftwareSPI.h +/// \brief Encapsulate a software SPI interface +/// +/// This concrete subclass of RHGenericSPI enapsulates a bit-banged software SPI interface. +/// Caution: this software SPI interface will be much slower than hardware SPI on most +/// platforms. +/// +/// SPI transactions are not supported, and associated functions do nothing. +/// +/// \par Usage +/// +/// Usage varies slightly depending on what driver you are using. +/// +/// For RF22, for example: +/// \code +/// #include +/// RHSoftwareSPI spi; +/// RH_RF22 driver(SS, 2, spi); +/// RHReliableDatagram(driver, CLIENT_ADDRESS); +/// void setup() +/// { +/// spi.setPins(6, 5, 7); // Or whatever SPI pins you need +/// .... +/// } +/// \endcode +class RHSoftwareSPI : public RHGenericSPI +{ +public: + + /// Constructor + /// Creates an instance of a bit-banged software SPI interface. + /// Sets the SPI pins to the defaults of + /// MISO = 12, MOSI = 11, SCK = 13. If you need other assigments, call setPins() before + /// calling manager.init() or driver.init(). + /// \param[in] frequency One of RHGenericSPI::Frequency to select the SPI bus frequency. The frequency + /// is mapped to the closest available bus frequency on the platform. CAUTION: the achieved + /// frequency will almost certainly be very much slower on most platforms. eg on Arduino Uno, the + /// the clock rate is likely to be at best around 46kHz. + /// \param[in] bitOrder Select the SPI bus bit order, one of RHGenericSPI::BitOrderMSBFirst or + /// RHGenericSPI::BitOrderLSBFirst. + /// \param[in] dataMode Selects the SPI bus data mode. One of RHGenericSPI::DataMode + RHSoftwareSPI(Frequency frequency = Frequency1MHz, BitOrder bitOrder = BitOrderMSBFirst, DataMode dataMode = DataMode0); + + /// Transfer a single octet to and from the SPI interface + /// \param[in] data The octet to send + /// \return The octet read from SPI while the data octet was sent. + uint8_t transfer(uint8_t data); + + /// Initialise the software SPI library + /// Call this after configuring the SPI interface and before using it to transfer data. + /// Initializes the SPI bus by setting SCK, MOSI, and SS to outputs, pulling SCK and MOSI low, and SS high. + void begin(); + + /// Disables the SPI bus usually, in this case + /// there is no hardware controller to disable. + void end(); + + /// Sets the pins used by this SoftwareSPIClass instance. + /// The defaults are: MISO = 12, MOSI = 11, SCK = 13. + /// \param[in] miso master in slave out pin used + /// \param[in] mosi master out slave in pin used + /// \param[in] sck clock pin used + void setPins(uint8_t miso = 12, uint8_t mosi = 11, uint8_t sck = 13); + +private: + + /// Delay routine for bus timing. + void delayPeriod(); + +private: + uint8_t _miso; + uint8_t _mosi; + uint8_t _sck; + uint8_t _delayCounts; + uint8_t _clockPolarity; + uint8_t _clockPhase; +}; + +#endif diff --git a/RHTcpProtocol.h b/RHTcpProtocol.h new file mode 100644 index 0000000..bce81d0 --- /dev/null +++ b/RHTcpProtocol.h @@ -0,0 +1,66 @@ +// RH_TcpProtocol.h +// Author: Mike McCauley (mikem@aierspayce.com) +// Definition of protocol messages sent and received by RH_TCP +// Copyright (C) 2014 Mike McCauley +// $Id: RHTcpProtocol.h,v 1.3 2014/05/22 06:07:09 mikem Exp $ + +/// This file contains the definitions of message structures passed between +/// RH_TCP and the etherSimulator +#ifndef RH_TcpProtocol_h +#define RH_TcpProtocol_h + +#define RH_TCP_MESSAGE_TYPE_NOP 0 +#define RH_TCP_MESSAGE_TYPE_THISADDRESS 1 +#define RH_TCP_MESSAGE_TYPE_PACKET 2 + +// Maximum message length (including the headers) we are willing to support +#define RH_TCP_MAX_PAYLOAD_LEN 255 + +// The length of the headers we add. +// The headers are inside the RF69's payload and are therefore encrypted if encryption is enabled +#define RH_TCP_HEADER_LEN 4 + + +// This is the maximum message length that can be supported by this protocol. +#define RH_TCP_MAX_MESSAGE_LEN (RH_TCP_MAX_PAYLOAD_LEN - RH_TCP_HEADER_LEN) + +#pragma pack(push, 1) // No padding + +/// \brief Generic RH_TCP simulator message structure +typedef struct +{ + uint32_t length; ///< Number of octets following, in network byte order + uint8_t payload[RH_TCP_MAX_PAYLOAD_LEN + 1]; ///< Payload +} RHTcpMessage; + +/// \brief Generic RH_TCP message structure with message type +typedef struct +{ + uint32_t length; ///< Number of octets following, in network byte order + uint8_t type; ///< One of RH_TCP_MESSAGE_TYPE_* + uint8_t payload[RH_TCP_MAX_PAYLOAD_LEN]; ///< Payload +} RHTcpTypeMessage; + +/// \brief RH_TCP message Notifies the server of thisAddress of this client +typedef struct +{ + uint32_t length; ///< Number of octets following, in network byte order + uint8_t type; ///< == RH_TCP_MESSAGE_TYPE_THISADDRESS + uint8_t thisAddress; ///< Node address +} RHTcpThisAddress; + +/// \brief RH_TCP radio message passed to or from the simulator +typedef struct +{ + uint32_t length; ///< Number of octets following, in network byte order + uint8_t type; ///< == RH_TCP_MESSAGE_TYPE_PACKET + uint8_t to; ///< Node address of the recipient + uint8_t from; ///< Node address of the sender + uint8_t id; ///< Message sequence number + uint8_t flags; ///< Message flags + uint8_t payload[RH_TCP_MAX_MESSAGE_LEN]; ///< 0 or more, length deduced from length above +} RHTcpPacket; + +#pragma pack(pop) + +#endif diff --git a/RH_ABZ.cpp b/RH_ABZ.cpp new file mode 100644 index 0000000..c9082c3 --- /dev/null +++ b/RH_ABZ.cpp @@ -0,0 +1,116 @@ +// RH_ABZ.cpp +// +// Copyright (C) 2020 Mike McCauley +// $Id: RH_ABZ.cpp,v 1.1 2020/06/15 23:39:39 mikem Exp $ + +#if (RH_PLATFORM == RH_PLATFORM_STM32L0) && (defined STM32L082xx || defined STM32L072xx) + +#include + +// Pointer to the _only_ permitted ABZ instance (there is only one radio connected to this device) +RH_ABZ* RH_ABZ::_thisDevice; + +// The muRata cmwx1zzabz module has its builtin SX1276 radio connected to the processor's SPI1 port, +// but the Arduino compatible SPI interface in Grumpy Pizzas Arduino Core is configured for SPI1 or SPI2 +// depending on the exact board variant selected. +// So here we define our own Arduino compatible SPI interface +// so we are _sure_ to get the one connected to the radio, independent of the board variant selected +#include +static const stm32l0_spi_params_t RADIO_SPI_PARAMS = { + STM32L0_SPI_INSTANCE_SPI1, + 0, + STM32L0_DMA_CHANNEL_NONE, + STM32L0_DMA_CHANNEL_NONE, + { + STM32L0_GPIO_PIN_PA7_SPI1_MOSI, + STM32L0_GPIO_PIN_PA6_SPI1_MISO, + STM32L0_GPIO_PIN_PB3_SPI1_SCK, + STM32L0_GPIO_PIN_NONE, + }, +}; + +// Create and configure an Arduino compatible SPI interface. This will be referred to in RHHardwareSPI.cpp +// and used as the SPI interface to the radio. +static stm32l0_spi_t RADIO_SPI; +SPIClass radio_spi(&RADIO_SPI, &RADIO_SPI_PARAMS); + +// Glue code between the 'C' DIO0 interrupt and the C++ interrupt handler in RH_RF95 +void RH_INTERRUPT_ATTR RH_ABZ::isr() +{ + _thisDevice->handleInterrupt(); +} + +RH_ABZ::RH_ABZ(): + RH_RF95(RH_INVALID_PIN, RH_INVALID_PIN) +{ +} + +bool RH_ABZ::init() +{ + _thisDevice = this; + + // REVISIT: RESET THE RADIO??? + + // The SX1276 radio DIO0 is connected to STM32 pin PB4 + // It will later be configured as an interrupt + stm32l0_gpio_pin_configure(STM32L0_GPIO_PIN_PB4, (STM32L0_GPIO_PARK_NONE | STM32L0_GPIO_PUPD_PULLDOWN | STM32L0_GPIO_OSPEED_HIGH | STM32L0_GPIO_OTYPE_PUSHPULL | STM32L0_GPIO_MODE_INPUT)); + + // Here we configure the interrupt handler for DIO0 to call the C++ + // interrupt handler in RH_RF95, in a roundabout way +#ifdef STM32L0_EXTI_CONTROL_PRIORITY_CRITICAL + stm32l0_exti_attach(STM32L0_GPIO_PIN_PB4, (STM32L0_EXTI_CONTROL_PRIORITY_CRITICAL | STM32L0_EXTI_CONTROL_EDGE_RISING), (stm32l0_exti_callback_t)isr, NULL); // STM32L0_EXTI_CONTROL_PRIORITY_CRITICAL not in 0.0.10 +#else + stm32l0_exti_attach(STM32L0_GPIO_PIN_PB4, STM32L0_EXTI_CONTROL_EDGE_RISING, (stm32l0_exti_callback_t)isr, NULL); +#endif + // The SX1276 radio slave select (NSS) is connected to STM32 pin PA15 + stm32l0_gpio_pin_configure(STM32L0_GPIO_PIN_PA15, (STM32L0_GPIO_PARK_HIZ | STM32L0_GPIO_PUPD_NONE | STM32L0_GPIO_OSPEED_HIGH | STM32L0_GPIO_OTYPE_PUSHPULL | STM32L0_GPIO_MODE_OUTPUT)); + + // muRata cmwx1zzabz module has an antenna switch which must be driven the right way to connect the antenna + // to the appropriate SX1276 pins. + // Antenna switch might be something like NJG180K64, but not sure. + // See Application note: AN-ZZABZ-001 P. 20/20 + // in typeABZ_hardware_design_guide_revC.pdf + // with 3 pins connected to STM32L0_GPIO_PIN_PA1, STM32L0_GPIO_PIN_PC2, STM32L0_GPIO_PIN_PC1 + // which select RX, RFO or PA_BOOST respecitvely + // See modeWillChange() for implementation of pin twiddling when the transmitter is on + // + // We use native STM32 calls because the various different variants in the Grumpy Pizza + // Arduino core and various forks of that core have inconsistent definitions of the Arduino compatible + // pins. We want to be sure we get the right ones for the muRata modules connections to the Radio + stm32l0_gpio_pin_configure(STM32L0_GPIO_PIN_PA1, (STM32L0_GPIO_PARK_NONE | STM32L0_GPIO_PUPD_NONE | STM32L0_GPIO_OSPEED_LOW | STM32L0_GPIO_OTYPE_PUSHPULL | STM32L0_GPIO_MODE_OUTPUT)); + stm32l0_gpio_pin_configure(STM32L0_GPIO_PIN_PC2, (STM32L0_GPIO_PARK_NONE | STM32L0_GPIO_PUPD_NONE | STM32L0_GPIO_OSPEED_LOW | STM32L0_GPIO_OTYPE_PUSHPULL | STM32L0_GPIO_MODE_OUTPUT)); + stm32l0_gpio_pin_configure(STM32L0_GPIO_PIN_PC1, (STM32L0_GPIO_PARK_NONE | STM32L0_GPIO_PUPD_NONE | STM32L0_GPIO_OSPEED_LOW | STM32L0_GPIO_OTYPE_PUSHPULL | STM32L0_GPIO_MODE_OUTPUT)); + + return RH_RF95::init(); +} + +void RH_ABZ::selectSlave() +{ + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PA15, 0); +} + +void RH_ABZ::deselectSlave() +{ + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PA15, 1); +} + +bool RH_ABZ::modeWillChange(RHMode mode) +{ + if (mode == RHModeTx) + { + // Tell the antenna switch to connect to one of thetransmoitter output pins + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PA1, 0); // RX + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PC2, _useRFO ? 1 : 0); // RFO + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PC1, _useRFO ? 0 : 1); // BOOST + } + else + { + // Enabling the RX from the antenna switch improves reception RSSI by about 5 + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PA1, 1); // RX + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PC2, 0); // RFO + stm32l0_gpio_pin_write(STM32L0_GPIO_PIN_PC1, 0); // BOOST + } + return true; +} + +#endif diff --git a/RH_ABZ.h b/RH_ABZ.h new file mode 100644 index 0000000..9e07438 --- /dev/null +++ b/RH_ABZ.h @@ -0,0 +1,191 @@ +// RH_ABZ.h +// +// Definitions for SX1276 radio in muRata CMWX1ZZABZ (TypeABZ) module +// as used in GrumpyOldPizza Grasshopper-L082CZ, EcoNode SmartTrap etc with +// GrumpyOldPizza / ArduinoCore-stm32l0 installed per https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0 +// +// For data refer to muRata Application note: AN-ZZABZ-001 +// muRata Preliminary Specification Number : SP-ABZ-093-E +// $p/EcoNode +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2020 Mike McCauley +// $Id: RH_ABZ.h,v 1.1 2020/06/15 23:39:39 mikem Exp $ +// +#ifndef RH_ABZ_h +#define RH_ABZ_h + +#include + +///////////////////////////////////////////////////////////////////// +/// \class RH_ABZ RH_ABZ.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via +/// radio transceiver in a muRata cmwx1zzabz module, which includes an STM32L0 processor, +/// a SX1276 LoRa radio and an antenna switch. +/// +/// Requires the Grumpy Old Pizza Arduino Core installed per https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0 +/// +/// Works with EcoNode SmartTrap, Tlera Grasshopper and family. Almost any board equipped with a muRata cmwx1zzabz module +/// should work. Tested with EcoNode SmartTrap, Arduino 1.8.9, GrumpyOldPizza Arduino Core for STM32L0. +/// When building for EcoNode SmartTrap in Arduino IDE, select board type Grasshopper-L082CZ. +/// +/// \par Overview +/// +/// This class is a specialisation of the RH_RF95 class, but with overridden functions to ensure that RH_RF95 communicates +/// with the SX1276 radio on the correct SPI interface (STM32L0_SPI_INSTANCE_SPI1). The class configures the SPI and +/// interfaces with the radio using native stm32l0 calls. It also uses stm32l0 calls to intialise the radio interface pins, +/// antenna interface pins, and to toggle the radio NSS pin for SPI communicaitons. The reason for this speicalisaiton is that +/// all the STM32L0 variants in the Grumpy Pizzas Arduino Core define the Arduino compatible SPI interface and the +/// Arduino compatible IO pins in varying and inconsistent ways. So we use native stm32l0 calls to make _sure_ we get the right +/// pins and interfaces. +/// +/// All the comments in the RH_RF95 class concerning modulation, packet formats etc apply equally to this module. +/// +/// \par Temperature Controlled Crystal Oscillator (TCXO) +/// +/// The muRata cmwx1zzabz module includes a TCXO. Pins to enable the TCXO and to connect to 32MHz output to the radio +/// are exposed on the module. Some boards (Econode SmartTrap for example) permanently power the TCXO and permanenetly +/// connect it to the radio. Other boards (Grasshopper for example) have the TCXO enable connected to a GPIO pin, allowing +/// the TCXO to be controlled by software. Different boards may use different GPIO pins to control the TCXO. +/// +/// The SX1276 radio can be configured to use the TCXO, and the Arduino Core defaults the radio to using TCXO. +/// Therefore it is important that you ensure the TCXO is powered up, at least when you want the radio to operate. +/// If the TCXO is not powered, the radio will not work. +/// +/// On the Tlera boards supported the Arduino Core, you can call SX1276SetBoardTcxo() to enable or disable the TCXO +/// by controlling the correct pin for your board. +/// By default the core disables TCXO at the end of initialisation, so by the time your sketch starts to run +/// the TCXO is powered off. +/// You will almost certainly need to call +/// \code +/// SX1276SetBoardTcxo(true); +/// \endcode +/// in your setup() or at other times when you want the radio to operate. +/// +/// If you have a board where the TCXO is permanently powered, this is unnecessary. +/// +/// \par Connecting and configuring the radio +/// +/// There is no special configuration for the SX1276 radio in the muRata cmwx1zzabz module: the CPU, radio and +/// antenna switch are all hardwired within the module can, and cannot be changed. Initialise the radio like this +/// with the default constructor: +/// \code +/// RH_ABZ driver; +/// \endcode +/// +/// \par Range +/// +/// We made some primitive range tests with 2 identical EcoNode SmartTrap at 868MHz, 20dBm transmit power with +/// modem config RH_RF95::Bw125Cr45Sf2048, using the abz_client and abz_server sketches included in this distribution. +/// We monitored for reliabilty of 2-way communications (ie how reliably can the client get a reply from the server, +/// which is a 2-way comms that depends on both send and receive. The SmartTrap has a simple small helical antenna. +/// The environment was a beach in a developed area, one node was stationary +/// on a rock about 1m above sand level. The mobile node was handl-held at 1 m above ground level. All measurements were line-of-sight. +/// +/// \code +/// Location Distance (km) % successful round trip +/// Elephant Rock 0 100 +/// Dune St 1.48 100 +/// Shell St 1.71 100 +/// Sand St 1.93 100 +/// Sea St 2.15 0 +/// Short St 2.36 50 +/// John St 2.59 50 +/// Surf St 2.80 100 +/// Matters St 2.98 100 +/// Mills St 3.92 100 +/// North Kirra 4.62 0 +/// North Kirra SLSC 4.81 80 +/// Haig St 5.20 0 +/// Kirra SLSC 5.91 0 +/// \endcode +/// +/// \par Transmitter Power +/// +/// We have made some actual power measurements against +/// programmed power on an EcoNode SmartTrap. +/// - EcoNode SmartTrap at 868MHz +/// - 15cm RG316 soldered direct to SmartTrap antenna pin +/// - SMA/BNC connector +/// - 12db attenuator (calibrated as 13.5dB at 868MHz) +/// - SMA/BNC connector +/// - 30cm RG316 +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// useRFO==false (ie uses PA_BOOST for higher power) +/// Program power Measured Power +/// dBm dBm +/// 2 2.7 +/// 5 5 +/// 7 6.5 +/// 10 9.5 +/// 13 12.5 +/// 15 14.5 +/// 16 15.5 +/// 17 16.5 +/// 18 17.2 +/// 19 17.6 +/// 20 18.2 +/// +/// useRFO==true (ie no PA_BOOST) +/// Program power Measured Power +/// dBm dBm +/// 0 -5.5 +/// 2 -2.5 +/// 4 -0.5 +/// 6 2 +/// 8 4 +/// 10 6.5 +/// 12 9 +/// 13 10.5 +/// 14 11.5 +/// 15 12.5 +/// \endcode + +/// In the the Grumpy Old Pizza Arduino Core, there is a function for turning the +/// TCXO power source on and off, which depends on exactly which board is being compiled for +/// If the Radio in your boards has its TCXO connected to a programmable power pin, +/// and if you enable TCXO on the radio (by default it is on these boards) +/// then this function needs to be called to enable the TCXO before the radio will work. +extern "C" void SX1276SetBoardTcxo( bool state ); + +class RH_ABZ : public RH_RF95 +{ +public: + /// Constructor + RH_ABZ(); + + /// Initialise the Driver transport hardware and software. Leaves the radio in idle mode, + /// with default configuration of: 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + /// \return true if initialisation succeeded. + virtual bool init(); + +protected: + /// Called by RH_RF95 when the radio mode is about to change to a new setting. + /// Configures the antenna switch to connect to the right radio pin. + /// \param[in] mode RHMode the new mode about to take effect + /// \return true if the subclasses changes successful + virtual bool modeWillChange(RHMode mode); + + /// Called by RHSPIDriver when the SPI is about to talk to the radio. + /// Uses native spi32l0 calls to enable the radio NSS pin + virtual void selectSlave(); + + /// Called by RHSPIDriver when the SPI is finished talking to the radio. + /// Uses native spi32l0 calls to disable the radio NSS pin + virtual void deselectSlave(); + +private: + /// Glue code between the DIO0 interrupt the interrupt handler in RH_RF95 + static void RH_INTERRUPT_ATTR isr(); + + /// Pointer to the one and only instance permitted, for interrupt linkage + static RH_ABZ* _thisDevice; + +}; + +/// @example abz_client.pde +/// @example abz_server.pde + +#endif diff --git a/RH_ASK.cpp b/RH_ASK.cpp new file mode 100644 index 0000000..854594c --- /dev/null +++ b/RH_ASK.cpp @@ -0,0 +1,939 @@ +// RH_ASK.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RH_ASK.cpp,v 1.31 2020/07/05 08:52:21 mikem Exp mikem $ + +#include +#include + +#ifndef __SAMD51__ + +#if (RH_PLATFORM == RH_PLATFORM_STM32) + // Maple etc +HardwareTimer timer(MAPLE_TIMER); + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F3) || defined(ARDUINO_ARCH_STM32F4) +// rogerclarkmelbourne/Arduino_STM32 +// And stm32duino +HardwareTimer timer(TIM1); + +#elif (RH_PLATFORM == RH_PLATFORM_ESP32) +// Michael Cain +DRAM_ATTR hw_timer_t * timer; + +#endif + +// RH_ASK on Arduino uses Timer 1 to generate interrupts 8 times per bit interval +// Define RH_ASK_ARDUINO_USE_TIMER2 if you want to use Timer 2 instead of Timer 1 on Arduino +// You may need this to work around other librraies that insist on using timer 1 +// Should be moved to header file +//#define RH_ASK_ARDUINO_USE_TIMER2 + +// Interrupt handler uses this to find the most recently initialised instance of this driver +static RH_ASK* thisASKDriver; + +// 4 bit to 6 bit symbol converter table +// Used to convert the high and low nybbles of the transmitted data +// into 6 bit symbols for transmission. Each 6-bit symbol has 3 1s and 3 0s +// with at most 3 consecutive identical bits +static uint8_t symbols[] = +{ + 0xd, 0xe, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, + 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34 +}; + +// This is the value of the start symbol after 6-bit conversion and nybble swapping +#define RH_ASK_START_SYMBOL 0xb38 + +RH_ASK::RH_ASK(uint16_t speed, uint8_t rxPin, uint8_t txPin, uint8_t pttPin, bool pttInverted) + : + _speed(speed), + _rxPin(rxPin), + _txPin(txPin), + _pttPin(pttPin), + _rxInverted(false), + _pttInverted(pttInverted) +{ + // Initialise the first 8 nibbles of the tx buffer to be the standard + // preamble. We will append messages after that. 0x38, 0x2c is the start symbol before + // 6-bit conversion to RH_ASK_START_SYMBOL + uint8_t preamble[RH_ASK_PREAMBLE_LEN] = {0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c}; + memcpy(_txBuf, preamble, sizeof(preamble)); +} + +bool RH_ASK::init() +{ + if (!RHGenericDriver::init()) + return false; + thisASKDriver = this; + +#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) + #ifdef RH_ASK_PTT_PIN + RH_ASK_PTT_DDR |= (1< 1) && (ulticks < max_ticks)) + break; // found prescaler + + // Won't fit, check with next prescaler value + } + + + // Check for error + if ((prescaler == 6) || (ulticks < 2) || (ulticks > max_ticks)) + { + // signal fault + *nticks = 0; + return 0; + } + + *nticks = ulticks; + return prescaler; +#else + return 0; // not implemented or needed on other platforms +#endif +} + +// The idea here is to get 8 timer interrupts per bit period +void RH_ASK::timerSetup() +{ +#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) + uint16_t nticks; + uint8_t prescaler = timerCalc(_speed, (uint16_t)-1, &nticks); + if (!prescaler) return; + _COMB(TCCR,RH_ASK_TIMER_INDEX,A)= 0; + _COMB(TCCR,RH_ASK_TIMER_INDEX,B)= _BV(WGM12); + _COMB(TCCR,RH_ASK_TIMER_INDEX,B)|= prescaler; + _COMB(OCR,RH_ASK_TIMER_INDEX,A)= nticks; + _COMB(TI,MSK,RH_ASK_TIMER_INDEX)|= _BV(_COMB(OCIE,RH_ASK_TIMER_INDEX,A)); + +#elif (RH_PLATFORM == RH_PLATFORM_MSP430) // LaunchPad specific + // Calculate the counter overflow count based on the required bit speed + // and CPU clock rate + uint16_t ocr1a = (F_CPU / 8UL) / _speed; + + // This code is for Energia/MSP430 + TA0CCR0 = ocr1a; // Ticks for 62,5 us + TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode + TA0CCTL0 |= CCIE; // CCR0 interrupt enabled + +#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) + Serial.println("STM32L0 RH_ASK NOT YET IMPLEMENTED "); + +#elif (RH_PLATFORM == RH_PLATFORM_STM32) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F3) || defined(ARDUINO_ARCH_STM32F4) + // Maple etc + // or rogerclarkmelbourne/Arduino_STM32 + // or stm32duino + // Pause the timer while we're configuring it + timer.pause(); + + void interrupt(); // defined below + +#ifdef BOARD_NAME + // ST's Arduino Core STM32, https://github.com/stm32duino/Arduino_Core_STM32 + uint16_t us=(1000000/8)/_speed; + timer.setMode(1, TIMER_OUTPUT_COMPARE); + timer.setOverflow(us, MICROSEC_FORMAT); + timer.setCaptureCompare(1, us - 1, MICROSEC_COMPARE_FORMAT); + timer.attachInterrupt(1,interrupt); + +#else + // Roger Clark Arduino STM32, https://github.com/rogerclarkmelbourne/Arduino_STM32 + + //old interface? + timer.setPeriod((1000000/8)/_speed); + // Set up an interrupt on channel 1 + timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); + timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update + timer.attachCompare1Interrupt(interrupt); + + //new interface? + //uint16_t us=(1000000/8)/_speed; + //Timer.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE); + //uint16_t timerOverflow = Timer.setPeriod(us); + //Timer.setCompare(TIMER_CH1, timerOverflow); + //Timer.attachInterrupt(TIMER_CH1, interrupt); + +#endif + + + // Refresh the timer's count, prescale, and overflow + timer.refresh(); + + // Start the timer counting + timer.resume(); + +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY) + // figure out prescaler value and counter match value + // REVISIT: does not correctly handle 1MHz clock speeds, only works with 8MHz clocks + // At 1MHz clock, get 1/8 of the expected baud rate + uint16_t nticks; + uint8_t prescaler = timerCalc(_speed, (uint8_t)-1, &nticks); + if (!prescaler) + return; // fault + + TCCR0A = 0; + TCCR0A = _BV(WGM01); // Turn on CTC mode / Output Compare pins disconnected + + // convert prescaler index to TCCRnB prescaler bits CS00, CS01, CS02 + TCCR0B = 0; + TCCR0B = prescaler; // set CS00, CS01, CS02 (other bits not needed) + + + // Number of ticks to count before firing interrupt + OCR0A = uint8_t(nticks); + + // Set mask to fire interrupt when OCF0A bit is set in TIFR0 + #ifdef TIMSK0 + // ATtiny84 + TIMSK0 |= _BV(OCIE0A); + #else + // ATtiny85 + TIMSK |= _BV(OCIE0A); + #endif + +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA) + // Timer A is used for millis/micros, and B 0 for Tone by default + // Use Timer B 1 + volatile TCB_t* timer = &TCB1; + + // Calculate compare value + uint32_t compare_val = F_CPU_CORRECTED / _speed / 8 - 1; + // If compare larger than 16bits, need to prescale (will be DIV64) + if (compare_val > 0xFFFF) + { + // recalculate with new prescaler + compare_val = F_CPU_CORRECTED / _speed / 8 / 64 - 1; + // Prescaler needed + timer->CTRLA = TCB_CLKSEL_CLKTCA_gc; + } + else + { + // No prescaler needed + timer->CTRLA = TCB_CLKSEL_CLKDIV1_gc; + } + + // Timer to Periodic interrupt mode + // This write will also disable any active PWM outputs + timer->CTRLB = TCB_CNTMODE_INT_gc; + // Write compare register + timer->CCMP = compare_val; + // Enable interrupt + timer->INTCTRL = TCB_CAPTEI_bm; + // Enable Timer + timer->CTRLA |= TCB_ENABLE_bm; + +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) // Arduino specific + uint16_t nticks; // number of prescaled ticks needed + uint8_t prescaler; // Bit values for CS0[2:0] + + + #if defined(__arm__) && defined(CORE_TEENSY) + // on Teensy 3.0 (32 bit ARM), use an interval timer + IntervalTimer *t = new IntervalTimer(); + void TIMER1_COMPA_vect(void); + t->begin(TIMER1_COMPA_vect, 125000 / _speed); + + #elif defined (__arm__) && defined(ARDUINO_ARCH_SAMD) + // Arduino Zero + #define RH_ASK_ZERO_TIMER TC3 + // Clock speed is 48MHz, prescaler of 64 gives a good range of available speeds vs precision + #define RH_ASK_ZERO_PRESCALER 64 + #define RH_ASK_ZERO_TIMER_IRQ TC3_IRQn + + // Enable clock for TC + REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC2_TC3)) ; + while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync + + // The type cast must fit with the selected timer mode + TcCount16* TC = (TcCount16*)RH_ASK_ZERO_TIMER; // get timer struct + + TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; // Disable TC + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + + TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TC as Match Frequency + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + + // Compute the count required to achieve the requested baud (with 8 interrupts per bit) + uint32_t rc = (VARIANT_MCK / _speed) / RH_ASK_ZERO_PRESCALER / 8; + + TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV64; // Set prescaler to agree with RH_ASK_ZERO_PRESCALER + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + + TC->CC[0].reg = rc; // FIXME + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + + // Interrupts + TC->INTENSET.reg = 0; // disable all interrupts + TC->INTENSET.bit.MC0 = 1; // enable compare match to CC0 + + // Enable InterruptVector + NVIC_ClearPendingIRQ(RH_ASK_ZERO_TIMER_IRQ); + NVIC_EnableIRQ(RH_ASK_ZERO_TIMER_IRQ); + + // Enable TC + TC->CTRLA.reg |= TC_CTRLA_ENABLE; + while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync + + #elif defined(__arm__) && defined(ARDUINO_SAM_DUE) + // Arduino Due + // Clock speed is 84MHz + // Due has 9 timers in 3 blocks of 3. + // We use timer 1 TC1_IRQn on TC0 channel 1, since timers 0, 2, 3, 4, 5 are used by the Servo library + #define RH_ASK_DUE_TIMER TC0 + #define RH_ASK_DUE_TIMER_CHANNEL 1 + #define RH_ASK_DUE_TIMER_IRQ TC1_IRQn + pmc_set_writeprotect(false); + pmc_enable_periph_clk(RH_ASK_DUE_TIMER_IRQ); + + // Clock speed 4 can handle all reasonable _speeds we might ask for. Its divisor is 128 + // and we want 8 interrupts per bit + uint32_t rc = (VARIANT_MCK / _speed) / 128 / 8; + TC_Configure(RH_ASK_DUE_TIMER, RH_ASK_DUE_TIMER_CHANNEL, + TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4); + TC_SetRC(RH_ASK_DUE_TIMER, RH_ASK_DUE_TIMER_CHANNEL, rc); + // Enable the RC Compare Interrupt + RH_ASK_DUE_TIMER->TC_CHANNEL[RH_ASK_DUE_TIMER_CHANNEL].TC_IER = TC_IER_CPCS; + NVIC_ClearPendingIRQ(RH_ASK_DUE_TIMER_IRQ); + NVIC_EnableIRQ(RH_ASK_DUE_TIMER_IRQ); + TC_Start(RH_ASK_DUE_TIMER, RH_ASK_DUE_TIMER_CHANNEL); + + #else + // This is the path for most Arduinos + // figure out prescaler value and counter match value + #if defined(RH_ASK_ARDUINO_USE_TIMER2) + prescaler = timerCalc(_speed, (uint8_t)-1, &nticks); + if (!prescaler) + return; // fault + // Use timer 2 + TCCR2A = _BV(WGM21); // Turn on CTC mode) + // convert prescaler index to TCCRnB prescaler bits CS10, CS11, CS12 + TCCR2B = prescaler; + + // Caution: special procedures for setting 16 bit regs + // is handled by the compiler + OCR2A = nticks; + // Enable interrupt + #ifdef TIMSK2 + // atmega168 + TIMSK2 |= _BV(OCIE2A); + #else + // others + TIMSK |= _BV(OCIE2A); + #endif // TIMSK2 + #else + // Use timer 1 + prescaler = timerCalc(_speed, (uint16_t)-1, &nticks); + if (!prescaler) + return; // fault + TCCR1A = 0; // Output Compare pins disconnected + TCCR1B = _BV(WGM12); // Turn on CTC mode + + // convert prescaler index to TCCRnB prescaler bits CS10, CS11, CS12 + TCCR1B |= prescaler; + + // Caution: special procedures for setting 16 bit regs + // is handled by the compiler + OCR1A = nticks; + // Enable interrupt + #ifdef TIMSK1 + // atmega168 + TIMSK1 |= _BV(OCIE1A); + #else + // others + TIMSK |= _BV(OCIE1A); + #endif // TIMSK1 + #endif + #endif + +#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Photon + // Inspired by SparkIntervalTimer + // We use Timer 6 + void TimerInterruptHandler(); // Forward declaration for interrupt handler + #define SYSCORECLOCK 60000000UL // Timer clock tree uses core clock / 2 + TIM_TimeBaseInitTypeDef timerInitStructure; + NVIC_InitTypeDef nvicStructure; + TIM_TypeDef* TIMx; + uint32_t period = (1000000 / 8) / _speed; // In microseconds + uint16_t prescaler = (uint16_t)(SYSCORECLOCK / 1000000UL) - 1; //To get TIM counter clock = 1MHz + + attachSystemInterrupt(SysInterrupt_TIM6_Update, TimerInterruptHandler); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); + nvicStructure.NVIC_IRQChannel = TIM6_DAC_IRQn; + TIMx = TIM6; + nvicStructure.NVIC_IRQChannelPreemptionPriority = 10; + nvicStructure.NVIC_IRQChannelSubPriority = 1; + nvicStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&nvicStructure); + timerInitStructure.TIM_Prescaler = prescaler; + timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; + timerInitStructure.TIM_Period = period; + timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; + timerInitStructure.TIM_RepetitionCounter = 0; + + TIM_TimeBaseInit(TIMx, &timerInitStructure); + TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); + TIM_Cmd(TIMx, ENABLE); + +#elif (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE) + // UsingChipKIT Core on Arduino IDE + uint32_t chipkit_timer_interrupt_handler(uint32_t currentTime); // Forward declaration + attachCoreTimerService(chipkit_timer_interrupt_handler); + +#elif (RH_PLATFORM == RH_PLATFORM_UNO32) + // Under old MPIDE, which has been discontinued: + // ON Uno32 we use timer1 + OpenTimer1(T1_ON | T1_PS_1_1 | T1_SOURCE_INT, (F_CPU / 8) / _speed); + ConfigIntTimer1(T1_INT_ON | T1_INT_PRIOR_1); + +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) + void RH_INTERRUPT_ATTR esp8266_timer_interrupt_handler(); // Forward declaration + // The - 120 is a heuristic to correct for interrupt handling overheads + _timerIncrement = (clockCyclesPerMicrosecond() * 1000000 / 8 / _speed) - 120; + timer0_isr_init(); + timer0_attachInterrupt(esp8266_timer_interrupt_handler); + timer0_write(ESP.getCycleCount() + _timerIncrement); +// timer0_write(ESP.getCycleCount() + 41660000); +#elif (RH_PLATFORM == RH_PLATFORM_ESP32) + void RH_INTERRUPT_ATTR esp32_timer_interrupt_handler(); // Forward declaration + timer = timerBegin(0, 80, true); // Alarm value will be in in us + timerAttachInterrupt(timer, &esp32_timer_interrupt_handler, true); + timerAlarmWrite(timer, 1000000 / _speed / 8, true); + timerAlarmEnable(timer); +#endif + +} + +void RH_INTERRUPT_ATTR RH_ASK::setModeIdle() +{ + if (_mode != RHModeIdle) + { + // Disable the transmitter hardware + writePtt(LOW); + writeTx(LOW); + _mode = RHModeIdle; + } +} + +void RH_INTERRUPT_ATTR RH_ASK::setModeRx() +{ + if (_mode != RHModeRx) + { + // Disable the transmitter hardware + writePtt(LOW); + writeTx(LOW); + _mode = RHModeRx; + } +} + +void RH_ASK::setModeTx() +{ + if (_mode != RHModeTx) + { + // PRepare state varibles for a new transmission + _txIndex = 0; + _txBit = 0; + _txSample = 0; + + // Enable the transmitter hardware + writePtt(HIGH); + + _mode = RHModeTx; + } +} + +// Call this often +bool RH_ASK::available() +{ + if (_mode == RHModeTx) + return false; + setModeRx(); + if (_rxBufFull) + { + validateRxBuf(); + _rxBufFull= false; + } + return _rxBufValid; +} + +bool RH_INTERRUPT_ATTR RH_ASK::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + + if (buf && len) + { + // Skip the length and 4 headers that are at the beginning of the rxBuf + // and drop the trailing 2 bytes of FCS + uint8_t message_len = _rxBufLen-RH_ASK_HEADER_LEN - 3; + if (*len > message_len) + *len = message_len; + memcpy(buf, _rxBuf+RH_ASK_HEADER_LEN+1, *len); + } + _rxBufValid = false; // Got the most recent message, delete it +// printBuffer("recv:", buf, *len); + return true; +} + +// Caution: this may block +bool RH_ASK::send(const uint8_t* data, uint8_t len) +{ + uint8_t i; + uint16_t index = 0; + uint16_t crc = 0xffff; + uint8_t *p = _txBuf + RH_ASK_PREAMBLE_LEN; // start of the message area + uint8_t count = len + 3 + RH_ASK_HEADER_LEN; // Added byte count and FCS and headers to get total number of bytes + + if (len > RH_ASK_MAX_MESSAGE_LEN) + return false; + + // Wait for transmitter to become available + waitPacketSent(); + + if (!waitCAD()) + return false; // Check channel activity + + // Encode the message length + crc = RHcrc_ccitt_update(crc, count); + p[index++] = symbols[count >> 4]; + p[index++] = symbols[count & 0xf]; + + // Encode the headers + crc = RHcrc_ccitt_update(crc, _txHeaderTo); + p[index++] = symbols[_txHeaderTo >> 4]; + p[index++] = symbols[_txHeaderTo & 0xf]; + crc = RHcrc_ccitt_update(crc, _txHeaderFrom); + p[index++] = symbols[_txHeaderFrom >> 4]; + p[index++] = symbols[_txHeaderFrom & 0xf]; + crc = RHcrc_ccitt_update(crc, _txHeaderId); + p[index++] = symbols[_txHeaderId >> 4]; + p[index++] = symbols[_txHeaderId & 0xf]; + crc = RHcrc_ccitt_update(crc, _txHeaderFlags); + p[index++] = symbols[_txHeaderFlags >> 4]; + p[index++] = symbols[_txHeaderFlags & 0xf]; + + // Encode the message into 6 bit symbols. Each byte is converted into + // 2 6-bit symbols, high nybble first, low nybble second + for (i = 0; i < len; i++) + { + crc = RHcrc_ccitt_update(crc, data[i]); + p[index++] = symbols[data[i] >> 4]; + p[index++] = symbols[data[i] & 0xf]; + } + + // Append the fcs, 16 bits before encoding (4 6-bit symbols after encoding) + // Caution: VW expects the _ones_complement_ of the CCITT CRC-16 as the FCS + // VW sends FCS as low byte then hi byte + crc = ~crc; + p[index++] = symbols[(crc >> 4) & 0xf]; + p[index++] = symbols[crc & 0xf]; + p[index++] = symbols[(crc >> 12) & 0xf]; + p[index++] = symbols[(crc >> 8) & 0xf]; + + // Total number of 6-bit symbols to send + _txBufLen = index + RH_ASK_PREAMBLE_LEN; + + // Start the low level interrupt handler sending symbols + setModeTx(); + + return true; +} + +// Read the RX data input pin, taking into account platform type and inversion. +bool RH_INTERRUPT_ATTR RH_ASK::readRx() +{ + bool value; +#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) + value = ((RH_ASK_RX_PORT & (1<handleTimerInterrupt(); +} + +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && defined(ARDUINO_ARCH_SAMD) +// Arduino Zero +void TC3_Handler() +{ + // The type cast must fit with the selected timer mode + TcCount16* TC = (TcCount16*)RH_ASK_ZERO_TIMER; // get timer struct + TC->INTFLAG.bit.MC0 = 1; + thisASKDriver->handleTimerInterrupt(); +} + +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(ARDUINO_SAM_DUE) +// Arduino Due +void TC1_Handler() +{ + TC_GetStatus(RH_ASK_DUE_TIMER, 1); + thisASKDriver->handleTimerInterrupt(); +} +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && (defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F3) || defined(ARDUINO_ARCH_STM32F4)) +//rogerclarkmelbourne/Arduino_STM32 +void interrupt() +{ + thisASKDriver->handleTimerInterrupt(); +} +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) +// This is the interrupt service routine called when timer1 overflows +// Its job is to output the next bit from the transmitter (every 8 calls) +// and to call the PLL code if the receiver is enabled +//ISR(SIG_OUTPUT_COMPARE1A) +ISR(RH_ASK_TIMER_VECTOR) +{ + thisASKDriver->handleTimerInterrupt(); +} + +#elif (RH_PLATFORM == RH_PLATFORM_MSP430) || (RH_PLATFORM == RH_PLATFORM_STM32) +// LaunchPad, Maple +void interrupt() +{ + thisASKDriver->handleTimerInterrupt(); +} + +#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Photon +void TimerInterruptHandler() +{ + thisASKDriver->handleTimerInterrupt(); +} + +#elif (RH_PLATFORM == RH_PLATFORM_MSP430) +interrupt(TIMER0_A0_VECTOR) Timer_A_int(void) +{ + thisASKDriver->handleTimerInterrupt(); +}; + +#elif (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE) +// Using ChipKIT Core on Arduino IDE +uint32_t chipkit_timer_interrupt_handler(uint32_t currentTime) +{ + thisASKDriver->handleTimerInterrupt(); + return (currentTime + ((CORE_TICK_RATE * 1000)/8)/thisASKDriver->speed()); +} + +#elif (RH_PLATFORM == RH_PLATFORM_UNO32) +// Under old MPIDE, which has been discontinued: +extern "C" +{ + void __ISR(_TIMER_1_VECTOR, ipl1) timerInterrupt(void) + { + thisASKDriver->handleTimerInterrupt(); + mT1ClearIntFlag(); // Clear timer 1 interrupt flag +} +} +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) +void RH_INTERRUPT_ATTR esp8266_timer_interrupt_handler() +{ +// timer0_write(ESP.getCycleCount() + 41660000); +// timer0_write(ESP.getCycleCount() + (clockCyclesPerMicrosecond() * 100) - 120 ); + timer0_write(ESP.getCycleCount() + thisASKDriver->_timerIncrement); +// static int toggle = 0; +// toggle = (toggle == 1) ? 0 : 1; +// digitalWrite(4, toggle); + thisASKDriver->handleTimerInterrupt(); +} +#elif (RH_PLATFORM == RH_PLATFORM_ESP32) +void IRAM_ATTR esp32_timer_interrupt_handler() +{ + thisASKDriver->handleTimerInterrupt(); +} +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA) +ISR(TCB1_INT_vect) +{ + thisASKDriver->handleTimerInterrupt(); + TCB1.INTFLAGS = TCB_CAPT_bm; +} +#endif + +// Convert a 6 bit encoded symbol into its 4 bit decoded equivalent +uint8_t RH_INTERRUPT_ATTR RH_ASK::symbol_6to4(uint8_t symbol) +{ + uint8_t i; + uint8_t count; + + // Linear search :-( Could have a 64 byte reverse lookup table? + // There is a little speedup here courtesy Ralph Doncaster: + // The shortcut works because bit 5 of the symbol is 1 for the last 8 + // symbols, and it is 0 for the first 8. + // So we only have to search half the table + for (i = (symbol>>2) & 8, count=8; count-- ; i++) + if (symbol == symbols[i]) return i; + + return 0; // Not found +} + +// Check whether the latest received message is complete and uncorrupted +// We should always check the FCS at user level, not interrupt level +// since it is slow +void RH_ASK::validateRxBuf() +{ + uint16_t crc = 0xffff; + // The CRC covers the byte count, headers and user data + for (uint8_t i = 0; i < _rxBufLen; i++) + crc = RHcrc_ccitt_update(crc, _rxBuf[i]); + if (crc != 0xf0b8) // CRC when buffer and expected CRC are CRC'd + { + // Reject and drop the message + _rxBad++; + _rxBufValid = false; + return; + } + + // Extract the 4 headers that follow the message length + _rxHeaderTo = _rxBuf[1]; + _rxHeaderFrom = _rxBuf[2]; + _rxHeaderId = _rxBuf[3]; + _rxHeaderFlags = _rxBuf[4]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +void RH_INTERRUPT_ATTR RH_ASK::receiveTimer() +{ + bool rxSample = readRx(); + + // Integrate each sample + if (rxSample) + _rxIntegrator++; + + if (rxSample != _rxLastSample) + { + // Transition, advance if ramp > 80, retard if < 80 + _rxPllRamp += ((_rxPllRamp < RH_ASK_RAMP_TRANSITION) + ? RH_ASK_RAMP_INC_RETARD + : RH_ASK_RAMP_INC_ADVANCE); + _rxLastSample = rxSample; + } + else + { + // No transition + // Advance ramp by standard 20 (== 160/8 samples) + _rxPllRamp += RH_ASK_RAMP_INC; + } + if (_rxPllRamp >= RH_ASK_RX_RAMP_LEN) + { + // Add this to the 12th bit of _rxBits, LSB first + // The last 12 bits are kept + _rxBits >>= 1; + + // Check the integrator to see how many samples in this cycle were high. + // If < 5 out of 8, then its declared a 0 bit, else a 1; + if (_rxIntegrator >= 5) + _rxBits |= 0x800; + + _rxPllRamp -= RH_ASK_RX_RAMP_LEN; + _rxIntegrator = 0; // Clear the integral for the next cycle + + if (_rxActive) + { + // We have the start symbol and now we are collecting message bits, + // 6 per symbol, each which has to be decoded to 4 bits + if (++_rxBitCount >= 12) + { + // Have 12 bits of encoded message == 1 byte encoded + // Decode as 2 lots of 6 bits into 2 lots of 4 bits + // The 6 lsbits are the high nybble + uint8_t this_byte = + (symbol_6to4(_rxBits & 0x3f)) << 4 + | symbol_6to4(_rxBits >> 6); + + // The first decoded byte is the byte count of the following message + // the count includes the byte count and the 2 trailing FCS bytes + // REVISIT: may also include the ACK flag at 0x40 + if (_rxBufLen == 0) + { + // The first byte is the byte count + // Check it for sensibility. It cant be less than 7, since it + // includes the byte count itself, the 4 byte header and the 2 byte FCS + _rxCount = this_byte; + if (_rxCount < 7 || _rxCount > RH_ASK_MAX_PAYLOAD_LEN) + { + // Stupid message length, drop the whole thing + _rxActive = false; + _rxBad++; + return; + } + } + _rxBuf[_rxBufLen++] = this_byte; + + if (_rxBufLen >= _rxCount) + { + // Got all the bytes now + _rxActive = false; + _rxBufFull = true; + setModeIdle(); + } + _rxBitCount = 0; + } + } + // Not in a message, see if we have a start symbol + else if (_rxBits == RH_ASK_START_SYMBOL) + { + // Have start symbol, start collecting message + _rxActive = true; + _rxBitCount = 0; + _rxBufLen = 0; + } + } +} + +void RH_INTERRUPT_ATTR RH_ASK::transmitTimer() +{ + if (_txSample++ == 0) + { + // Send next bit + // Symbols are sent LSB first + // Finished sending the whole message? (after waiting one bit period + // since the last bit) + if (_txIndex >= _txBufLen) + { + setModeIdle(); + _txGood++; + } + else + { + writeTx(_txBuf[_txIndex] & (1 << _txBit++)); + if (_txBit >= 6) + { + _txBit = 0; + _txIndex++; + } + } + } + + if (_txSample > 7) + _txSample = 0; +} + +void RH_INTERRUPT_ATTR RH_ASK::handleTimerInterrupt() +{ + if (_mode == RHModeRx) + receiveTimer(); // Receiving + else if (_mode == RHModeTx) + transmitTimer(); // Transmitting +} + +#endif //_SAMD51__ diff --git a/RH_ASK.h b/RH_ASK.h new file mode 100644 index 0000000..bea8104 --- /dev/null +++ b/RH_ASK.h @@ -0,0 +1,449 @@ +// RH_ASK.h +// +// Copyright (C) 2014 Mike McCauley +// $Id: RH_ASK.h,v 1.22 2020/05/06 22:26:45 mikem Exp $ + +#ifndef RH_ASK_h +#define RH_ASK_h + +#include + +// Maximum message length (including the headers, byte count and FCS) we are willing to support +// This is pretty arbitrary +#define RH_ASK_MAX_PAYLOAD_LEN 67 + +// The length of the headers we add (To, From, Id, Flags) +// The headers are inside the payload and are therefore protected by the FCS +#define RH_ASK_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this library. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 1 byte message length, 4 bytes headers, user data and 2 bytes of FCS +#ifndef RH_ASK_MAX_MESSAGE_LEN + #define RH_ASK_MAX_MESSAGE_LEN (RH_ASK_MAX_PAYLOAD_LEN - RH_ASK_HEADER_LEN - 3) +#endif + +#if !defined(RH_ASK_RX_SAMPLES_PER_BIT) +/// Number of samples per bit + #define RH_ASK_RX_SAMPLES_PER_BIT 8 +#endif //RH_ASK_RX_SAMPLES_PER_BIT + +/// The size of the receiver ramp. Ramp wraps modulo this number +#define RH_ASK_RX_RAMP_LEN 160 + +// Ramp adjustment parameters +// Standard is if a transition occurs before RH_ASK_RAMP_TRANSITION (80) in the ramp, +// the ramp is retarded by adding RH_ASK_RAMP_INC_RETARD (11) +// else by adding RH_ASK_RAMP_INC_ADVANCE (29) +// If there is no transition it is adjusted by RH_ASK_RAMP_INC (20) +/// Internal ramp adjustment parameter +#define RH_ASK_RAMP_INC (RH_ASK_RX_RAMP_LEN/RH_ASK_RX_SAMPLES_PER_BIT) +/// Internal ramp adjustment parameter +#define RH_ASK_RAMP_TRANSITION RH_ASK_RX_RAMP_LEN/2 +/// Internal ramp adjustment parameter +#define RH_ASK_RAMP_ADJUST 9 +/// Internal ramp adjustment parameter +#define RH_ASK_RAMP_INC_RETARD (RH_ASK_RAMP_INC-RH_ASK_RAMP_ADJUST) +/// Internal ramp adjustment parameter +#define RH_ASK_RAMP_INC_ADVANCE (RH_ASK_RAMP_INC+RH_ASK_RAMP_ADJUST) + +/// Outgoing message bits grouped as 6-bit words +/// 36 alternating 1/0 bits, followed by 12 bits of start symbol (together called the preamble) +/// Followed immediately by the 4-6 bit encoded byte count, +/// message buffer and 2 byte FCS +/// Each byte from the byte count on is translated into 2x6-bit words +/// Caution, each symbol is transmitted LSBit first, +/// but each byte is transmitted high nybble first +/// This is the number of 6 bit nibbles in the preamble +#define RH_ASK_PREAMBLE_LEN 8 + +///////////////////////////////////////////////////////////////////// +/// \class RH_ASK RH_ASK.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via inexpensive ASK (Amplitude Shift Keying) or +/// OOK (On Off Keying) RF transceivers. +/// +/// The message format and software technology is based on our earlier VirtualWire library +/// (http://www.airspayce.com/mikem/arduino/VirtualWire), with which it is compatible. +/// See http://www.airspayce.com/mikem/arduino/VirtualWire.pdf for more details. +/// VirtualWire is now obsolete and unsupported and is replaced by this library. +/// +/// RH_ASK is a Driver for Arduino, Maple and others that provides features to send short +/// messages, without addressing, retransmit or acknowledgment, a bit like UDP +/// over wireless, using ASK (amplitude shift keying). Supports a number of +/// inexpensive radio transmitters and receivers. All that is required is +/// transmit data, receive data and (for transmitters, optionally) a PTT +/// transmitter enable. Can also be used over various analog connections (not just a data radio), +/// such as the audio channel of an A/V sender, or long TTL lines. +/// +/// It is intended to be compatible with the RF Monolithics (www.rfm.com) +/// Virtual Wire protocol, but this has not been tested. +/// +/// Does not use the Arduino UART. Messages are sent with a training preamble, +/// message length and checksum. Messages are sent with 4-to-6 bit encoding +/// for good DC balance, and a CRC checksum for message integrity. +/// +/// But why not just use a UART connected directly to the +/// transmitter/receiver? As discussed in the RFM documentation, ASK receivers +/// require a burst of training pulses to synchronize the transmitter and +/// receiver, and also requires good balance between 0s and 1s in the message +/// stream in order to maintain the DC balance of the message. UARTs do not +/// provide these. They work a bit with ASK wireless, but not as well as this +/// code. +/// +/// \par Theory of operation +/// +/// See ASH Transceiver Software Designer's Guide of 2002.08.07 +/// http://wireless.murata.com/media/products/apnotes/tr_swg05.pdf?ref=rfm.com +/// +/// http://web.engr.oregonstate.edu/~moon/research/files/cas2_mar_07_dpll.pdf while not directly relevant +/// is also interesting. +/// +/// \par Implementation Details +/// +/// Messages of up to RH_ASK_MAX_PAYLOAD_LEN (67) bytes can be sent +/// Each message is transmitted as: +/// +/// - 36 bit training preamble consisting of 0-1 bit pairs +/// - 12 bit start symbol 0xb38 +/// - 1 byte of message length byte count (4 to 30), count includes byte count and FCS bytes +/// - n message bytes (uincluding 4 bytes of header), maximum n is RH_ASK_MAX_MESSAGE_LEN + 4 (64) +/// - 2 bytes FCS, sent low byte-hi byte +/// +/// Everything after the start symbol is encoded 4 to 6 bits, Therefore a byte in the message +/// is encoded as 2x6 bit symbols, sent hi nybble, low nybble. Each symbol is sent LSBit +/// first. The message may consist of any binary digits. +/// +/// The Arduino Diecimila clock rate is 16MHz => 62.5ns/cycle. +/// For an RF bit rate of 2000 bps, need 500microsec bit period. +/// The ramp requires 8 samples per bit period, so need 62.5microsec per sample => interrupt tick is 62.5microsec. +/// +/// The maximum packet length consists of +/// (6 + 2 + RH_ASK_MAX_MESSAGE_LEN*2) * 6 = 768 bits = 0.384 secs (at 2000 bps). +/// where RH_ASK_MAX_MESSAGE_LEN is RH_ASK_MAX_PAYLOAD_LEN - 7 (= 60). +/// The code consists of an ISR interrupt handler. Most of the work is done in the interrupt +/// handler for both transmit and receive, but some is done from the user level. Expensive +/// functions like CRC computations are always done in the user level. +/// +/// \par Supported Hardware +/// +/// A range of communications +/// hardware is supported. The ones listed below are available in common retail +/// outlets in Australia and other countries for under $10 per unit. Many +/// other modules may also work with this software. +/// +/// Runs on a wide range of Arduino processors using Arduino IDE 1.0 or later. +/// Also runs on on Energia, +/// with MSP430G2553 / G2452 and Arduino with ATMega328 (courtesy Yannick DEVOS - XV4Y), +/// but untested by us. It also runs on Teensy 3.0 (courtesy of Paul +/// Stoffregen), but untested by us. Also compiles and runs on ATtiny85 in +/// Arduino environment, courtesy r4z0r7o3. Also compiles on maple-ide-v0.0.12, +/// and runs on Maple, flymaple 1.1 etc. Runs on ATmega8/168 (Arduino Diecimila, +/// Uno etc), ATmega328 and can run on almost any other AVR8 platform, +/// without relying on the Arduino framework, by properly configuring the +/// library editing the RH_ASK.h header file for describing the access +/// to IO pins and for setting up the timer. +/// Runs on ChipKIT Core supported processors such as Uno32 etc. +/// +/// - Receivers +/// - RX-B1 (433.92MHz) (also known as ST-RX04-ASK) +/// - RFM83C from HopeRF http://www.hoperfusa.com/details.jsp?pid=126 +/// - SYN480R and other similar ASK receivers +/// - Transmitters: +/// - TX-C1 (433.92MHz) +/// - RFM85 from HopeRF http://www.hoperfusa.com/details.jsp?pid=127 +/// - SYN115, F115 and other similar ASK transmitters +/// - Transceivers +/// - DR3100 (433.92MHz) +/// +/// \par Connecting to Arduino +/// +/// Most transmitters can be connected to Arduino like this: +/// \code +/// Arduino Transmitter +/// GND------------------------------GND +/// D12------------------------------Data +/// 5V-------------------------------VCC +/// \endcode +/// +/// Most receivers can be connected to Arduino like this: +/// \code +/// Arduino Receiver +/// GND------------------------------GND +/// D11------------------------------Data +/// 5V-------------------------------VCC +/// SHUT (not connected) +/// WAKEB (not connected) +/// GND | +/// ANT |- connect to your antenna syetem +/// \endcode +/// +/// RH_ASK works with ATTiny85, using Arduino 1.0.5 and tinycore from +/// https://code.google.com/p/arduino-tiny/downloads/detail?name=arduino-tiny-0100-0018.zip +/// Tested with the examples ask_transmitter and ask_receiver on ATTiny85. +/// Caution: The RAM memory requirements on an ATTiny85 are *very* tight. Even the bare bones +/// ask_transmitter sketch barely fits in eh RAM available on the ATTiny85. Its unlikely to work on +/// smaller ATTinys such as the ATTiny45 etc. If you have wierd behaviour, consider +/// reducing the size of RH_ASK_MAX_PAYLOAD_LEN to the minimum you can work with. +/// Caution: the default internal clock speed on an ATTiny85 is 1MHz. You MUST set the internal clock speed +/// to 8MHz. You can do this with Arduino IDE, tineycore and ArduinoISP by setting the board type to "ATtiny85@8MHz', +/// setting theProgrammer to 'Arduino as ISP' and selecting Tools->Burn Bootloader. This does not actually burn a +/// bootloader into the tiny, it just changes the fuses so the chip runs at 8MHz. +/// If you run the chip at 1MHz, you will get RK_ASK speeds 1/8th of the expected. +/// +/// Initialise RH_ASK for ATTiny85 like this: +/// \code +/// // #include // comment this out, not needed +/// RH_ASK driver(2000, 4, 3); // 200bps, TX on D3 (pin 2), RX on D4 (pin 3) +/// \endcode +/// then: +/// Connect D3 (pin 2) as the output to the transmitter +/// Connect D4 (pin 3) as the input from the receiver. +/// +/// With AtTiny x17 (such as 3217 etc) using Spencer Kondes megaTinyCore, You can initialise like this: +/// RH_ASK driver(2000, 6, 7); +/// which will transmit on digital pin 7 == PB4 == physical pin 12 on Attiny x17 +/// and receive on digital pin 6 == PB5 == physical pin 11 on Attiny x17 +/// Uses Timer B1. +/// +/// With AtTiny x16 (such as 3216 etc) using Spencer Kondes megaTinyCore, You can initialise like this: +/// RH_ASK driver(2000, 11, 12); +/// which will transmit on digital pin 12 == PC2 == physical pin 14 on Attiny x16 +/// and receive on digital pin 11 == PC1 == physical pin 13 on Attiny x16 +/// Uses Timer B1. +/// +/// With AtTiny x14 (such as 1614 etc) using Spencer Kondes megaTinyCore, You can initialise like this: +/// RH_ASK driver(2000, 6, 7); +/// which will transmit on digital pin 7 == PB0 == physical pin 9 on Attiny x14 +/// and receive on digital pin 6 == PB1 == physical pin 8 on Attiny x16 +/// Uses Timer B1. +/// +/// For testing purposes you can connect 2 Arduino RH_ASK instances directly, by +/// connecting pin 12 of one to 11 of the other and vice versa, like this for a duplex connection: +/// +/// \code +/// Arduino 1 wires Arduino 1 +/// D11-----------------------------D12 +/// D12-----------------------------D11 +/// GND-----------------------------GND +/// \endcode +/// +/// You can also connect 2 RH_ASK instances over a suitable analog +/// transmitter/receiver, such as the audio channel of an A/V transmitter/receiver. You may need +/// buffers at each end of the connection to convert the 0-5V digital output to a suitable analog voltage. +/// +/// Measured power output from RFM85 at 5V was 18dBm. +/// +/// \par ESP8266 +/// This module has been tested with the ESP8266 using an ESP-12 on a breakout board +/// ESP-12E SMD Adaptor Board with Power Regulator from tronixlabs +/// http://tronixlabs.com.au/wireless/esp8266/esp8266-esp-12e-smd-adaptor-board-with-power-regulator-australia/ +/// compiled on Arduino 1.6.5 and the ESP8266 support 2.0 installed with Board Manager. +/// CAUTION: do not use pin 11 for IO with this chip: it will cause the sketch to hang. Instead +/// use constructor arguments to configure different pins, eg: +/// \code +/// RH_ASK driver(2000, 2, 4, 5); +/// \endcode +/// Which will initialise the driver at 2000 bps, recieve on GPIO2, transmit on GPIO4, PTT on GPIO5. +/// Caution: on the tronixlabs breakout board, pins 4 and 5 may be labelled vice-versa. +/// +/// \par Timers +/// The RH_ASK driver uses a timer-driven interrupt to generate 8 interrupts per bit period. RH_ASK +/// takes over a timer on Arduino-like platforms. By default it takes over Timer 1. You can force it +/// to use Timer 2 instead by enabling the define RH_ASK_ARDUINO_USE_TIMER2 near the top of RH_ASK.cpp +/// On Arduino Zero it takes over timer TC3. On Arduino Due it takes over timer +/// TC0. On ESP8266, takes over timer0 (which conflicts with ServoTimer0). +/// +/// Caution: ATTiny85 has only 2 timers, one (timer 0) usually used for +/// millis() and one (timer 1) for PWM analog outputs. The RH_ASK Driver +/// library, when built for ATTiny85, takes over timer 0, which prevents use +/// of millis() etc but does permit analog outputs. This will affect the accuracy of millis() and time +/// measurement. +/// +/// \par STM32 F4 Discovery with Arduino and Arduino_STM32 +/// You can initialise the driver like this: +/// \code +/// RH_ASK driver(2000, PA3, PA4); +/// \endcode +/// and connect the serial to pins PA3 and PA4 +class RH_ASK : public RHGenericDriver +{ +public: + /// Constructor. + /// At present only one instance of RH_ASK per sketch is supported. + /// \param[in] speed The desired bit rate in bits per second + /// \param[in] rxPin The pin that is used to get data from the receiver + /// \param[in] txPin The pin that is used to send data to the transmitter + /// \param[in] pttPin The pin that is connected to the transmitter controller. It will be set HIGH to enable the transmitter (unless pttInverted is true). + /// \param[in] pttInverted true if you desire the pttin to be inverted so that LOW wil enable the transmitter. + RH_ASK(uint16_t speed = 2000, uint8_t rxPin = 11, uint8_t txPin = 12, uint8_t pttPin = 10, bool pttInverted = false); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received bythe transport, when it wil be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + RH_INTERRUPT_ATTR virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + RH_INTERRUPT_ATTR void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the RF69. + RH_INTERRUPT_ATTR void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the RF69. + void setModeTx(); + + /// dont call this it used by the interrupt handler + RH_INTERRUPT_ATTR void handleTimerInterrupt(); + + /// Returns the current speed in bits per second + /// \return The current speed in bits per second + uint16_t speed() { return _speed;} + +#if (RH_PLATFORM == RH_PLATFORM_ESP8266) + /// ESP8266 timer0 increment value + uint32_t _timerIncrement; +#endif + +protected: + /// Helper function for calculating timer ticks + uint8_t timerCalc(uint16_t speed, uint16_t max_ticks, uint16_t *nticks); + + /// Set up the timer and its interrutps so the interrupt handler is called at the right frequency + void timerSetup(); + + /// Read the rxPin in a platform dependent way, taking into account whether it is inverted or not + RH_INTERRUPT_ATTR bool readRx(); + + /// Write the txPin in a platform dependent way + void writeTx(bool value); + + /// Write the txPin in a platform dependent way, taking into account whether it is inverted or not + void writePtt(bool value); + + /// Translates a 6 bit symbol to its 4 bit plaintext equivalent + RH_INTERRUPT_ATTR uint8_t symbol_6to4(uint8_t symbol); + + /// The receiver handler function, called a 8 times the bit rate + void receiveTimer(); + + /// The transmitter handler function, called a 8 times the bit rate + void transmitTimer(); + + /// Check whether the latest received message is complete and uncorrupted + /// We should always check the FCS at user level, not interrupt level + /// since it is slow + void validateRxBuf(); + + /// Configure bit rate in bits per second + uint16_t _speed; + + /// The configure receiver pin + uint8_t _rxPin; + + /// The configure transmitter pin + uint8_t _txPin; + + /// The configured transmitter enable pin + uint8_t _pttPin; + + /// True of the sense of the rxPin is to be inverted + bool _rxInverted; + + /// True of the sense of the pttPin is to be inverted + bool _pttInverted; + + // Used in the interrupt handlers + /// Buf is filled but not validated + volatile bool _rxBufFull; + + /// Buf is full and valid + volatile bool _rxBufValid; + + /// Last digital input from the rx data pin + volatile bool _rxLastSample; + + /// This is the integrate and dump integral. If there are <5 0 samples in the PLL cycle + /// the bit is declared a 0, else a 1 + volatile uint8_t _rxIntegrator; + + /// PLL ramp, varies between 0 and RH_ASK_RX_RAMP_LEN-1 (159) over + /// RH_ASK_RX_SAMPLES_PER_BIT (8) samples per nominal bit time. + /// When the PLL is synchronised, bit transitions happen at about the + /// 0 mark. + volatile uint8_t _rxPllRamp; + + /// Flag indicates if we have seen the start symbol of a new message and are + /// in the processes of reading and decoding it + volatile uint8_t _rxActive; + + /// Last 12 bits received, so we can look for the start symbol + volatile uint16_t _rxBits; + + /// How many bits of message we have received. Ranges from 0 to 12 + volatile uint8_t _rxBitCount; + + /// The incoming message buffer + uint8_t _rxBuf[RH_ASK_MAX_PAYLOAD_LEN]; + + /// The incoming message expected length + volatile uint8_t _rxCount; + + /// The incoming message buffer length received so far + volatile uint8_t _rxBufLen; + + /// Index of the next symbol to send. Ranges from 0 to vw_tx_len + uint8_t _txIndex; + + /// Bit number of next bit to send + uint8_t _txBit; + + /// Sample number for the transmitter. Runs 0 to 7 during one bit interval + uint8_t _txSample; + + /// The transmitter buffer in _symbols_ not data octets + uint8_t _txBuf[(RH_ASK_MAX_PAYLOAD_LEN * 2) + RH_ASK_PREAMBLE_LEN]; + + /// Number of symbols in _txBuf to be sent; + uint8_t _txBufLen; + +}; + +/// @example ask_reliable_datagram_client.pde +/// @example ask_reliable_datagram_server.pde +/// @example ask_transmitter.pde +/// @example ask_receiver.pde +#endif diff --git a/RH_CC110.cpp b/RH_CC110.cpp new file mode 100644 index 0000000..bd3f0d9 --- /dev/null +++ b/RH_CC110.cpp @@ -0,0 +1,493 @@ +// RH_CC110.cpp +// +// Driver for Texas Instruments CC110L transceiver. +// +// Copyright (C) 2016 Mike McCauley +// $Id: RH_CC110.cpp,v 1.11 2020/01/05 07:02:23 mikem Exp $ + +#include + +// Interrupt vectors for the 3 Arduino interrupt pins +// Each interrupt can be handled by a different instance of RH_CC110, allowing you to have +// 2 or more LORAs per Arduino +RH_CC110* RH_CC110::_deviceForInterrupt[RH_CC110_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_CC110::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// We need 2 tables of modem configuration registers, since some values change depending on the Xtal frequency +// These are indexed by the values of ModemConfigChoice +// Canned modem configurations generated with the TI SmartRF Studio v7 version 2.3.0 on boodgie +// based on the sample 'Typical settings' +// Stored in flash (program) memory to save SRAM +// For 26MHz crystals +PROGMEM static const RH_CC110::ModemConfig MODEM_CONFIG_TABLE_26MHZ[] = +{ + // 0B 0C 10 11 12 15 19 1A 1B 1C 1D 21 22 23 24 25 26 2C 2D 2E + {0x06, 0x00, 0xf5, 0x83, 0x13, 0x15, 0x16, 0x6c, 0x03, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb1_2Fd5_2 + {0x06, 0x00, 0xf6, 0x83, 0x13, 0x15, 0x16, 0x6c, 0x03, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb2_4Fd5_2 + {0x06, 0x00, 0xc7, 0x83, 0x13, 0x40, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb4_8Fd25_4 + {0x06, 0x00, 0xc8, 0x93, 0x13, 0x34, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb10Fd19 + {0x06, 0x00, 0xca, 0x83, 0x13, 0x35, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb38_4Fd20 + {0x08, 0x00, 0x7b, 0x83, 0x13, 0x42, 0x1d, 0x1c, 0xc7, 0x00, 0xb2, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb76_8Fd32 + {0x08, 0x00, 0x5b, 0xf8, 0x13, 0x47, 0x1d, 0x1c, 0xc7, 0x00, 0xb2, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x81, 0x31, 0x09}, // GFSK_Rb100Fd47 + {0x0c, 0x00, 0x2d, 0x3b, 0x13, 0x62, 0x1d, 0x1c, 0xc7, 0x00, 0xb0, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x88, 0x31, 0x09}, // GFSK_Rb250Fd127 +}; + +// For 27MHz crystals +PROGMEM static const RH_CC110::ModemConfig MODEM_CONFIG_TABLE_27MHZ[] = +{ + // 0B 0C 10 11 12 15 19 1A 1B 1C 1D 21 22 23 24 25 26 2C 2D 2E + {0x06, 0x00, 0xf5, 0x75, 0x13, 0x14, 0x16, 0x6c, 0x03, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb1_2Fd5_2 + {0x06, 0x00, 0xf6, 0x75, 0x13, 0x14, 0x16, 0x6c, 0x03, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb2_4Fd5_2 + {0x06, 0x00, 0xc7, 0x75, 0x13, 0x37, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb4_8Fd25_4 + {0x06, 0x00, 0xc8, 0x84, 0x13, 0x33, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb10Fd19 + {0x06, 0x00, 0xca, 0x75, 0x13, 0x34, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb38_4Fd20 + {0x08, 0x00, 0x7b, 0x75, 0x13, 0x42, 0x1d, 0x1c, 0xc7, 0x00, 0xb2, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}, // GFSK_Rb76_8Fd32 + {0x08, 0x00, 0x5b, 0xf8, 0x13, 0x47, 0x1d, 0x1c, 0xc7, 0x00, 0xb2, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x81, 0x31, 0x09}, // GFSK_Rb100Fd47 + {0x0c, 0x00, 0x2d, 0x2f, 0x13, 0x62, 0x1d, 0x1c, 0xc7, 0x00, 0xb0, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x88, 0x31, 0x09}, // GFSK_Rb250Fd127 +}; + +// These power outputs are based on the suggested optimum values for +// multilayer inductors in the 915MHz frequency band. Per table 5-15. +// Yes these are not linear. +// Caution: this table is indexed by the values of enum TransmitPower +// Do not change one without changing the other. +// If you do not like these values, use setPaTable() directly. +PROGMEM static const uint8_t paPowerValues[] = +{ + 0x03, // -30dBm + 0x0e, // -20dBm + 0x1e, // -15dBm + 0x27, // -10dBm + 0x8e, // 0dBm + 0xcd, // 5dBm + 0xc7, // 7dBm + 0xc0, // 10dBm +}; + +RH_CC110::RH_CC110(uint8_t slaveSelectPin, uint8_t interruptPin, bool is27MHz, RHGenericSPI& spi) + : + RHNRFSPIDriver(slaveSelectPin, spi), + _rxBufValid(false), + _is27MHz(is27MHz) +{ + _interruptPin = interruptPin; + _myInterruptIndex = 0xff; // Not allocated yet +} + +bool RH_CC110::init() +{ + if (!RHNRFSPIDriver::init()) + return false; + + // 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 + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + + // Reset the chip + // Strobe the reset + uint8_t val = spiCommand(RH_CC110_STROBE_30_SRES); // Reset + delay(100); + val = spiCommand(RH_CC110_STROBE_36_SIDLE); // IDLE + if (val != 0x0f) + return false; // No chip there or reset failed. + + // Add by Adrien van den Bossche 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_CC110_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 + + spiWriteRegister(RH_CC110_REG_02_IOCFG0, RH_CC110_GDO_CFG_CRC_OK_AUTORESET); // gdo0 interrupt on CRC_OK + spiWriteRegister(RH_CC110_REG_06_PKTLEN, RH_CC110_MAX_PAYLOAD_LEN); // max packet length + spiWriteRegister(RH_CC110_REG_07_PKTCTRL1, RH_CC110_CRC_AUTOFLUSH); // no append status, crc autoflush, no addr check + spiWriteRegister(RH_CC110_REG_08_PKTCTRL0, RH_CC110_PKT_FORMAT_NORMAL | RH_CC110_CRC_EN | RH_CC110_LENGTH_CONFIG_VARIABLE); + spiWriteRegister(RH_CC110_REG_13_MDMCFG1, RH_CC110_NUM_PREAMBLE_4); // 4 preamble bytes, chan spacing not used + spiWriteRegister(RH_CC110_REG_17_MCSM1, RH_CC110_CCA_MODE_RSSI_PACKET | RH_CC110_RXOFF_MODE_RX | RH_CC110_TXOFF_MODE_IDLE); + spiWriteRegister(RH_CC110_REG_18_MCSM0, RH_CC110_FS_AUTOCAL_FROM_IDLE | RH_CC110_PO_TIMEOUT_64); // cal when going to tx or rx + spiWriteRegister(RH_CC110_REG_20_WORCTRL, 0xfb); // from smartrf + spiWriteRegister(RH_CC110_REG_29_FSTEST, 0x59); // from smartrf + spiWriteRegister(RH_CC110_REG_2A_PTEST, 0x7f); // from smartrf + spiWriteRegister(RH_CC110_REG_2B_AGCTEST, 0x3f); // from smartrf + + // Set some reasonable default values + uint8_t syncWords[] = { 0xd3, 0x91 }; + setSyncWords(syncWords, sizeof(syncWords)); + setTxPower(TransmitPower5dBm); + setFrequency(915.0); + setModemConfig(GFSK_Rb1_2Fd5_2); + return true; +} + +void RH_CC110::setIs27MHz(bool is27MHz) +{ + _is27MHz = is27MHz; +} + +// C++ level interrupt handler for this instance +// We use this to get RxDone and TxDone interrupts +void RH_CC110::handleInterrupt() +{ +// Serial.println("I"); + if (_mode == RHModeRx) + { + // Radio is configured to stay in RX until we move it to IDLE after a CRC_OK message for us + // We only get interrupts in RX mode, on CRC_OK + + uint8_t raw_rssi = spiBurstReadRegister(RH_CC110_REG_34_RSSI); // Was set when sync word was detected + // Conversion of RSSI value to received power level in dBm per TI section 5.18.2 + if (raw_rssi >= 128) + _lastRssi = (((int16_t)raw_rssi - 256) / 2) - 74; + else + _lastRssi = ((int16_t)raw_rssi / 2) - 74; + + _bufLen = spiReadRegister(RH_CC110_REG_3F_FIFO); + if (_bufLen < 4) + { + // Something wrong there, flush the FIFO + spiCommand(RH_CC110_STROBE_3A_SFRX); + clearRxBuf(); + return; + } + spiBurstRead(RH_CC110_REG_3F_FIFO | RH_CC110_SPI_BURST_MASK | RH_CC110_SPI_READ_MASK, _buf, _bufLen); + // All good so far. See if its for us + validateRxBuf(); + if (_rxBufValid) + setModeIdle(); // Done + } +} + +// These are low level functions that call the interrupt handler for the correct +// instance of RH_CC110. +// 3 interrupts allows us to have 3 different devices +void RH_INTERRUPT_ATTR RH_CC110::isr0() +{ + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_CC110::isr1() +{ + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_CC110::isr2() +{ + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +} + +uint8_t RH_CC110::spiReadRegister(uint8_t reg) +{ + return spiRead((reg & 0x3f) | RH_CC110_SPI_READ_MASK); +} + +uint8_t RH_CC110::spiBurstReadRegister(uint8_t reg) +{ + return spiRead((reg & 0x3f) | RH_CC110_SPI_READ_MASK | RH_CC110_SPI_BURST_MASK); +} + +uint8_t RH_CC110::spiWriteRegister(uint8_t reg, uint8_t val) +{ + return spiWrite((reg & 0x3f), val); +} + +uint8_t RH_CC110::spiBurstWriteRegister(uint8_t reg, const uint8_t* src, uint8_t len) +{ + return spiBurstWrite((reg & 0x3f) | RH_CC110_SPI_BURST_MASK, src, len); +} + +bool RH_CC110::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + uint8_t i; + for (i = 0; i <= 0x2f; i++) + { + Serial.print(i, HEX); + Serial.print(": "); + Serial.println(spiReadRegister(i), HEX); + } + // Burst registers + for (i = 0x30; i <= 0x3e; i++) + { + Serial.print(i, HEX); + Serial.print(": "); + Serial.println(spiBurstReadRegister(i), HEX); + } +#endif + return true; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_CC110::validateRxBuf() +{ + if (_bufLen < 4) + return; // Too short to be a real message + // Extract the 4 headers + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +bool RH_CC110::available() +{ + if (_mode == RHModeTx) + return false; + if (_rxBufValid) // Will be set by the interrupt handler when a good message is received + return true; + setModeRx(); // Make sure we are receiving + return false; // Nothing yet +} + +void RH_CC110::clearRxBuf() +{ + ATOMIC_BLOCK_START; + _rxBufValid = false; + _bufLen = 0; + ATOMIC_BLOCK_END; +} + +bool RH_CC110::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + + if (buf && len) + { + ATOMIC_BLOCK_START; + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen - RH_CC110_HEADER_LEN) + *len = _bufLen - RH_CC110_HEADER_LEN; + memcpy(buf, _buf + RH_CC110_HEADER_LEN, *len); + ATOMIC_BLOCK_END; + } + clearRxBuf(); // This message accepted and cleared + + return true; +} + +bool RH_CC110::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_CC110_MAX_MESSAGE_LEN) + return false; + + waitPacketSent(); // Make sure we dont interrupt an outgoing message + setModeIdle(); + + if (!waitCAD()) + return false; // Check channel activity + + spiWriteRegister(RH_CC110_REG_3F_FIFO, len + RH_CC110_HEADER_LEN); + spiWriteRegister(RH_CC110_REG_3F_FIFO,_txHeaderTo); + spiWriteRegister(RH_CC110_REG_3F_FIFO,_txHeaderFrom); + spiWriteRegister(RH_CC110_REG_3F_FIFO,_txHeaderId); + spiWriteRegister(RH_CC110_REG_3F_FIFO,_txHeaderFlags); + spiBurstWriteRegister(RH_CC110_REG_3F_FIFO, data, len); + + // Radio returns to Idle when TX is finished + // need waitPacketSent() to detect change of _mode and TX completion + setModeTx(); + + return true; +} + +uint8_t RH_CC110::maxMessageLength() +{ + return RH_CC110_MAX_MESSAGE_LEN; +} + +void RH_CC110::handleOverFlows(uint8_t status) +{ + spiCommand(RH_CC110_STROBE_3A_SFRX); + //Handle RX and TX overflows so we don't get stuck in either state + if( (status&RH_CC110_STATUS_RXFIFO_OVERFLOW) == RH_CC110_STATUS_RXFIFO_OVERFLOW ) { + spiCommand(RH_CC110_STROBE_3A_SFRX); + clearRxBuf(); + } + else if( (status&RH_CC110_STATUS_TXFIFO_UNDERFLOW) == RH_CC110_STATUS_TXFIFO_UNDERFLOW ) { + spiCommand(RH_CC110_STROBE_3B_SFTX); + } +} + +void RH_CC110::setModeIdle() +{ + if (_mode != RHModeIdle) + { + uint8_t status = spiCommand(RH_CC110_STROBE_36_SIDLE); + _mode = RHModeIdle; + handleOverFlows(status); + } +} + +bool RH_CC110::sleep() +{ + if (_mode != RHModeSleep) + { + spiCommand(RH_CC110_STROBE_36_SIDLE); //preceeding sleep IDLE first + spiCommand(RH_CC110_STROBE_39_SPWD); + _mode = RHModeSleep; + } + return true; +} + +void RH_CC110::setModeRx() +{ + if (_mode != RHModeRx) + { + // Radio is configuewd to stay in RX mode + // only receipt of a CRC_OK wil cause us to return it to IDLE + spiCommand(RH_CC110_STROBE_34_SRX); + _mode = RHModeRx; + } +} + +void RH_CC110::setModeTx() +{ + if (_mode != RHModeTx) + { + spiCommand(RH_CC110_STROBE_35_STX); + _mode = RHModeTx; + } +} + +uint8_t RH_CC110::statusRead() +{ + uint8_t status = spiCommand(RH_CC110_STROBE_3D_SNOP); + handleOverFlows(status); + return status; +} + +// Sigh, this chip has no TXDONE type interrupt, so we have to poll +bool RH_CC110::waitPacketSent() +{ + // If we are not currently in transmit mode, there is no packet to wait for + if (_mode != RHModeTx) + return false; + + // Caution: may transition through CALIBRATE + while ((statusRead() & RH_CC110_STATUS_STATE) != RH_CC110_STATUS_IDLE) + YIELD; + + _mode = RHModeIdle; + return true; +} + +bool RH_CC110::setTxPower(TransmitPower power) +{ + if (power > sizeof(paPowerValues)) + return false; + + uint8_t patable[2]; + memcpy_P(&patable[0], (void*)&paPowerValues[power], sizeof(uint8_t)); + patable[1] = 0x00; + setPaTable(patable, sizeof(patable)); + return true; +} + +void RH_CC110::setPaTable(uint8_t* patable, uint8_t patablesize) +{ + spiBurstWriteRegister(RH_CC110_REG_3E_PATABLE, patable, patablesize); +} + +bool RH_CC110::setFrequency(float centre) +{ + // From section 5.21: fcarrier = fxosc / 2^16 * FREQ + uint32_t FREQ; + float fxosc = _is27MHz ? 27.0 : 26.0; + FREQ = (uint32_t)(centre * 65536 / fxosc); + // Some trivial checks + if (FREQ & 0xff000000) + return false; + spiWriteRegister(RH_CC110_REG_0D_FREQ2, (FREQ >> 16) & 0xff); + spiWriteRegister(RH_CC110_REG_0E_FREQ1, (FREQ >> 8) & 0xff); + spiWriteRegister(RH_CC110_REG_0F_FREQ0, FREQ & 0xff); + + // Radio is configured to calibrate automatically whenever it enters RX or TX mode + // so no need to check for PLL lock here + return true; +} + +// Sets registers from a canned modem configuration structure +void RH_CC110::setModemRegisters(const ModemConfig* config) +{ + spiWriteRegister(RH_CC110_REG_0B_FSCTRL1, config->reg_0b); + spiWriteRegister(RH_CC110_REG_0C_FSCTRL0, config->reg_0c); + spiWriteRegister(RH_CC110_REG_10_MDMCFG4, config->reg_10); + spiWriteRegister(RH_CC110_REG_11_MDMCFG3, config->reg_11); + spiWriteRegister(RH_CC110_REG_12_MDMCFG2, config->reg_12); + spiWriteRegister(RH_CC110_REG_15_DEVIATN, config->reg_15); + spiWriteRegister(RH_CC110_REG_19_FOCCFG, config->reg_19); + spiWriteRegister(RH_CC110_REG_1A_BSCFG, config->reg_1a); + spiWriteRegister(RH_CC110_REG_1B_AGCCTRL2, config->reg_1b); + spiWriteRegister(RH_CC110_REG_1C_AGCCTRL1, config->reg_1c); + spiWriteRegister(RH_CC110_REG_1D_AGCCTRL0, config->reg_1d); + spiWriteRegister(RH_CC110_REG_21_FREND1, config->reg_21); + spiWriteRegister(RH_CC110_REG_22_FREND0, config->reg_22); + spiWriteRegister(RH_CC110_REG_23_FSCAL3, config->reg_23); + spiWriteRegister(RH_CC110_REG_24_FSCAL2, config->reg_24); + spiWriteRegister(RH_CC110_REG_25_FSCAL1, config->reg_25); + spiWriteRegister(RH_CC110_REG_26_FSCAL0, config->reg_26); + spiWriteRegister(RH_CC110_REG_2C_TEST2, config->reg_2c); + spiWriteRegister(RH_CC110_REG_2D_TEST1, config->reg_2d); + spiWriteRegister(RH_CC110_REG_2E_TEST0, config->reg_2e); +} + +// Set one of the canned Modem configs +// Returns true if its a valid choice +bool RH_CC110::setModemConfig(ModemConfigChoice index) +{ + if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE_27MHZ) / sizeof(ModemConfig))) + return false; + + const RH_CC110::ModemConfig *p = _is27MHz ? MODEM_CONFIG_TABLE_27MHZ : MODEM_CONFIG_TABLE_26MHZ ; + RH_CC110::ModemConfig cfg; + memcpy_P(&cfg, p + index, sizeof(RH_CC110::ModemConfig)); + setModemRegisters(&cfg); + + return true; +} + +void RH_CC110::setSyncWords(const uint8_t* syncWords, uint8_t len) +{ + if (!syncWords || len != 2) + return; // Only 2 byte sync words are supported + + spiWriteRegister(RH_CC110_REG_04_SYNC1, syncWords[0]); + spiWriteRegister(RH_CC110_REG_05_SYNC0, syncWords[1]); +} diff --git a/RH_CC110.h b/RH_CC110.h new file mode 100644 index 0000000..ed6f598 --- /dev/null +++ b/RH_CC110.h @@ -0,0 +1,895 @@ +// RH_CC110.h +// +// Definitions for Texas Instruments CC110L transceiver. +// http://www.ti.com/lit/ds/symlink/cc110l.pdf +// As used in Anaren CC110L Air Module BoosterPack +// https://www.anaren.com/air/cc110l-air-module-boosterpack-embedded-antenna-module-anaren +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2016 Mike McCauley +// $Id: RH_CC110.h,v 1.9 2020/01/05 07:02:23 mikem Exp $ +// + +#ifndef RH_CC110_h +#define RH_CC110_h + + +#include + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_CC110_NUM_INTERRUPTS 3 + +// Max number of octets the FIFO can hold +#define RH_CC110_FIFO_SIZE 64 + +// This is the maximum number of bytes that can be carried by the chip +// We use some for headers, keeping fewer for RadioHead messages +#define RH_CC110_MAX_PAYLOAD_LEN RH_CC110_FIFO_SIZE + +// The length of the headers we add. +// The headers are inside the chip payload +#define RH_CC110_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this driver. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 1 byte message length, 4 bytes headers, user data +#ifndef RH_CC110_MAX_MESSAGE_LEN + #define RH_CC110_MAX_MESSAGE_LEN (RH_CC110_MAX_PAYLOAD_LEN - RH_CC110_HEADER_LEN - 1) +#endif + +#define RH_CC110_SPI_READ_MASK 0x80 +#define RH_CC110_SPI_BURST_MASK 0x40 + +// Register definitions from Table 5-22 +#define RH_CC110_REG_00_IOCFG2 0x00 +#define RH_CC110_REG_01_IOCFG1 0x01 +#define RH_CC110_REG_02_IOCFG0 0x02 +#define RH_CC110_REG_03_FIFOTHR 0x03 +#define RH_CC110_REG_04_SYNC1 0x04 +#define RH_CC110_REG_05_SYNC0 0x05 +#define RH_CC110_REG_06_PKTLEN 0x06 +#define RH_CC110_REG_07_PKTCTRL1 0x07 +#define RH_CC110_REG_08_PKTCTRL0 0x08 +#define RH_CC110_REG_09_ADDR 0x09 +#define RH_CC110_REG_0A_CHANNR 0x0a +#define RH_CC110_REG_0B_FSCTRL1 0x0b +#define RH_CC110_REG_0C_FSCTRL0 0x0c +#define RH_CC110_REG_0D_FREQ2 0x0d +#define RH_CC110_REG_0E_FREQ1 0x0e +#define RH_CC110_REG_0F_FREQ0 0x0f +#define RH_CC110_REG_10_MDMCFG4 0x10 +#define RH_CC110_REG_11_MDMCFG3 0x11 +#define RH_CC110_REG_12_MDMCFG2 0x12 +#define RH_CC110_REG_13_MDMCFG1 0x13 +#define RH_CC110_REG_14_MDMCFG0 0x14 +#define RH_CC110_REG_15_DEVIATN 0x15 +#define RH_CC110_REG_16_MCSM2 0x16 +#define RH_CC110_REG_17_MCSM1 0x17 +#define RH_CC110_REG_18_MCSM0 0x18 +#define RH_CC110_REG_19_FOCCFG 0x19 +#define RH_CC110_REG_1A_BSCFG 0x1a +#define RH_CC110_REG_1B_AGCCTRL2 0x1b +#define RH_CC110_REG_1C_AGCCTRL1 0x1c +#define RH_CC110_REG_1D_AGCCTRL0 0x1d +#define RH_CC110_REG_1E_WOREVT1 0x1e +#define RH_CC110_REG_1F_WOREVT0 0x1f +#define RH_CC110_REG_20_WORCTRL 0x20 +#define RH_CC110_REG_21_FREND1 0x21 +#define RH_CC110_REG_22_FREND0 0x22 +#define RH_CC110_REG_23_FSCAL3 0x23 +#define RH_CC110_REG_24_FSCAL2 0x24 +#define RH_CC110_REG_25_FSCAL1 0x25 +#define RH_CC110_REG_26_FSCAL0 0x26 +#define RH_CC110_REG_27_RCCTRL1 0x28 +#define RH_CC110_REG_28_RCCTRL0 0x29 +#define RH_CC110_REG_29_FSTEST 0x2a +#define RH_CC110_REG_2A_PTEST 0x2b +#define RH_CC110_REG_2B_AGCTEST 0x2c +#define RH_CC110_REG_2C_TEST2 0x2c +#define RH_CC110_REG_2D_TEST1 0x2d +#define RH_CC110_REG_2E_TEST0 0x2e + +// Single byte read and write version of registers 0x30 to 0x3f. Strobes +// use spiCommand() +#define RH_CC110_STROBE_30_SRES 0x30 +#define RH_CC110_STROBE_31_SFSTXON 0x31 +#define RH_CC110_STROBE_32_SXOFF 0x32 +#define RH_CC110_STROBE_33_SCAL 0x33 +#define RH_CC110_STROBE_34_SRX 0x34 +#define RH_CC110_STROBE_35_STX 0x35 +#define RH_CC110_STROBE_36_SIDLE 0x36 + +#define RH_CC110_STROBE_39_SPWD 0x39 +#define RH_CC110_STROBE_3A_SFRX 0x3a +#define RH_CC110_STROBE_3B_SFTX 0x3b + +#define RH_CC110_STROBE_3D_SNOP 0x3d + + +// Burst read from these registers gives more data: +// use spiBurstReadRegister() +#define RH_CC110_REG_30_PARTNUM 0x30 +#define RH_CC110_REG_31_VERSION 0x31 +#define RH_CC110_REG_32_FREQEST 0x32 +#define RH_CC110_REG_33_CRC_REG 0x33 +#define RH_CC110_REG_34_RSSI 0x34 +#define RH_CC110_REG_35_MARCSTATE 0x35 + +#define RH_CC110_REG_38_PKTSTATUS 0x38 + +#define RH_CC110_REG_3A_TXBYTES 0x3a +#define RH_CC110_REG_3B_RXBYTES 0x3b + +// PATABLE, TXFIFO, RXFIFO also support burst +#define RH_CC110_REG_3E_PATABLE 0x3e +#define RH_CC110_REG_3F_FIFO 0x3f + +// Status Byte +#define RH_CC110_STATUS_CHIP_RDY 0x80 +#define RH_CC110_STATUS_STATE 0x70 +#define RH_CC110_STATUS_IDLE 0x00 +#define RH_CC110_STATUS_RX 0x10 +#define RH_CC110_STATUS_TX 0x20 +#define RH_CC110_STATUS_FSTXON 0x30 +#define RH_CC110_STATUS_CALIBRATE 0x40 +#define RH_CC110_STATUS_SETTLING 0x50 +#define RH_CC110_STATUS_RXFIFO_OVERFLOW 0x60 +#define RH_CC110_STATUS_TXFIFO_UNDERFLOW 0x70 +#define RH_CC110_STATUS_FIFOBYTES_AVAILABLE 0x0f + +// Register contents +// Chip Status Byte, read from header, data or command strobe +#define RH_CC110_CHIP_RDY 0x80 +#define RH_CC110_STATE 0x70 +#define RH_CC110_FIFO_BYTES_AVAILABLE 0x0f + +// Register bit field definitions +// #define RH_CC110_REG_00_IOCFG2 0x00 +// #define RH_CC110_REG_01_IOCFG1 0x01 +// #define RH_CC110_REG_02_IOCFG0 0x02 +#define RH_CC110_GDO_CFG_RX_FIFO_THR 0x00 +#define RH_CC110_GDO_CFG_RX_FIFO_FULL 0x01 +#define RH_CC110_GDO_CFG_TX_FIFO_THR 0x02 +#define RH_CC110_GDO_CFG_TX_FIFO_EMPTY 0x03 +#define RH_CC110_GDO_CFG_RX_FIFO_OVERFLOW 0x04 +#define RH_CC110_GDO_CFG_TX_FIFO_UNDEFLOOW 0x05 +#define RH_CC110_GDO_CFG_SYNC 0x06 +#define RH_CC110_GDO_CFG_CRC_OK_AUTORESET 0x07 +#define RH_CC110_GDO_CFG_CCA 0x09 +#define RH_CC110_GDO_CFG_LOCK_DETECT 0x0a +#define RH_CC110_GDO_CFG_SERIAL_CLOCK 0x0b +#define RH_CC110_GDO_CFG_SYNCHRONOUS_SDO 0x0c +#define RH_CC110_GDO_CFG_SDO 0x0d +#define RH_CC110_GDO_CFG_CARRIER 0x0e +#define RH_CC110_GDO_CFG_CRC_OK 0x0f +#define RH_CC110_GDO_CFG_PA_PD 0x1b +#define RH_CC110_GDO_CFG_LNA_PD 0x1c +#define RH_CC110_GDO_CFG_CLK_32K 0x27 +#define RH_CC110_GDO_CFG_CHIP_RDYN 0x29 +#define RH_CC110_GDO_CFG_XOSC_STABLE 0x2b +#define RH_CC110_GDO_CFG_HIGH_IMPEDANCE 0x2e +#define RH_CC110_GDO_CFG_0 0x2f +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_1 0x30 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_1_5 0x31 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_2 0x32 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_3 0x33 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_4 0x34 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_6 0x35 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_8 0x36 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_12 0x37 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_16 0x38 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_24 0x39 +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_32 0x3a +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_48 0x3b +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_64 0x3c +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_96 0x3d +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_128 0x3e +#define RH_CC110_GDO_CFG_CLK_XOSC_DIV_192 0x3f + +// #define RH_CC110_REG_03_FIFOTHR 0x03 +#define RH_CC110_ADC_RETENTION 0x80 + +#define RH_CC110_CLOSE_IN_RX 0x30 +#define RH_CC110_CLOSE_IN_RX_0DB 0x00 +#define RH_CC110_CLOSE_IN_RX_6DB 0x10 +#define RH_CC110_CLOSE_IN_RX_12DB 0x20 +#define RH_CC110_CLOSE_IN_RX_18DB 0x30 + +#define RH_CC110_FIFO_THR 0x0f + +// #define RH_CC110_REG_04_SYNC1 0x04 +// #define RH_CC110_REG_05_SYNC0 0x05 +// #define RH_CC110_REG_06_PKTLEN 0x06 +// #define RH_CC110_REG_07_PKTCTRL1 0x07 +#define RH_CC110_CRC_AUTOFLUSH 0x08 +#define RH_CC110_APPEND_STATUS 0x04 +#define RH_CC110_ADDR_CHK 0x03 +// can or the next 2: +#define RH_CC110_ADDR_CHK_ADDRESS 0x01 +#define RH_CC110_ADDR_CHK_BROADCAST 0x02 + + +// #define RH_CC110_REG_08_PKTCTRL0 0x08 +#define RH_CC110_PKT_FORMAT 0x30 +#define RH_CC110_PKT_FORMAT_NORMAL 0x00 +#define RH_CC110_PKT_FORMAT_SYNC_SERIAL 0x10 +#define RH_CC110_PKT_FORMAT_RANDOM_TX 0x20 +#define RH_CC110_PKT_FORMAT_ASYNC_SERIAL 0x30 + +#define RH_CC110_CRC_EN 0x04 + +#define RH_CC110_LENGTH_CONFIG 0x03 +#define RH_CC110_LENGTH_CONFIG_FIXED 0x00 +#define RH_CC110_LENGTH_CONFIG_VARIABLE 0x01 +#define RH_CC110_LENGTH_CONFIG_INFINITE 0x02 + +// #define RH_CC110_REG_09_ADDR 0x09 +// #define RH_CC110_REG_0A_CHANNR 0x0a +// #define RH_CC110_REG_0B_FSCTRL1 0x0b +// #define RH_CC110_REG_0C_FSCTRL0 0x0c +// #define RH_CC110_REG_0D_FREQ2 0x0d +// #define RH_CC110_REG_0E_FREQ1 0x0e +// #define RH_CC110_REG_0F_FREQ0 0x0f +// #define RH_CC110_REG_10_MDMCFG4 0x10 +#define RH_CC110_CHANBW_E 0xc0 +#define RH_CC110_CHANBW_M 0x30 +#define RH_CC110_DRATE_E 0x0f + +// #define RH_CC110_REG_11_MDMCFG3 0x11 +// #define RH_CC110_REG_12_MDMCFG2 0x12 +#define RH_CC110_DEM_DCFILT_OFF 0x80 +#define RH_CC110_MOD_FORMAT 0x70 +#define RH_CC110_MOD_FORMAT_2FSK 0x00 +#define RH_CC110_MOD_FORMAT_GFSK 0x10 +#define RH_CC110_MOD_FORMAT_OOK 0x30 +#define RH_CC110_MOD_FORMAT_4FSK 0x40 +#define RH_CC110_MANCHESTER_EN 0x08 +#define RH_CC110_SYNC_MODE 0x07 +#define RH_CC110_SYNC_MODE_NONE 0x00 +#define RH_CC110_SYNC_MODE_15_16 0x01 +#define RH_CC110_SYNC_MODE_16_16 0x02 +#define RH_CC110_SYNC_MODE_30_32 0x03 +#define RH_CC110_SYNC_MODE_NONE_CARRIER 0x04 +#define RH_CC110_SYNC_MODE_15_16_CARRIER 0x05 +#define RH_CC110_SYNC_MODE_16_16_CARRIER 0x06 +#define RH_CC110_SYNC_MODE_30_32_CARRIER 0x07 + +// #define RH_CC110_REG_13_MDMCFG1 0x13 +#define RH_CC110_NUM_PREAMBLE 0x70 +#define RH_CC110_NUM_PREAMBLE_2 0x00 +#define RH_CC110_NUM_PREAMBLE_3 0x10 +#define RH_CC110_NUM_PREAMBLE_4 0x20 +#define RH_CC110_NUM_PREAMBLE_6 0x30 +#define RH_CC110_NUM_PREAMBLE_8 0x40 +#define RH_CC110_NUM_PREAMBLE_12 0x50 +#define RH_CC110_NUM_PREAMBLE_16 0x60 +#define RH_CC110_NUM_PREAMBLE_24 0x70 + +#define RH_CC110_CHANSPC_E 0x03 + +// #define RH_CC110_REG_14_MDMCFG0 0x14 +// #define RH_CC110_REG_15_DEVIATN 0x15 +#define RH_CC110_DEVIATION_E 0x70 +#define RH_CC110_DEVIATION_M 0x07 + +// #define RH_CC110_REG_16_MCSM2 0x16 +#define RH_CC110_RX_TIME_RSSI 0x10 + +// #define RH_CC110_REG_17_MCSM1 0x17 +#define RH_CC110_CCA_MODE 0x30 +#define RH_CC110_CCA_MODE_ALWAYS 0x00 +#define RH_CC110_CCA_MODE_RSSI 0x10 +#define RH_CC110_CCA_MODE_PACKET 0x20 +#define RH_CC110_CCA_MODE_RSSI_PACKET 0x30 +#define RH_CC110_RXOFF_MODE 0x0c +#define RH_CC110_RXOFF_MODE_IDLE 0x00 +#define RH_CC110_RXOFF_MODE_FSTXON 0x04 +#define RH_CC110_RXOFF_MODE_TX 0x08 +#define RH_CC110_RXOFF_MODE_RX 0x0c +#define RH_CC110_TXOFF_MODE 0x03 +#define RH_CC110_TXOFF_MODE_IDLE 0x00 +#define RH_CC110_TXOFF_MODE_FSTXON 0x01 +#define RH_CC110_TXOFF_MODE_TX 0x02 +#define RH_CC110_TXOFF_MODE_RX 0x03 + +// #define RH_CC110_REG_18_MCSM0 0x18 +#define RH_CC110_FS_AUTOCAL 0x30 +#define RH_CC110_FS_AUTOCAL_NEVER 0x00 +#define RH_CC110_FS_AUTOCAL_FROM_IDLE 0x10 +#define RH_CC110_FS_AUTOCAL_TO_IDLE 0x20 +#define RH_CC110_FS_AUTOCAL_TO_IDLE_4 0x30 +#define RH_CC110_PO_TIMEOUT 0x0c +#define RH_CC110_PO_TIMEOUT_1 0x00 +#define RH_CC110_PO_TIMEOUT_16 0x04 +#define RH_CC110_PO_TIMEOUT_64 0x08 +#define RH_CC110_PO_TIMEOUT_256 0x0c +#define RH_CC110_XOSC_FORCE_ON 0x01 + +// #define RH_CC110_REG_19_FOCCFG 0x19 +#define RH_CC110_FOC_BS_CS_GATE 0x20 +#define RH_CC110_FOC_PRE_K 0x18 +#define RH_CC110_FOC_PRE_K_0 0x00 +#define RH_CC110_FOC_PRE_K_1 0x08 +#define RH_CC110_FOC_PRE_K_2 0x10 +#define RH_CC110_FOC_PRE_K_3 0x18 +#define RH_CC110_FOC_POST_K 0x04 +#define RH_CC110_FOC_LIMIT 0x03 +#define RH_CC110_FOC_LIMIT_0 0x00 +#define RH_CC110_FOC_LIMIT_8 0x01 +#define RH_CC110_FOC_LIMIT_4 0x02 +#define RH_CC110_FOC_LIMIT_2 0x03 + +// #define RH_CC110_REG_1A_BSCFG 0x1a +#define RH_CC110_BS_PRE_K 0xc0 +#define RH_CC110_BS_PRE_K_1 0x00 +#define RH_CC110_BS_PRE_K_2 0x40 +#define RH_CC110_BS_PRE_K_3 0x80 +#define RH_CC110_BS_PRE_K_4 0xc0 +#define RH_CC110_BS_PRE_KP 0x30 +#define RH_CC110_BS_PRE_KP_1 0x00 +#define RH_CC110_BS_PRE_KP_2 0x10 +#define RH_CC110_BS_PRE_KP_3 0x20 +#define RH_CC110_BS_PRE_KP_4 0x30 +#define RH_CC110_BS_POST_KI 0x08 +#define RH_CC110_BS_POST_KP 0x04 +#define RH_CC110_BS_LIMIT 0x03 +#define RH_CC110_BS_LIMIT_0 0x00 +#define RH_CC110_BS_LIMIT_3 0x01 +#define RH_CC110_BS_LIMIT_6 0x02 +#define RH_CC110_BS_LIMIT_12 0x03 + +// #define RH_CC110_REG_1B_AGCCTRL2 0x1b +#define RH_CC110_MAX_DVA_GAIN 0xc0 +#define RH_CC110_MAX_DVA_GAIN_ALL 0x00 +#define RH_CC110_MAX_DVA_GAIN_ALL_LESS_1 0x40 +#define RH_CC110_MAX_DVA_GAIN_ALL_LESS_2 0x80 +#define RH_CC110_MAX_DVA_GAIN_ALL_LESS_3 0xc0 +#define RH_CC110_MAX_LNA_GAIN 0x38 + +#define RH_CC110_MAGN_TARGET 0x07 +#define RH_CC110_MAGN_TARGET_24DB 0x00 +#define RH_CC110_MAGN_TARGET_27DB 0x01 +#define RH_CC110_MAGN_TARGET_30DB 0x02 +#define RH_CC110_MAGN_TARGET_33DB 0x03 +#define RH_CC110_MAGN_TARGET_36DB 0x04 +#define RH_CC110_MAGN_TARGET_38DB 0x05 +#define RH_CC110_MAGN_TARGET_40DB 0x06 +#define RH_CC110_MAGN_TARGET_42DB 0x07 + +// #define RH_CC110_REG_1C_AGCCTRL1 0x1c +#define RH_CC110_AGC_LNA_PRIORITY 0x40 +#define RH_CC110_CARRIER_SENSE_REL_THR 0x30 +#define RH_CC110_CARRIER_SENSE_REL_THR_0DB 0x00 +#define RH_CC110_CARRIER_SENSE_REL_THR_6DB 0x10 +#define RH_CC110_CARRIER_SENSE_REL_THR_10DB 0x20 +#define RH_CC110_CARRIER_SENSE_REL_THR_14DB 0x30 +#define RH_CC110_CARRIER_SENSE_ABS_THR 0x0f + +// #define RH_CC110_REG_1D_AGCCTRL0 0x1d +#define RH_CC110_HYST_LEVEL 0xc0 +#define RH_CC110_HYST_LEVEL_NONE 0x00 +#define RH_CC110_HYST_LEVEL_LOW 0x40 +#define RH_CC110_HYST_LEVEL_MEDIUM 0x80 +#define RH_CC110_HYST_LEVEL_HIGH 0xc0 +#define RH_CC110_WAIT_TIME 0x30 +#define RH_CC110_WAIT_TIME_8 0x00 +#define RH_CC110_WAIT_TIME_16 0x10 +#define RH_CC110_WAIT_TIME_24 0x20 +#define RH_CC110_WAIT_TIME_32 0x30 +#define RH_CC110_AGC_FREEZE 0x0c +#define RH_CC110_AGC_FILTER_LENGTH 0x03 +#define RH_CC110_AGC_FILTER_LENGTH_8 0x00 +#define RH_CC110_AGC_FILTER_LENGTH_16 0x01 +#define RH_CC110_AGC_FILTER_LENGTH_32 0x02 +#define RH_CC110_AGC_FILTER_LENGTH_64 0x03 + +// #define RH_CC110_REG_1E_WOREVT1 0x1e +// #define RH_CC110_REG_1F_WOREVT0 0x1f +// #define RH_CC110_REG_20_WORCTRL 0x20 +// #define RH_CC110_REG_21_FREND1 0x21 +#define RH_CC110_LNA_CURRENT 0xc0 +#define RH_CC110_LNA2MIX_CURRENT 0x30 +#define RH_CC110_LODIV_BUF_CURRENT_RX 0x0c +#define RH_CC110_MIX_CURRENT 0x03 + +// #define RH_CC110_REG_22_FREND0 0x22 +#define RH_CC110_LODIV_BUF_CURRENT_TX 0x30 +#define RH_CC110_PA_POWER 0x07 + +// #define RH_CC110_REG_23_FSCAL3 0x23 +#define RH_CC110_FSCAL3_7_6 0xc0 +#define RH_CC110_CHP_CURR_CAL_EN 0x30 +#define RH_CC110_FSCAL3_3_0 0x0f + +// #define RH_CC110_REG_24_FSCAL2 0x24 +#define RH_CC110_VCO_CORE_H_EN 0x20 +#define RH_CC110_FSCAL2 0x1f + +// #define RH_CC110_REG_25_FSCAL1 0x25 +#define RH_CC110_FSCAL1 0x3f + +// #define RH_CC110_REG_26_FSCAL0 0x26 +#define RH_CC110_FSCAL0 0x7f + +// #define RH_CC110_REG_27_RCCTRL1 0x28 +// #define RH_CC110_REG_28_RCCTRL0 0x29 +// #define RH_CC110_REG_29_FSTEST 0x2a +// #define RH_CC110_REG_2A_PTEST 0x2b +// #define RH_CC110_REG_2B_AGCTEST 0x2c +// #define RH_CC110_REG_2C_TEST2 0x2c +// #define RH_CC110_REG_2D_TEST1 0x2d +// #define RH_CC110_REG_2E_TEST0 0x2e +#define RH_CC110_TEST0_7_2 0xfc +#define RH_CC110_VCO_SEL_CAL_EN 0x02 +#define RH_CC110_TEST0_0 0x01 + +// #define RH_CC110_REG_30_PARTNUM 0x30 +// #define RH_CC110_REG_31_VERSION 0x31 +// #define RH_CC110_REG_32_FREQEST 0x32 +// #define RH_CC110_REG_33_CRC_REG 0x33 +#define RH_CC110_CRC_REG_CRC_OK 0x80 + +// #define RH_CC110_REG_34_RSSI 0x34 +// #define RH_CC110_REG_35_MARCSTATE 0x35 +#define RH_CC110_MARC_STATE 0x1f +#define RH_CC110_MARC_STATE_SLEEP 0x00 +#define RH_CC110_MARC_STATE_IDLE 0x01 +#define RH_CC110_MARC_STATE_XOFF 0x02 +#define RH_CC110_MARC_STATE_VCOON_MC 0x03 +#define RH_CC110_MARC_STATE_REGON_MC 0x04 +#define RH_CC110_MARC_STATE_MANCAL 0x05 +#define RH_CC110_MARC_STATE_VCOON 0x06 +#define RH_CC110_MARC_STATE_REGON 0x07 +#define RH_CC110_MARC_STATE_STARTCAL 0x08 +#define RH_CC110_MARC_STATE_BWBOOST 0x09 +#define RH_CC110_MARC_STATE_FS_LOCK 0x0a +#define RH_CC110_MARC_STATE_IFADCON 0x0b +#define RH_CC110_MARC_STATE_ENDCAL 0x0c +#define RH_CC110_MARC_STATE_RX 0x0d +#define RH_CC110_MARC_STATE_RX_END 0x0e +#define RH_CC110_MARC_STATE_RX_RST 0x0f +#define RH_CC110_MARC_STATE_TXRX_SWITCH 0x10 +#define RH_CC110_MARC_STATE_RXFIFO_OVERFLOW 0x11 +#define RH_CC110_MARC_STATE_FSTXON 0x12 +#define RH_CC110_MARC_STATE_TX 0x13 +#define RH_CC110_MARC_STATE_TX_END 0x14 +#define RH_CC110_MARC_STATE_RXTX_SWITCH 0x15 +#define RH_CC110_MARC_STATE_TXFIFO_UNDERFLOW 0x16 + +// #define RH_CC110_REG_38_PKTSTATUS 0x38 +#define RH_CC110_PKTSTATUS_CRC_OK 0x80 +#define RH_CC110_PKTSTATUS_CS 0x40 +#define RH_CC110_PKTSTATUS_CCA 0x10 +#define RH_CC110_PKTSTATUS_SFD 0x08 +#define RH_CC110_PKTSTATUS_GDO2 0x04 +#define RH_CC110_PKTSTATUS_GDO0 0x01 + +// #define RH_CC110_REG_3A_TXBYTES 0x3a +#define RH_CC110_TXFIFO_UNDERFLOW 0x80 +#define RH_CC110_NUM_TXBYTES 0x7f + +// #define RH_CC110_REG_3B_RXBYTES 0x3b +#define RH_CC110_RXFIFO_UNDERFLOW 0x80 +#define RH_CC110_NUM_RXBYTES 0x7f + +///////////////////////////////////////////////////////////////////// +/// \class RH_CC110 RH_CC110.h +/// \brief Send and receive unaddressed, unreliable, datagrams by Texas Instruments CC110L and compatible transceivers and modules. +/// +/// The TI CC110L is a low cost tranceiver chip capable of 300 to 928MHz and with a wide range of modulation types and speeds. +/// The chip is typically provided on a module that also includes the antenna and coupling hardware +/// and is therefore capable of a more restricted frequency range. +/// +/// Supported modules include: +/// - Anaren AIR BoosterPack 430BOOST-CC110L +/// +/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams +/// of arbitrary length to 59 octets per packet at a selected data rate and modulation type. +/// Use one of the Manager classes to get addressing and +/// acknowledgement reliability, routing, meshes etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// data rate, and with identical network addresses. +/// +/// Several CC110L modules can be connected to an Arduino, permitting the construction of translators +/// and frequency changers, etc. +/// +/// Several GFSK modulation schemes are provided and may be selected by calling setModemConfig(). No FSK or OOK +/// modulation schemes are provided though the implementor may configure the mnodem characteristics directly +/// by calling setModemRegisters(). +/// +/// Implementation based on: +/// http://www.ti.com/lit/ds/symlink/cc110l.pdf +/// and +/// https://www.anaren.com/air/cc110l-air-module-boosterpack-embedded-antenna-module-anaren +/// +/// \par Crystal Frequency +/// +/// Modules based on the CC110L may contain a crystal oscillator with one of 2 possible frequencies: 26MHz or 27MHz. +/// A number of radio configuration parameters (including carrier frequency and data rates) depend on the +/// crystal oscillator frequency. The chip has no knowledge of the frequency, so it is up to the implementer +/// to tell the driver the oscillator frequency by passing in the appropriate value of is27MHz to the constructor (default 26MHz) +/// or by calling setIs27MHz() before calling init(). +/// Failure to correctly set this flag will cause incorrect frequency and modulation +/// characteristics to be used. +/// +/// Caution: it is not easy to determine what the actual crystal frequency is on some modules. For example, +/// the documentation for the Anaren BoosterPack indictes a 26MHz crystal, but measurements on the devices delivered here +/// indicate a 27MHz crystal is actually installed. TI recommend 27MHz for +/// +/// \par Packet Format +/// +/// - 2 octets sync (a configurable network address) +/// - 1 octet message length +/// - 4 to 63 octets of payload consisting of: +/// - 1 octet TO header +/// - 1 octet FROM header +/// - 1 octet ID header +/// - 1 octet FLAGS header +/// - 0 to 59 octets of user message +/// - 2 octets CRC +/// +/// \par Connecting CC110L to Arduino +/// +/// Warning: the CC110L is a 3.3V part, and exposing it to 5V on any pin will damage it. Ensure you are using a 3.3V +/// MCU or use level shifters. We tested with Teensy 3.1. +/// +/// The electrical connection between a CC110L module and the Arduino or other processor +/// require 3.3V, the 3 x SPI pins (SCK, SDI, SDO), +/// a Chip Select pin and an Interrupt pin. +/// Examples below assume the Anaren BoosterPack. Caution: the pin numbering on the Anaren BoosterPack +/// is a bit counter-intuitive: the direction of number on J1 is the reverse of J2. Check the pin numbers +/// stenciled on the front of the board to be sure. +/// +/// \code +/// Teensy 3.1 CC110L pin name Anaren BoosterPack pin +/// 3.3V---------VDD (3.3V in) J1-1 +/// SS pin D10----------CSn (chip select in) J2-8 +/// SCK pin D13----------SCLK (SPI clock in) J1-7 +/// MOSI pin D11----------MOSI (SPI data in) J2-5 +/// MISO pin D12----------MISO (SPI data out) J2-4 +/// D2-----------GDO0 (Interrupt output) J2-9 +/// GND----------GND (ground in) J2-10 +/// \endcode +/// and use the default RH_CC110 constructor. You can use other pins by passing the appropriate arguments +/// to the RH_CC110 constructor, depending on what your MCU supports. +/// +/// For the Particle Photon: +/// \code +/// Photon CC110L pin name Anaren BoosterPack pin +/// 3.3V---------VDD (3.3V in) J1-1 +/// SS pin A2-----------CSn (chip select in) J2-8 +/// SCK pin A3-----------SCLK (SPI clock in) J1-7 +/// MOSI pin A5-----------MOSI (SPI data in) J2-5 +/// MISO pin A4-----------MISO (SPI data out) J2-4 +/// D2-----------GDO0 (Interrupt output) J2-9 +/// GND----------GND (ground in) J2-10 +/// \endcode +/// and use the default RH_CC110 constructor. You can use other pins by passing the appropriate arguments +/// to the RH_CC110 constructor, depending on what your MCU supports. +/// +/// \par Example programs +/// +/// Several example programs are provided. +/// +/// \par Radio operating strategy and defaults +/// +/// The radio is enabled at all times and switched between RX, TX and IDLE modes. +/// When RX is enabled (by calling available() or setModeRx()) the radio will stay in RX mode until a +/// valid CRC correct message addressed to thiis node is received, when it will transition to IDLE. +/// When TX is enabled (by calling send()) it will stay in TX mode until the message has ben sent +/// and waitPacketSent() is called when it wil transition to IDLE +///(this radio has no 'packet sent' interrupt that could be used, so polling +/// with waitPacketSent() is required +/// +/// The modulation schemes supported include the GFSK schemes provided by default in the TI SmartRF Suite. +/// This software allows you to get the correct register values for diferent modulation schemes. All the modulation +/// schemes prvided in the driver are based on the recommended register values given by SmartRF. +/// Other schemes such a 2-FSK, 4-FSK and OOK are suported by the chip, but canned configurations are not provided with this driver. +/// The implementer may choose to create their own modem configurations and pass them to setModemRegisters(). +/// +class RH_CC110 : public RHNRFSPIDriver +{ +public: + + /// \brief Defines register configuration values for a desired modulation + /// + /// Defines values for various configuration fields and registers to + /// achieve a desired modulation speed and frequency deviation. + typedef struct + { + uint8_t reg_0b; ///< RH_CC110_REG_0B_FSCTRL1 + uint8_t reg_0c; ///< RH_CC110_REG_0C_FSCTRL0 + uint8_t reg_10; ///< RH_CC110_REG_10_MDMCFG4 + uint8_t reg_11; ///< RH_CC110_REG_11_MDMCFG3 + uint8_t reg_12; ///< RH_CC110_REG_12_MDMCFG2 + uint8_t reg_15; ///< RH_CC110_REG_15_DEVIATN + uint8_t reg_19; ///< RH_CC110_REG_19_FOCCFG + uint8_t reg_1a; ///< RH_CC110_REG_1A_BSCFG + uint8_t reg_1b; ///< RH_CC110_REG_1B_AGCCTRL2 + uint8_t reg_1c; ///< RH_CC110_REG_1C_AGCCTRL1 + uint8_t reg_1d; ///< RH_CC110_REG_1D_AGCCTRL0 + uint8_t reg_21; ///< RH_CC110_REG_21_FREND1 + uint8_t reg_22; ///< RH_CC110_REG_22_FREND0 + uint8_t reg_23; ///< RH_CC110_REG_23_FSCAL3 + uint8_t reg_24; ///< RH_CC110_REG_24_FSCAL2 + uint8_t reg_25; ///< RH_CC110_REG_25_FSCAL1 + uint8_t reg_26; ///< RH_CC110_REG_26_FSCAL0 + uint8_t reg_2c; ///< RH_CC110_REG_2C_TEST2 + uint8_t reg_2d; ///< RH_CC110_REG_2D_TEST1 + uint8_t reg_2e; ///< RH_CC110_REG_2E_TEST0 + } ModemConfig; + + + /// Choices for setModemConfig() for a selected subset of common modulation types, + /// and data rates. If you need another configuration, use the register calculator. + /// and call setModemRegisters() with your desired settings. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + /// All configs use SYNC_MODE = RH_CC110_SYNC_MODE_16_16 (2 byte sync) + typedef enum + { + GFSK_Rb1_2Fd5_2 = 0, ///< GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity + GFSK_Rb2_4Fd5_2, ///< GFSK, Data Rate: 2.4kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity + GFSK_Rb4_8Fd25_4, ///< GFSK, Data Rate: 4.8kBaud, Dev: 25.4kHz, RX BW 100kHz, optimised for sensitivity + GFSK_Rb10Fd19, ///< GFSK, Data Rate: 10kBaud, Dev: 19kHz, RX BW 100kHz, optimised for sensitivity + GFSK_Rb38_4Fd20, ///< GFSK, Data Rate: 38.4kBaud, Dev: 20kHz, RX BW 100kHz, optimised for sensitivity + GFSK_Rb76_8Fd32, ///< GFSK, Data Rate: 76.8kBaud, Dev: 32kHz, RX BW 232kHz, optimised for sensitivity + GFSK_Rb100Fd47, ///< GFSK, Data Rate: 100kBaud, Dev: 47kHz, RX BW 325kHz, optimised for sensitivity + GFSK_Rb250Fd127, ///< GFSK, Data Rate: 250kBaud, Dev: 127kHz, RX BW 540kHz, optimised for sensitivity + } ModemConfigChoice; + + /// These power outputs are based on the suggested optimum values for + /// multilayer inductors in the 915MHz frequency band. Per table 5-15. + /// Caution: these enum values are indexes into PaPowerValues. + /// Do not change one without changing the other. Use the symbolic names, not the integer values + typedef enum + { + TransmitPowerM30dBm = 0, ///< -30dBm + TransmitPowerM20dBm, ///< -20dBm + TransmitPowerM15dBm, ///< -15dBm + TransmitPowerM10dBm, ///< -10dBm + TransmitPower0dBm, ///< 0dBm + TransmitPower5dBm, ///< 5dBm + TransmitPower7dBm, ///< 7dBm + TransmitPower10dBm, ///< 10dBm + } TransmitPower; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the CC110L before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the CC110L GDO0 interrupt line. + /// Defaults to pin 2. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param[in] is27MHz Set to true if your CC110 is equipped with a 27MHz crystal oscillator. Defaults to false. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_CC110(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, bool is27MHz = false, RHGenericSPI& spi = hardware_spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// In particular, ensure you have called setIs27MHz(true) if your module has a 27MHz crystal oscillator. + /// After init(), the following default characteristics are set: + /// TxPower: TransmitPower5dBm + /// Frequency: 915.0 + /// Modulation: GFSK_Rb1_2Fd5_2 (GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity) + /// Sync Words: 0xd3, 0x91 + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Prints the value of all chip registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegisters(); + + /// Blocks until the current message (if any) + /// has been transmitted + /// \return true on success, false if the chip is not in transmit mode or other transmit failure + virtual bool waitPacketSent(); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it will be returned to RHModeIdle + /// and available() will return true. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Turns the receiver on if it not already on (after wiaint gor any currenly transmitting message to complete). + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf. The message cannot be retreived again. + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send + /// \return true if the message length was valid and it was correctly queued for transmit + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// If current mode is Sleep, Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver. The radio will stay in Rx mode until a CRC correct message addressed to this node + /// is received, or the ode is changed to Tx, Idle or Sleep. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Tx. + /// Starts the transmitter sending the current message. + void setModeTx(); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode to idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// Caution: waking up from sleep loses values from registers 0x29 through 0x2e + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + + /// Set the Power Amplifier power setting. + /// The PaTable settings are based on are based on the suggested optimum values for + /// multilayer inductors in the 915MHz frequency band. Per table 5-15. + /// If these values are not suitable, use setPaTable() directly. + /// Caution: be a good neighbour and use the lowest power setting compatible with your application. + /// Caution: Permissable power settings for your area may depend on frequency and modulation characteristics: + /// consult local authorities. + /// param[in] power One of TransmitPower enum values + bool setTxPower(TransmitPower power); + + /// Indicates the presence of 27MHz crystal oscillator. + /// You must indicate to the driver if your CC110L is equipped with a 27MHz crystal oscillator (26MHz is the default + /// in the constructor). + /// This should be called before calling init() if you have a 27MHz crystal. + /// It can be called after calling init() but you must reset the frequency (with setFrequency()) and modulation + /// (with setModemConfig()) afterwards. + /// \param[in] is27MHz Pass true if the CC110L has a 27MHz crystal (default is true). + void setIs27MHz(bool is27MHz = true); + + /// Sets the transmitter and receiver + /// centre frequency. + /// Caution: permissable frequency bands will depend on you country and area: consult local authorities. + /// \param[in] centre Frequency in MHz. 300.0 to 928.0 + /// \return true if the selected frquency centre is within range + bool setFrequency(float centre); + + /// Sets all the registers required to configure the data modem in the CC110, including the data rate, + /// bandwidths etc. You cas use this to configure the modem with custom configuraitons if none of the + /// canned configurations in ModemConfigChoice suit you. + /// \param[in] config A ModemConfig structure containing values for the modem configuration registers. + void setModemRegisters(const ModemConfig* config); + + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Sets the sync words for transmit and receive in registers RH_CC110_REG_04_SYNC1 and RH_CC110_REG_05_SYNC0. + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0xd3, 0x91 }. + /// \param[in] syncWords Array of sync words, 2 octets long + /// \param[in] len Number of sync words to set. MUST be 2. + void setSyncWords(const uint8_t* syncWords, uint8_t len); + + /// Sets the PaTable registers directly. + /// Ensure you use suitable PATABLE values per Tbale 5-15 or 5-16 + /// You may need to do this to implement an OOK modulation scheme. + void setPaTable(uint8_t* patable, uint8_t patablesize); + +protected: + /// This is a low level function to handle the interrupts for one instance of RH_RF95. + /// Called automatically by isr*() + /// Should not need to be called by user code. + void handleInterrupt(); + + /// Reads a single register from the CC110L + /// \param[in] reg Register number, one of RH_CC110_REG + /// \return The value of the register + uint8_t spiReadRegister(uint8_t reg); + + /// Reads a single register in burst mode. + /// On the CC110L, some registers yield different data when read in burst mode + /// as opposed to single byte mode. + /// \param[in] reg Register number, one of RH_CC110_REG (burst mode readable) + /// \return The value of the register after a burst read + uint8_t spiBurstReadRegister(uint8_t reg); + + /// Writes to a single single register on the CC110L + /// \param[in] reg Register number, one of RH_CC110L_REG_* + /// \param[in] val The value to write + /// \return returns the chip status byte per table 5.2 + uint8_t spiWriteRegister(uint8_t reg, uint8_t val); + + /// Write a number of bytes to a burst capable register + /// \param[in] reg Register number of the first register, one of RH_CC110L_REG_* + /// \param[in] src Array of new register values to write. Must be at least len bytes + /// \param[in] len Number of bytes to write + /// \return the chip status byte per table 5.2 + uint8_t spiBurstWriteRegister(uint8_t reg, const uint8_t* src, uint8_t len); + + /// Examine the receive buffer to determine whether the message is for this node + /// Sets _rxBufValid. + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + + /// Reads and returns the status byte by issuing the SNOP strobe + /// \return The value of the status byte per Table 5-2 + uint8_t statusRead(); + + /// Handle the TX or RX overflow state of the given status + /// \param status The status byte read from the last SPI command + /// \return void + void handleOverFlows(uint8_t status); + + +private: + /// Low level interrupt service routine for device connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_CC110* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// Number of octets in the buffer + volatile uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_CC110_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + volatile bool _rxBufValid; + + /// True if crystal oscillator is 26 MHz, not 26MHz. + bool _is27MHz; +}; + +/// @example cc110_client.pde +/// @example cc110_server.pde + +#endif diff --git a/RH_E32.cpp b/RH_E32.cpp new file mode 100644 index 0000000..a0d974c --- /dev/null +++ b/RH_E32.cpp @@ -0,0 +1,347 @@ +// RH_E32.cpp +// +// Copyright (C) 2017 Mike McCauley +// $Id: RH_E32.cpp,v 1.6 2020/01/07 23:35:02 mikem Exp $ + +#include +#ifdef RH_HAVE_SERIAL // No serial + +#include +#include + +RH_E32::RH_E32(Stream *s, uint8_t m0_pin, uint8_t m1_pin, uint8_t aux_pin) + : + _s(s), + _m0_pin(m0_pin), + _m1_pin(m1_pin), + _aux_pin(aux_pin) +{ + // Prevent glitches at startup + pinMode(_aux_pin, INPUT); + digitalWrite(_m0_pin, HIGH); + digitalWrite(_m1_pin, HIGH); + pinMode(_m0_pin, OUTPUT); + pinMode(_m1_pin, OUTPUT); +} + +bool RH_E32::init() +{ + // When a message is available, Aux will go low 5 msec before the first character is output + // So if we ever wait more than this period of time after Aux low, can conclude there will be no data + _s->setTimeout(10); + + // Wait until the module is connected + waitAuxHigh(); + + if (!getVersion()) + return false; // Could not communicate with module or wrong type of module + + setMode(RHModeRx); + clearRxBuf(); + + if (!setDataRate(DataRate5kbps)) + return false; + + if (!setPower(Power21dBm)) + return false; + + // if (!setBaudRate(BaudRate9600, Parity8N1)) + // return false; + + if (!setFrequency(433)) + return false; + + return true; +} + +bool RH_E32::reset() +{ + setOperatingMode(ModeSleep); + uint8_t resetCommand[] = { RH_E32_COMMAND_RESET, RH_E32_COMMAND_RESET, RH_E32_COMMAND_RESET }; + size_t result = _s->write(resetCommand, sizeof(resetCommand)); + setOperatingMode(ModeNormal); + return (result == sizeof(resetCommand)); +} + +bool RH_E32::readParameters(Parameters& params) +{ + setOperatingMode(ModeSleep); + uint8_t readParamsCommand[] = { RH_E32_COMMAND_READ_PARAMS, RH_E32_COMMAND_READ_PARAMS, RH_E32_COMMAND_READ_PARAMS }; + _s->write(readParamsCommand, sizeof(readParamsCommand)); + size_t result = _s->readBytes((char*)¶ms, sizeof(params)); // default 1 sec timeout + setOperatingMode(ModeNormal); + return (result == sizeof(Parameters)); +} + +bool RH_E32::writeParameters(Parameters& params, bool save) +{ + setOperatingMode(ModeSleep); + params.head = save ? RH_E32_COMMAND_WRITE_PARAMS_SAVE : RH_E32_COMMAND_WRITE_PARAMS_NOSAVE; + // printBuffer("writing now", (uint8_t*)¶ms, sizeof(params)); + size_t result = _s->write((uint8_t*)¶ms, sizeof(params)); + if (result != sizeof(params)) + return false; + + // Now we expect to get the same data back + result = _s->readBytes((char*)¶ms, sizeof(params)); + if (result != sizeof(params)) + return false; + // printBuffer("additional read", (uint8_t*)¶ms, sizeof(params)); + // Without a little delay here, writing params often fails + delay(20); + + setOperatingMode(ModeNormal); + return result == sizeof(params); +} + +void RH_E32::setOperatingMode(OperatingMode mode) +{ + waitAuxHigh(); + switch (mode) + { + case ModeNormal: + digitalWrite(_m0_pin, LOW); + digitalWrite(_m1_pin, LOW); + break; + + case ModeWakeUp: + digitalWrite(_m0_pin, HIGH); + digitalWrite(_m1_pin, LOW); + break; + + case ModePowerSaving: + digitalWrite(_m0_pin, LOW); + digitalWrite(_m1_pin, HIGH); + break; + + case ModeSleep: + digitalWrite(_m0_pin, HIGH); + digitalWrite(_m1_pin, HIGH); + break; + + } + delay(10); // Takes a little while to start its response + waitAuxHigh(); +} + +bool RH_E32::getVersion() +{ + setOperatingMode(ModeSleep); + uint8_t readVersionCommand[] = { RH_E32_COMMAND_READ_VERSION, RH_E32_COMMAND_READ_VERSION, RH_E32_COMMAND_READ_VERSION }; + _s->write(readVersionCommand, sizeof(readVersionCommand)); + uint8_t version[4]; + size_t result = _s->readBytes((char *)version, sizeof(version)); // default 1 sec timeout + setOperatingMode(ModeNormal); + if (result == 4) + { + // Successful read + // printBuffer("read version", version, sizeof(version)); + if (version[0] != 0xc3 || version [1] != 0x32) + { + // Not an E32 + return false; + } + else + { + // REVISIT: do something with it? + } + } + else + { + // Read failed: no module? Wrong baud? + return false; + } + return true; +} + +void RH_E32::waitAuxHigh() +{ + // REVISIT: timeout needed? + while (digitalRead(_aux_pin) == false) + ; +} + +void RH_E32::waitAuxLow() +{ + while (digitalRead(_aux_pin) == true) + ; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_E32::validateRxBuf() +{ + if (_bufLen < RH_E32_HEADER_LEN) + return; // Too short to be a real message + if (_bufLen != _buf[0]) + return; // Do we have all the message? + + // Extract the 4 headers + _rxHeaderTo = _buf[1]; + _rxHeaderFrom = _buf[2]; + _rxHeaderId = _buf[3]; + _rxHeaderFlags = _buf[4]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +void RH_E32::clearRxBuf() +{ + _rxBufValid = false; + _bufLen = 0; +} + +bool RH_E32::available() +{ + // Caution: long packets could be sent in several bursts + if (!_rxBufValid) + { + if (_mode == RHModeTx) + return false; + + if (!_s->available()) + return false; + + // Suck up all the characters we can + uint8_t data; + while (_s->readBytes((char *)&data, 1) == 1) // Not read timeout + { + _buf[_bufLen++] = data; + } + // Now assess what we have + if (_bufLen < RH_E32_HEADER_LEN) + { + // Serial.println("Incomplete header"); + return false; + } + else if (_bufLen < _buf[0]) + { + // Serial.println("Incomplete message"); + return false; + } + else if ( _bufLen > _buf[0] + || _bufLen > RH_E32_MAX_PAYLOAD_LEN) + { + // Serial.println("Overrun"); + clearRxBuf(); + _rxBad++; + return false; + } + + // Else it a partial or complete message, test it + // printBuffer("read success", _buf, _bufLen); + validateRxBuf(); + } + return _rxBufValid; +} + +bool RH_E32::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen - RH_E32_HEADER_LEN) + *len = _bufLen - RH_E32_HEADER_LEN; + memcpy(buf, _buf + RH_E32_HEADER_LEN, *len); + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +bool RH_E32::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_E32_MAX_MESSAGE_LEN) + return false; + + waitPacketSent(); // Make sure we dont collide with previous message + + // Set up the headers + _buf[0] = len + RH_E32_HEADER_LEN; // Number of octets in teh whole message + _buf[1] = _txHeaderTo; + _buf[2] = _txHeaderFrom; + _buf[3] = _txHeaderId; + _buf[4] = _txHeaderFlags; + + // REVISIT: do we really have to do this? perhaps just write it after writing the header? + memcpy(_buf+RH_E32_HEADER_LEN, data, len); + + _s->write(_buf, len + RH_E32_HEADER_LEN); + setMode(RHModeTx); + _txGood++; + // Aux will return high when the TX buffer is empty + + return true; +} + +uint8_t RH_E32::maxMessageLength() +{ + return RH_E32_MAX_MESSAGE_LEN; +} + +bool RH_E32::waitPacketSent() +{ + if (_mode == RHModeTx) + waitAuxHigh(); + setMode(RHModeRx); + return true; +} + +bool RH_E32::setDataRate(DataRate rate) +{ + Parameters params; + if (!readParameters(params)) + return false; + // The DataRate enums are the same values as the register bitmasks + params.sped &= ~RH_E32_PARAM_SPED_DATARATE_MASK; + params.sped |= (rate & RH_E32_PARAM_SPED_DATARATE_MASK); + return writeParameters(params); +} + +bool RH_E32::setPower(PowerLevel level) +{ + Parameters params; + if (!readParameters(params)) + return false; + // The DataRate enums are the same values as the register bitmasks + params.option &= ~RH_E32_PARAM_OPTION_POWER_MASK; + params.option |= (level & RH_E32_PARAM_OPTION_POWER_MASK); + return writeParameters(params); +} + +bool RH_E32::setBaudRate(BaudRate rate, Parity parity) +{ + Parameters params; + if (!readParameters(params)) + return false; + // The DataRate enums are the same values as the register bitmasks + params.sped &= ~RH_E32_PARAM_SPED_UART_BAUD_MASK; + params.sped |= (rate & RH_E32_PARAM_SPED_UART_BAUD_MASK); + + // Also set the parity + params.sped &= ~RH_E32_PARAM_SPED_UART_MODE_MASK; + params.sped |= (parity & RH_E32_PARAM_SPED_UART_MODE_MASK); + + return writeParameters(params); +} + + +bool RH_E32::setFrequency(uint16_t frequency) +{ + if (frequency < 410 || frequency > 441) + return false; + + Parameters params; + if (!readParameters(params)) + return false; + params.chan = frequency - 410; + return writeParameters(params); + +} + +#endif // RH_HAVE_SERIAL diff --git a/RH_E32.h b/RH_E32.h new file mode 100644 index 0000000..70852c2 --- /dev/null +++ b/RH_E32.h @@ -0,0 +1,453 @@ +// RH_E32.h +// +// Definitions for E32-TTL-1W family serial radios: +// http://www.cdebyte.com/en/product-view-news.aspx?id=108 +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2017 Mike McCauley +// $Id: RH_E32.h,v 1.5 2020/04/09 23:40:34 mikem Exp $ +// + +#ifndef RH_E32_h +#define RH_E32_h + +#include + +// The buffer in the E32 is 512 bytes, but we arbitrarily limit messages to a maximum of 58 octets +// We use some for headers, keeping fewer for RadioHead messages +// The E32 sends messages longer than 58 octets in several packets of 58 octets each, and since we dont have any +// framing or message level checksums there is a risk that long messages from 2 different sources +// could be incorrectly interpreted as a single message +#define RH_E32_MAX_PAYLOAD_LEN 58 + +// The length of the headers we add: +// message length (including these headers) +// to +// from +// id +// flags +// The headers are inside the E32 payload +#define RH_E32_HEADER_LEN 5 + +// This is the maximum RadioHead user message length that can be supported by this module. Limited by +#define RH_E32_MAX_MESSAGE_LEN (RH_E32_MAX_PAYLOAD_LEN-RH_E32_HEADER_LEN) + +// Commands to alter module behaviour +#define RH_E32_COMMAND_WRITE_PARAMS_SAVE 0xC0 +#define RH_E32_COMMAND_READ_PARAMS 0xC1 +#define RH_E32_COMMAND_WRITE_PARAMS_NOSAVE 0xC2 +#define RH_E32_COMMAND_READ_VERSION 0xC3 +#define RH_E32_COMMAND_RESET 0xC4 + +// Various flags and masks for param bytes +#define RH_E32_PARAM_SPED_UART_MODE_MASK 0xC0 +#define RH_E32_PARAM_SPED_UART_MODE_8N1 0x00 +#define RH_E32_PARAM_SPED_UART_MODE_8O1 0x40 +#define RH_E32_PARAM_SPED_UART_MODE_8E1 0x80 + +#define RH_E32_PARAM_SPED_UART_BAUD_MASK 0x38 +#define RH_E32_PARAM_SPED_UART_BAUD_1200 0x00 +#define RH_E32_PARAM_SPED_UART_BAUD_2400 0x08 +#define RH_E32_PARAM_SPED_UART_BAUD_4800 0x10 +#define RH_E32_PARAM_SPED_UART_BAUD_9600 0x18 +#define RH_E32_PARAM_SPED_UART_BAUD_19200 0x20 +#define RH_E32_PARAM_SPED_UART_BAUD_38400 0x28 +#define RH_E32_PARAM_SPED_UART_BAUD_57600 0x30 +#define RH_E32_PARAM_SPED_UART_BAUD_115200 0x38 + +#define RH_E32_PARAM_SPED_DATARATE_MASK 0x07 +#define RH_E32_PARAM_SPED_DATARATE_1KBPS 0x00 +#define RH_E32_PARAM_SPED_DATARATE_2KBPS 0x01 +#define RH_E32_PARAM_SPED_DATARATE_5KBPS 0x02 +#define RH_E32_PARAM_SPED_DATARATE_8KBPS 0x03 +#define RH_E32_PARAM_SPED_DATARATE_10KBPS 0x04 +#define RH_E32_PARAM_SPED_DATARATE_15KBPS 0x05 +#define RH_E32_PARAM_SPED_DATARATE_20KBPS 0x06 +#define RH_E32_PARAM_SPED_DATARATE_25KBPS 0x07 + +#define RH_E32_PARAM_OPTION_FIXED_MASK 0x80 + +#define RH_E32_PARAM_OPTION_IODRIVE_MASK 0x40 + +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_MASK 0x38 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_250MS 0x00 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_500MS 0x08 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_750MS 0x10 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1000MS 0x18 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1250MS 0x20 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1500MS 0x28 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_1750MS 0x30 +#define RH_E32_PARAM_OPTION_WAKEUP_TIME_2000MS 0x38 + +#define RH_E32_PARAM_OPTION_FEC_MASK 0x04 + +#define RH_E32_PARAM_OPTION_POWER_MASK 0x03 +#define RH_E32_PARAM_OPTION_POWER_30DBM 0x00 +#define RH_E32_PARAM_OPTION_POWER_27DBM 0x01 +#define RH_E32_PARAM_OPTION_POWER_24DBM 0x02 +#define RH_E32_PARAM_OPTION_POWER_21DBM 0x03 + +///////////////////////////////////////////////////////////////////// +/// \class RH_E32 RH_E32.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via a EBYTE E32-TTL-1W +/// and similar serial radio transceiver. +/// +/// Works with +/// E32-TTL-1W +/// +/// Note: it should also be possible to use the E32-TTL-1W with the RadioHead RH_Serial module, +/// which will also you to send longer packets, but will require you to use the EBYTE Wireless Module Setting program +/// to configure the radio first. In this arrangement the E32 would act as a transparent serial connection. +/// This has not been tested by us. +/// +/// \par Overview +/// +/// This class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 53 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with EBYTE +/// RFM95/96/97/98(W), Semtech SX1276/77/78/7E32-TTL-1W9 and compatible radio modules. These modules implement +/// long range LORA transcivers with a transparent serial interface. With 1W power output the manufacturer +/// claims up to 6km range. +/// +/// This Driver provides functions for sending and receiving messages of up +/// to 53 octets on any frequency supported by the radio, in a range of +/// data rates and power outputs. Frequency can be set with +/// 1MHz precision to any frequency from 410 to 441MHz. +/// +/// You can use either a hardware or software serial connection. +/// +/// Tested with Arduino Uno and software serial. +/// +/// \par Packet Format +/// +/// All messages sent and received by this Driver conform to this packet format: +/// +/// - 5 octets HEADER: (LENGTH, TO, FROM, ID, FLAGS) +/// - 0 to 53 octets DATA +/// +/// \par Connecting E32-TTL-1W to Arduino +/// +/// We tested with Arduino Uno. We used SoftwareSerial on pins 6 and 7) to connect to the E32 module, so +/// we could continue to use the only hardware serial port for debugging +/// \code +/// Arduino E32 +/// GND----------GND (ground in) +/// 5V-----------VCC (5V in) +/// pin D4-----------M0 (mode control pin input to radio) +/// pin D5-----------M1 (mode control pin input to radio) +/// pin D6-----------RXD (serial data input from Arduino to radio) +/// pin D7-----------TXD (serial data output from radio to Arduino) +/// pin D8-----------AUX (Aux pin output from radio to Arduino) +/// \endcode +/// With this connection, you can initialise the serial port and RH_E32 like this: +/// \code +/// SoftwareSerial mySerial(7, 6); +/// RH_E32 driver(&mySerial, 4, 5, 8); +/// \endcode +/// +/// For Adafruit M0 Feather: +/// \code +/// Feather E32 +/// GND----------GND (ground in) +/// 3V-----------VCC (3.3V in) +/// pin D5-----------M0 (mode control pin input to radio) +/// pin D6-----------M1 (mode control pin input to radio) +/// pin D1/Tx--------RXD (serial data input from M0 to radio) +/// pin D0/Rx--------TXD (serial data output from radio to M0) +/// pin D9-----------AUX (Aux pin output from radio to M0) +/// \endcode +/// With this connection, you can initialise serial port 1 and RH_E32 like this: +/// \code +/// RH_E32 driver(&Serial1, 5, 6, 9); +/// \endcode +/// Other connection schems are possible provided the approporiate constructors are used for SoftwareSerial and RH_E32 +/// +/// \par Memory +/// +/// The RH_RF95 driver requires non-trivial amounts of memory. The sample +/// programs all compile to about 8kbytes each, which will fit in the +/// flash proram memory of most Arduinos. However, the RAM requirements are +/// more critical. Therefore, you should be vary sparing with RAM use in +/// programs that use the RH_E32 driver. +/// +/// It is often hard to accurately identify when you are hitting RAM limits on Arduino. +/// The symptoms can include: +/// - Mysterious crashes and restarts +/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements) +/// - Hanging +/// - Output from Serial.print() not appearing +/// +/// \par Performance +/// +/// This radio supports a range of different data rates and powers. +/// The lowest speeds are the most reliable, however you should note that at 1kbps and with an 13 octet payload, +/// the transmission time for one packet approaches 5 seconds. Therefore you should be cautious about trying to +/// send too many or too long messages per unit of time, lest you monopolise the airwaves. +/// Be a good neighbour and use the lowest power and fastest speed that you can. +/// +/// Forward Error Correction (FEC) is always enabled in these radios by RH_E32. +/// +/// \par Range +/// +/// When running with a power output of 1W and at the slowest speed of 1kbps, this module has an impressive range. +/// We tested with: +/// E32-TTL-1W (1 W power, 1kbps data rate) +/// Single wire antenna with a small meta ground plane at about 1m above ground level +/// Arduino Uno +/// RadioHead RH_E32 module with e32_client and e32_server sketches +/// Packet length 13 octets (total payload 18 octets) +/// (and yes, we have an appropriate radio license for that power output) +/// +/// We were able to get reliable reception over 7km (6 km over ocean and 1 km through low rise residential area) +/// +/// You can expect less range with lower power outputs and faster speeds. +/// You can expect less range in highrise cities. +/// You can expect more range with directional antennas. +/// You can expect more range with shorter messages. +/// +/// \par Transmitter Power +/// TBA +/// +/// Caution: the maximum power output of this radio (1W = 30dbM) is almost certainly more than the +/// permitted power level for unlicensed users in the ISM bands in most countries. Be sure you comply with your local +/// regulations. Be a good neighbour and use the lowest power and fastest speed that you can. +/// +class Stream; +class RH_E32 : public RHGenericDriver +{ + public: + + /// \brief Values to be passed to setDataRate() to control the on-air data rate + /// + /// This is NOT to be used to control the baud rate of the serial connection to the radio + typedef enum + { + DataRate1kbps = RH_E32_PARAM_SPED_DATARATE_1KBPS, + DataRate2kbps = RH_E32_PARAM_SPED_DATARATE_2KBPS, + DataRate5kbps = RH_E32_PARAM_SPED_DATARATE_5KBPS, + DataRate8kbps = RH_E32_PARAM_SPED_DATARATE_8KBPS, + DataRate10kbps = RH_E32_PARAM_SPED_DATARATE_10KBPS, + DataRate15kbps = RH_E32_PARAM_SPED_DATARATE_15KBPS, + DataRate20kbps = RH_E32_PARAM_SPED_DATARATE_20KBPS, + DataRate25kbps = RH_E32_PARAM_SPED_DATARATE_25KBPS + } DataRate; + + /// \brief Values to be passed to setPower() to control the transmitter power + /// + typedef enum + { + Power30dBm = RH_E32_PARAM_OPTION_POWER_30DBM, + Power27dBm = RH_E32_PARAM_OPTION_POWER_27DBM, + Power24dBm = RH_E32_PARAM_OPTION_POWER_24DBM, + Power21dBm = RH_E32_PARAM_OPTION_POWER_21DBM, + } PowerLevel; + + /// \brief Values to be passed to setBaudRate() to control the radio serial connection baud rate + /// + /// This is NOT to be used to control the on-air data rate the radio transmits and receives at + typedef enum + { + BaudRate1200 = RH_E32_PARAM_SPED_UART_BAUD_1200, + BaudRate2400 = RH_E32_PARAM_SPED_UART_BAUD_2400, + BaudRate4800 = RH_E32_PARAM_SPED_UART_BAUD_4800, + BaudRate9600 = RH_E32_PARAM_SPED_UART_BAUD_9600, + BaudRate19200 = RH_E32_PARAM_SPED_UART_BAUD_19200, + BaudRate38400 = RH_E32_PARAM_SPED_UART_BAUD_38400, + BaudRate57600 = RH_E32_PARAM_SPED_UART_BAUD_57600, + BaudRate115200 = RH_E32_PARAM_SPED_UART_BAUD_115200, + } BaudRate; + + /// \brief Values to be passed to setBaudRate() to control the parity of the serial connection to the radio + typedef enum + { + Parity8N1 = RH_E32_PARAM_SPED_UART_MODE_8N1, + Parity8O1 = RH_E32_PARAM_SPED_UART_MODE_8O1, + Parity8E1 = RH_E32_PARAM_SPED_UART_MODE_8E1, + } Parity; + + /// Contructor. You can have multiple instances, but each instance must have its own + /// serial connection, M0 M1 and AUX connections. Initialises the mode of the referenced pins + /// Does NOT set the baud rate of the serial connection to the radio. + /// \param[in] s Reference to the SoftwareSerial or HardwareSerial port used to connect to the radio + /// \param[in] m0_pin Pin number of the Arduino pin that connects to the radio M0 input + /// \param[in] m1_pin Pin number of the Arduino pin that connects to the radio M1 input + /// \param[in] aux_pin Pin number of the Arduino pin that connects to the radio AUX output + RH_E32(Stream *s=&Serial, uint8_t m0_pin = 4, uint8_t m1_pin = 5, uint8_t aux_pin = 8); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly, including setting the serial port baud rate and parity to that + /// configured in the radio (typically 9600 baud, 8N1) before calling init(). + /// Sets the module to 443MHz, 21dBm power and 5kbps data rate (you can change these after initialisation with + /// the various set* functions). + /// This function may not return if the AUX pin is not connected. + /// Initialisation failure can be caused by: + /// Electrical connections to the radio incorrect or incomplete + /// Radio configured to use a different baud rate to the one configured to the Ardiono serial port + /// Incorrect radio module connected tot he serial port. + /// Other serial communicaitons problems between the Arduino and the radio + /// \return true if initialisation succeeded. + bool init(); + + /// Tests whether a new message is available + /// from the Driver. + /// This can and should be called multiple times in a timeout loop. You should call this as frequently as possible + /// whenever a message might be received + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv(). + bool available(); + + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send + /// \return true if the message length was valid and it was correctly queued for transmit. Return false + /// if CAD was requested and the CAD timeout timed out before clear channel was detected. + bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + uint8_t maxMessageLength(); + + /// Waits for any currently transmitting packet to be completely sent + /// Returns true if successful + bool waitPacketSent(); + + /// Sets the on-air data rate to be used by the transmitter and receiver + /// \param[in] rate A valid data rate from the DataRate enum + /// \return true if successful + bool setDataRate(DataRate rate); + + /// Sets the transmitter power output + /// \param[in] level A valid power setting from the Power enum + /// \return true if successful + bool setPower(PowerLevel level); + + /// Sets the radio serial port baud rate and parity (not the on-air data rate) + /// Does not set the Aruino rate or parity: you wil nned to do this afterwards + /// \param[in] rate A valid baud rate from the BaudRate enum + /// \param[in] parity A valid parity from the PArity enum + /// \return true if successful + bool setBaudRate(BaudRate rate = BaudRate9600, Parity parity = Parity8N1); + + /// Sets the tarnsmitter and receiver frequency. + /// \param[in] frequency Desired frequency in MHx from 410 to 441 MHz inclusive + /// \return true if successful + bool setFrequency(uint16_t frequency); + +protected: + + /// \brief Defines values to be passed to setOperatinMode + /// + /// For internal driver user only + typedef enum + { + ModeNormal = 0, ///< Normal mode for sending and receiving messages + ModeWakeUp, ///< Adds a long preamble to transmission to allow destination receivers to wake up + ModePowerSaving, ///< Receiver sleeps until a message is received + ModeSleep ///< Use during parameter setting + } OperatingMode; + + /// \brief Structure for reading and writing radio control parameters + /// + /// For internal driver user only + typedef struct + { + uint8_t head; ///< 0xc2 (no save) or 0xc0 (save) + uint8_t addh; ///< High address byte (not used by this driver) + uint8_t addl; ///< Low address byte (not used by this driver) + uint8_t sped; ///< Data and baud rate parameters + uint8_t chan; ///< Radio channel + uint8_t option; ///< Various control options + + } Parameters; + + /// Sets the operating mode of the radio. + /// For internal use only + void setOperatingMode(OperatingMode mode); + + /// Retrieves the version number for the radio and checks that it is valid + /// \return true if the version could be retrieved and is radio model number is correct + bool getVersion(); + + /// Waits for the AUX pin to go high + /// For internal use only + void waitAuxHigh(); + + /// Waits for the AUX pin to go low + /// For internal use only + void waitAuxLow(); + + /// Issues a reset command to the radio + /// WARNING: this seems to break reception. Why? + /// \return true if successful + bool reset(); + + /// Read the radio configuration parameters into + /// local memory + /// \param[in] params Reference to a Parameter structure which will be filled if successful + /// \return true if successful + bool readParameters(Parameters& params); + + /// Write radio configuration parameters from local memory + /// to the radio. You can choose whether the parameter will be saved across power down or not + /// \param[in] params Reference to a Parameter structure containing the radio configuration parameters + /// to be written to the radio. + /// \param[in] save If true, the parameters will be saved across power down in the radio + /// \return true if successful + bool writeParameters(Parameters& params, bool save = false); + + /// Examine the receive buffer to determine whether the message is for this node + /// For internal use only + void validateRxBuf(); + + /// Clear our local receive buffer + /// For internal use only + void clearRxBuf(); + +private: + /// Serial stream (hardware or software serial) + Stream* _s; + + /// Pin number connected to M0 + uint8_t _m0_pin; + + /// Pin number connected to M1 + uint8_t _m1_pin; + + /// Pin number connected to AUX + uint8_t _aux_pin; + + /// Number of octets in the buffer + uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_E32_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + bool _rxBufValid; + +}; + +/// @example e32_client.pde +/// @example e32_server.pde + +#endif + diff --git a/RH_MRF89.cpp b/RH_MRF89.cpp new file mode 100644 index 0000000..564c650 --- /dev/null +++ b/RH_MRF89.cpp @@ -0,0 +1,570 @@ +// RH_MRF89.cpp +// +// Copyright (C) 2015 Mike McCauley +// $Id: RH_MRF89.cpp,v 1.10 2019/09/02 05:21:52 mikem Exp $ + +#include +#define BAND_915 +#define DATA_RATE_200 +#define LNA_GAIN LNA_GAIN_0_DB +#define TX_POWER TX_POWER_13_DB + +// Interrupt vectors for the 3 Arduino interrupt pins +// Each interrupt can be handled by a different instance of RH_MRF89, allowing you to have +// 2 or more LORAs per Arduino +RH_MRF89* RH_MRF89::_deviceForInterrupt[RH_MRF89_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_MRF89::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// These are indexed by the values of ModemConfigChoice +// Values based on sample modulation values from MRF89XA.h +// TXIPOLFV set to be more than Fd +PROGMEM static const RH_MRF89::ModemConfig MODEM_CONFIG_TABLE[] = +{ + // MODSEL, FDVAL, BRVAL, FILCREG=(PASFILV|BUTFILV), TXIPOLFV + // FSK, No Manchester, Whitening + { RH_MRF89_MODSEL_FSK, 0x0B, 0x63, 0x40 | 0x01, 0x20 }, // FSK_Rb2Fd33 + { RH_MRF89_MODSEL_FSK, 0x0B, 0x27, 0x40 | 0x01, 0x20 }, // FSK_Rb5Fd33 + { RH_MRF89_MODSEL_FSK, 0x0B, 0x13, 0x40 | 0x01, 0x20 }, // FSK_Rb10Fd33 + { RH_MRF89_MODSEL_FSK, 0x09, 0x09, 0x70 | 0x02, 0x20 }, // FSK_Rb20Fd40 + { RH_MRF89_MODSEL_FSK, 0x04, 0x04, 0xB0 | 0x05, 0x40 }, // FSK_Rb40Fd80 + { RH_MRF89_MODSEL_FSK, 0x03, 0x03, 0xD0 | 0x06, 0x40 }, // FSK_Rb50Fd100 + { RH_MRF89_MODSEL_FSK, 0x02, 0x02, 0xE0 | 0x09, 0x60 }, // FSK_Rb66Fd133 + { RH_MRF89_MODSEL_FSK, 0x01, 0x01, 0xF0 | 0x0F, 0x80 }, // FSK_Rb100Fd200 + { RH_MRF89_MODSEL_FSK, 0x01, 0x00, 0xF0 | 0x0F, 0x80 } // FSK_Rb200Fd200 + +}; + + +RH_MRF89::RH_MRF89(uint8_t csconPin, uint8_t csdatPin, uint8_t interruptPin, RHGenericSPI& spi) + : + RHNRFSPIDriver(csconPin, spi), + _csconPin(csconPin), + _csdatPin(csdatPin), + _interruptPin(interruptPin) +{ + _myInterruptIndex = 0xff; // Not allocated yet +} + +bool RH_MRF89::init() +{ + // MRF89 data cant handle SPI greater than 1MHz. + // Sigh on teensy at 1MHz, need special delay after writes, see RHNRFSPIDriver::spiWrite + _spi.setFrequency(RHGenericSPI::Frequency1MHz); + if (!RHNRFSPIDriver::init()) + return false; + + // Initialise the chip select pins + pinMode(_csconPin, OUTPUT); + digitalWrite(_csconPin, HIGH); + pinMode(_csdatPin, OUTPUT); + digitalWrite(_csdatPin, HIGH); + + // 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 + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + + // Make sure we are not in some unexpected mode from a previous run + setOpMode(RH_MRF89_CMOD_STANDBY); + + // No way to check the device type but lets trivially check there is something there + // by trying to change a register: + spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0xaa); + if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0xaa) + return false; + spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0x3); // Back to the default for FDEV + if (spiReadRegister(RH_MRF89_REG_02_FDEVREG) != 0x3) + return false; + + // Add by Adrien van den Bossche 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 actually the + // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping + // yourself based on knowledge of what Arduino board you are running on. + if (_myInterruptIndex == 0xff) + { + // First run, no interrupt allocated yet + if (_interruptCount <= RH_MRF89_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 + + // When used with the MRF89XAM9A module, per 75017B.pdf section 1.3, need: + // crystal freq = 12.8MHz + // clock output disabled + // frequency bands 902-915 or 915-928 + // VCOT 60mV + // OOK max 28kbps + // Based on 70622C.pdf, section 3.12: + spiWriteRegister(RH_MRF89_REG_00_GCONREG, RH_MRF89_CMOD_STANDBY | RH_MRF89_FBS_950_960 | RH_MRF89_VCOT_60MV); + spiWriteRegister(RH_MRF89_REG_01_DMODREG, RH_MRF89_MODSEL_FSK | RH_MRF89_OPMODE_PACKET); // FSK, Packet mode, LNA 0dB + spiWriteRegister(RH_MRF89_REG_02_FDEVREG, 0); // Set by setModemConfig + spiWriteRegister(RH_MRF89_REG_03_BRSREG, 0); // Set by setModemConfig + spiWriteRegister(RH_MRF89_REG_04_FLTHREG, 0); // Set by setModemConfig (OOK only) + spiWriteRegister(RH_MRF89_REG_05_FIFOCREG, RH_MRF89_FSIZE_64); + spiWriteRegister(RH_MRF89_REG_06_R1CREG, 0); // Set by setFrequency + spiWriteRegister(RH_MRF89_REG_07_P1CREG, 0); // Set by setFrequency + spiWriteRegister(RH_MRF89_REG_08_S1CREG, 0); // Set by setFrequency + spiWriteRegister(RH_MRF89_REG_09_R2CREG, 0); // Frequency set 2 not used + spiWriteRegister(RH_MRF89_REG_0A_P2CREG, 0); // Frequency set 2 not used + spiWriteRegister(RH_MRF89_REG_0B_S2CREG, 0); // Frequency set 2 not used + spiWriteRegister(RH_MRF89_REG_0C_PACREG, RH_MRF89_PARC_23); + // IRQ0 rx mode: SYNC (not used) + // IRQ1 rx mode: CRCOK + // IRQ1 tx mode: TXDONE + spiWriteRegister(RH_MRF89_REG_0D_FTXRXIREG, RH_MRF89_IRQ0RXS_PACKET_SYNC | RH_MRF89_IRQ1RXS_PACKET_CRCOK | RH_MRF89_IRQ1TX); + spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, RH_MRF89_LENPLL); + spiWriteRegister(RH_MRF89_REG_0F_RSTHIREG, 0x00); // default not used if no RSSI interrupts + spiWriteRegister(RH_MRF89_REG_10_FILCREG, 0); // Set by setModemConfig + + spiWriteRegister(RH_MRF89_REG_11_PFCREG, 0x38);// 100kHz, recommended, but not used, see RH_MRF89_REG_12_SYNCREG OOK only? + spiWriteRegister(RH_MRF89_REG_12_SYNCREG, RH_MRF89_SYNCREN | RH_MRF89_SYNCWSZ_32); // No polyphase, no bsync, sync, 0 errors + spiWriteRegister(RH_MRF89_REG_13_RSVREG, 0x07);//default +// spiWriteRegister(RH_MRF89_REG_14_RSTSREG, 0x00); // NO, read only + spiWriteRegister(RH_MRF89_REG_15_OOKCREG, 0x00); // Set by setModemConfig OOK only + spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG, 0x69); // Set by setSyncWords + spiWriteRegister(RH_MRF89_REG_17_SYNCV23REG, 0x81); // Set by setSyncWords + spiWriteRegister(RH_MRF89_REG_18_SYNCV15REG, 0x7E); // Set by setSyncWords + spiWriteRegister(RH_MRF89_REG_19_SYNCV07REG, 0x96); // Set by setSyncWords + // TXIPOLFV set by setModemConfig. power set by setTxPower + spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, 0xf0 | RH_MRF89_TXOPVAL_13DBM); // TX cutoff freq=375kHz, + spiWriteRegister(RH_MRF89_REG_1B_CLKOREG, 0x00); // Disable clock output to save power + spiWriteRegister(RH_MRF89_REG_1C_PLOADREG, 0x40); // payload=64bytes (no RX-filtering on packet length) + spiWriteRegister(RH_MRF89_REG_1D_NADDSREG, 0x00); // Node Address (0=default) Not used + spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, RH_MRF89_PKTLENF | RH_MRF89_PRESIZE_4 | RH_MRF89_WHITEON | RH_MRF89_CHKCRCEN | RH_MRF89_ADDFIL_OFF); + spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, 0x00); // default (FIFO access in standby=write, clear FIFO on CRC mismatch) + + // Looking OK now + // Set some suitable defaults: + setPreambleLength(3); // The default + uint8_t syncwords[] = { 0x69, 0x81, 0x7e, 0x96 }; // Same as RH_MRF89XA + setSyncWords(syncwords, sizeof(syncwords)); + setTxPower(RH_MRF89_TXOPVAL_1DBM); + if (!setFrequency(915.4)) + return false; + // Some slow, reliable default speed and modulation + if (!setModemConfig(FSK_Rb20Fd40)) + return false; + + return true; +} + +bool RH_MRF89::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + uint8_t i; + for (i = 0; i <= 0x1f; i++) + { + Serial.print(i, HEX); + Serial.print(": "); + Serial.println(spiReadRegister(i), HEX); + } +#endif + return true; +} + +// C++ level interrupt handler for this instance +// MRF89XA is unusual in that it has 2 interrupt lines, and not a single, combined one. +// Only one of the several interrupt lines (IRQ1) from the RFM95 needs to be +// connnected to the processor. +// We use this to get CRCOK and TXDONE interrupts +void RH_MRF89::handleInterrupt() +{ +// Serial.println("I"); + if (_mode == RHModeTx) + { +// Serial.println("T"); + // TXDONE + // Transmit is complete + _txGood++; + setModeIdle(); + } + else if (_mode == RHModeRx) + { +// Serial.println("R"); + // CRCOK + // We have received a packet. + // First byte in FIFO is packet length + + // REVISIT: Capture last rssi from RSTSREG + // based roughly on Figure 3-9 + _lastRssi = (spiReadRegister(RH_MRF89_REG_14_RSTSREG) >> 1) - 120; + + _bufLen = spiReadData(); + if (_bufLen < 4) + { + // Drain the FIFO + uint8_t i; + for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++) + spiReadData(); + clearRxBuf(); + return; + } + + // Now drain all the data from the FIFO into _buf + uint8_t i; + for (i = 0; spiReadRegister(RH_MRF89_REG_0D_FTXRXIREG) & RH_MRF89_FIFOEMPTY; i++) + _buf[i] = spiReadData(); + + // All good. See if its for us + validateRxBuf(); + if (_rxBufValid) + setModeIdle(); // Got one + } +} + +// These are low level functions that call the interrupt handler for the correct +// instance of RH_MRF89. +// 3 interrupts allows us to have 3 different devices +void RH_INTERRUPT_ATTR RH_MRF89::isr0() +{ + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_MRF89::isr1() +{ + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_MRF89::isr2() +{ + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +} + +uint8_t RH_MRF89::spiReadRegister(uint8_t reg) +{ + // Tell the chip we want to talk to the configuration registers + setSlaveSelectPin(_csconPin); + digitalWrite(_csdatPin, HIGH); + return spiRead(((reg & 0x1f) << 1) | RH_MRF89_SPI_READ_MASK); +} + +uint8_t RH_MRF89::spiWriteRegister(uint8_t reg, uint8_t val) +{ + // Tell the chip we want to talk to the configuration registers + setSlaveSelectPin(_csconPin); + digitalWrite(_csdatPin, HIGH); + // Hmmm, on teensy 3.1, needed some special behaviour in RHNRFSPIDriver::spiWrite + // because otherwise, CSCON returns high before the final clock goes low, + // which prevents the MRF89XA spi write succeeding. Clock must be low when CSCON goes high. + return spiWrite(((reg & 0x1f) << 1), val); +} + +uint8_t RH_MRF89::spiWriteData(uint8_t data) +{ + spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO + setSlaveSelectPin(_csdatPin); + digitalWrite(_csconPin, HIGH); + return spiCommand(data); +} + +uint8_t RH_MRF89::spiWriteData(const uint8_t* data, uint8_t len) +{ + spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC); // Write to FIFO + setSlaveSelectPin(_csdatPin); + digitalWrite(_csconPin, HIGH); + + uint8_t status = 0; + ATOMIC_BLOCK_START; + _spi.beginTransaction(); + digitalWrite(_slaveSelectPin, LOW); + while (len--) + _spi.transfer(*data++); + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return status; + +} + +uint8_t RH_MRF89::spiReadData() +{ + spiWriteRegister(RH_MRF89_REG_1F_FCRCREG, RH_MRF89_ACFCRC | RH_MRF89_FRWAXS); // Read from FIFO + setSlaveSelectPin(_csdatPin); + digitalWrite(_csconPin, HIGH); + return spiCommand(0); +} + +void RH_MRF89::setOpMode(uint8_t mode) +{ + // REVISIT: do we need to have time delays when switching between modes? + uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG); + val = (val & ~RH_MRF89_CMOD) | (mode & RH_MRF89_CMOD); + spiWriteRegister(RH_MRF89_REG_00_GCONREG, val); +} + +void RH_MRF89::setModeIdle() +{ + if (_mode != RHModeIdle) + { + setOpMode(RH_MRF89_CMOD_STANDBY); + _mode = RHModeIdle; + } +} + +bool RH_MRF89::sleep() +{ + if (_mode != RHModeSleep) + { + setOpMode(RH_MRF89_CMOD_SLEEP); + _mode = RHModeSleep; + } + return true; +} + +void RH_MRF89::setModeRx() +{ + if (_mode != RHModeRx) + { + setOpMode(RH_MRF89_CMOD_RECEIVE); + _mode = RHModeRx; + } +} + +void RH_MRF89::setModeTx() +{ + if (_mode != RHModeTx) + { + setOpMode(RH_MRF89_CMOD_TRANSMIT); + _mode = RHModeTx; + } +} + +void RH_MRF89::setTxPower(uint8_t power) +{ + uint8_t txconreg = spiReadRegister(RH_MRF89_REG_1A_TXCONREG); + txconreg |= (power & RH_MRF89_TXOPVAL); + spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, txconreg); +} + +bool RH_MRF89::available() +{ + if (_mode == RHModeTx) + return false; + setModeRx(); + + return _rxBufValid; // Will be set by the interrupt handler when a good message is received +} + +bool RH_MRF89::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + + if (buf && len) + { + ATOMIC_BLOCK_START; + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen - RH_MRF89_HEADER_LEN) + *len = _bufLen - RH_MRF89_HEADER_LEN; + memcpy(buf, _buf + RH_MRF89_HEADER_LEN, *len); + ATOMIC_BLOCK_END; + } + clearRxBuf(); // This message accepted and cleared + + return true; +} + +bool RH_MRF89::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_MRF89_MAX_MESSAGE_LEN) + return false; + + waitPacketSent(); // Make sure we dont interrupt an outgoing message + setModeIdle(); + + if (!waitCAD()) + return false; // Check channel activity + + // First octet is the length of the chip payload + // 0 length messages are transmitted but never trigger a receive! + spiWriteData(len + RH_MRF89_HEADER_LEN); + spiWriteData(_txHeaderTo); + spiWriteData(_txHeaderFrom); + spiWriteData(_txHeaderId); + spiWriteData(_txHeaderFlags); + spiWriteData(data, len); + setModeTx(); // Start transmitting + + return true; +} + +uint8_t RH_MRF89::maxMessageLength() +{ + return RH_MRF89_MAX_MESSAGE_LEN; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_MRF89::validateRxBuf() +{ + if (_bufLen < 4) + return; // Too short to be a real message + // Extract the 4 headers + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +void RH_MRF89::clearRxBuf() +{ + ATOMIC_BLOCK_START; + _rxBufValid = false; + _bufLen = 0; + ATOMIC_BLOCK_END; +} + +bool RH_MRF89::verifyPLLLock() +{ + // Verify PLL-lock per instructions in Note 1 section 3.12 + // Need to do this after changing frequency. + uint8_t ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG); + spiWriteRegister(RH_MRF89_REG_0E_FTPRIREG, ftpriVal | RH_MRF89_LSTSPLL); // Clear PLL lock bit + setOpMode(RH_MRF89_CMOD_FS); + unsigned long ulStartTime = millis(); + while ((millis() - ulStartTime < 1000)) + { + ftpriVal = spiReadRegister(RH_MRF89_REG_0E_FTPRIREG); + if ((ftpriVal & RH_MRF89_LSTSPLL) != 0) + break; + } + setOpMode(RH_MRF89_CMOD_STANDBY); + return ((ftpriVal & RH_MRF89_LSTSPLL) != 0); +} + +bool RH_MRF89::setFrequency(float centre) +{ + // REVISIT: FSK only: its different for OOK :-( + + uint8_t FBS; + if (centre >= 902.0 && centre < 915.0) + { + FBS = RH_MRF89_FBS_902_915; + } + else if (centre >= 915.0 && centre <= 928.0) + { + FBS = RH_MRF89_FBS_915_928; + } + else if (centre >= 950.0 && centre <= 960.0) + { + // Not all modules support this frequency band: + // The MRF98XAM9A does not + FBS = RH_MRF89_FBS_950_960; + } +// else if (centre >= 863.0 && centre <= 870.0) +// { +// // Not all modules support this frequency band: +// // The MRF98XAM9A does not +// FBS = RH_MRF89_FBS_950_960; // Yes same as above +// } + else + { + // Cant do this freq + return false; + } + + // Based on frequency calcs done in MRF89XA.h +// uint8_t R = 100; // Recommended + uint8_t R = 119; // Also recommended :-( + uint32_t centre_kHz = centre * 1000; + uint32_t xtal_kHz = (RH_MRF89_XTAL_FREQ * 1000); + uint32_t compare = (centre_kHz * 8 * (R + 1)) / (9 * xtal_kHz); + uint8_t P = ((compare - 75) / 76) + 1; + uint8_t S = compare - (75 * (P + 1)); + + // Now set the new register values: + uint8_t val = spiReadRegister(RH_MRF89_REG_00_GCONREG); + val = (val & ~RH_MRF89_FBS) | (FBS & RH_MRF89_FBS); + spiWriteRegister(RH_MRF89_REG_00_GCONREG, val); + + spiWriteRegister(RH_MRF89_REG_06_R1CREG, R); + spiWriteRegister(RH_MRF89_REG_07_P1CREG, P); + spiWriteRegister(RH_MRF89_REG_08_S1CREG, S); + + return verifyPLLLock(); +} + +// Set one of the canned FSK Modem configs +// Returns true if its a valid choice +bool RH_MRF89::setModemConfig(ModemConfigChoice index) +{ + if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig))) + return false; + + RH_MRF89::ModemConfig cfg; + memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(cfg)); + + // Now update the registers + uint8_t val = spiReadRegister(RH_MRF89_REG_01_DMODREG); + val = (val & ~RH_MRF89_MODSEL) | cfg.MODSEL; + spiWriteRegister(RH_MRF89_REG_01_DMODREG, val); + + spiWriteRegister(RH_MRF89_REG_02_FDEVREG, cfg.FDVAL); + spiWriteRegister(RH_MRF89_REG_03_BRSREG, cfg.BRVAL); + spiWriteRegister(RH_MRF89_REG_10_FILCREG, cfg.FILCREG); + + // The sample configs in MRF89XA.h all use TXIPOLFV = 0xf0 => 375kHz, which is too wide for most modulations + val = spiReadRegister(RH_MRF89_REG_1A_TXCONREG); + val = (val & ~RH_MRF89_TXIPOLFV) | (cfg.TXIPOLFV & RH_MRF89_TXIPOLFV); + spiWriteRegister(RH_MRF89_REG_1A_TXCONREG, val); + + return true; +} + +void RH_MRF89::setPreambleLength(uint8_t bytes) +{ + if (bytes >= 1 && bytes <= 4) + { + bytes--; + uint8_t pktcreg = spiReadRegister(RH_MRF89_REG_1E_PKTCREG); + pktcreg = (pktcreg & ~RH_MRF89_PRESIZE) | ((bytes << 5) & RH_MRF89_PRESIZE); + spiWriteRegister(RH_MRF89_REG_1E_PKTCREG, pktcreg); + } +} + +void RH_MRF89::setSyncWords(const uint8_t* syncWords, uint8_t len) +{ + if (syncWords && (len > 0 and len <= 4)) + { + uint8_t syncreg = spiReadRegister(RH_MRF89_REG_12_SYNCREG); + syncreg = (syncreg & ~RH_MRF89_SYNCWSZ) | (((len - 1) << 3) & RH_MRF89_SYNCWSZ); + spiWriteRegister(RH_MRF89_REG_12_SYNCREG, syncreg); + uint8_t i; + for (i = 0; i < 4; i++) + { + if (len > i) + spiWriteRegister(RH_MRF89_REG_16_SYNCV31REG + i, syncWords[i]); + } + } +} + diff --git a/RH_MRF89.h b/RH_MRF89.h new file mode 100644 index 0000000..a87e3ba --- /dev/null +++ b/RH_MRF89.h @@ -0,0 +1,628 @@ +// RH_MRF89.h +// +// Definitions for Microchip MRF89XA family radios radios per: +// http://ww1.microchip.com/downloads/en/DeviceDoc/70622C.pdf +// http://ww1.microchip.com/downloads/en/DeviceDoc/75017B.pdf +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2015 Mike McCauley +// $Id: RH_MRF89.h,v 1.7 2017/07/25 05:26:50 mikem Exp $ +// + +#ifndef RH_RF95_h +#define RH_RF95_h + +#include + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_MRF89_NUM_INTERRUPTS 3 + +// Max number of octets the MRF89XA Rx/Tx FIFO can hold +#define RH_MRF89_FIFO_SIZE 64 + +// This is the maximum number of bytes that can be carried by the MRF89XA. +// We use some for headers, keeping fewer for RadioHead messages +#define RH_MRF89_MAX_PAYLOAD_LEN RH_MRF89_FIFO_SIZE + +// The length of the headers we add. +// The headers are inside the MRF89XA payload +#define RH_MRF89_HEADER_LEN 4 + +// This is the maximum user message length that can be supported by this driver. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 4 bytes headers, user data. Message length and CRC are automatically encoded and decoded by +// the MRF89XA +#ifndef RH_MRF89_MAX_MESSAGE_LEN + #define RH_MRF89_MAX_MESSAGE_LEN (RH_MRF89_MAX_PAYLOAD_LEN - RH_MRF89_HEADER_LEN) +#endif + +// Bits that must be set to do a SPI read +#define RH_MRF89_SPI_READ_MASK 0x40 + +// The MRF89XA crystal frequency in MHz +#define RH_MRF89_XTAL_FREQ 12.8 + +// Register names from Figure 2-18 +#define RH_MRF89_REG_00_GCONREG 0x00 +#define RH_MRF89_REG_01_DMODREG 0x01 +#define RH_MRF89_REG_02_FDEVREG 0x02 +#define RH_MRF89_REG_03_BRSREG 0x03 +#define RH_MRF89_REG_04_FLTHREG 0x04 +#define RH_MRF89_REG_05_FIFOCREG 0x05 +#define RH_MRF89_REG_06_R1CREG 0x06 +#define RH_MRF89_REG_07_P1CREG 0x07 +#define RH_MRF89_REG_08_S1CREG 0x08 +#define RH_MRF89_REG_09_R2CREG 0x09 +#define RH_MRF89_REG_0A_P2CREG 0x0a +#define RH_MRF89_REG_0B_S2CREG 0x0b +#define RH_MRF89_REG_0C_PACREG 0x0c +#define RH_MRF89_REG_0D_FTXRXIREG 0x0d +#define RH_MRF89_REG_0E_FTPRIREG 0x0e +#define RH_MRF89_REG_0F_RSTHIREG 0x0f +#define RH_MRF89_REG_10_FILCREG 0x10 +#define RH_MRF89_REG_11_PFCREG 0x11 +#define RH_MRF89_REG_12_SYNCREG 0x12 +// Hmm the addresses of the next 2 is ambiguous in the docs +// this seems to agree with whats in the chip: +#define RH_MRF89_REG_13_RSVREG 0x13 +#define RH_MRF89_REG_14_RSTSREG 0x14 +#define RH_MRF89_REG_15_OOKCREG 0x15 +#define RH_MRF89_REG_16_SYNCV31REG 0x16 +#define RH_MRF89_REG_17_SYNCV23REG 0x17 +#define RH_MRF89_REG_18_SYNCV15REG 0x18 +#define RH_MRF89_REG_19_SYNCV07REG 0x19 +#define RH_MRF89_REG_1A_TXCONREG 0x1a +#define RH_MRF89_REG_1B_CLKOREG 0x1b +#define RH_MRF89_REG_1C_PLOADREG 0x1c +#define RH_MRF89_REG_1D_NADDSREG 0x1d +#define RH_MRF89_REG_1E_PKTCREG 0x1e +#define RH_MRF89_REG_1F_FCRCREG 0x1f + +// Register bitfield definitions +//#define RH_MRF89_REG_00_GCONREG 0x00 +#define RH_MRF89_CMOD 0xe0 +#define RH_MRF89_CMOD_TRANSMIT 0x80 +#define RH_MRF89_CMOD_RECEIVE 0x60 +#define RH_MRF89_CMOD_FS 0x40 +#define RH_MRF89_CMOD_STANDBY 0x20 +#define RH_MRF89_CMOD_SLEEP 0x00 + +#define RH_MRF89_FBS 0x18 +#define RH_MRF89_FBS_950_960 0x10 +#define RH_MRF89_FBS_915_928 0x08 +#define RH_MRF89_FBS_902_915 0x00 + +#define RH_MRF89_VCOT 0x06 +#define RH_MRF89_VCOT_180MV 0x06 +#define RH_MRF89_VCOT_120MV 0x04 +#define RH_MRF89_VCOT_60MV 0x02 +#define RH_MRF89_VCOT_TANK 0x00 + +#define RH_MRF89_RPS 0x01 + +//#define RH_MRF89_REG_01_DMODREG 0x01 +#define RH_MRF89_MODSEL 0xc0 +#define RH_MRF89_MODSEL_FSK 0x80 +#define RH_MRF89_MODSEL_OOK 0x40 + +#define RH_MRF89_DMODE0 0x20 + +#define RH_MRF89_OOKTYP 0x18 +#define RH_MRF89_OOKTYP_AVERAGE 0x10 +#define RH_MRF89_OOKTYP_PEAK 0x08 +#define RH_MRF89_OOKTYP_FIXED 0x00 + +#define RH_MRF89_DMODE1 0x04 + +#define RH_MRF89_IFGAIN 0x03 +#define RH_MRF89_IFGAIN_M13P5 0x03 +#define RH_MRF89_IFGAIN_M9 0x02 +#define RH_MRF89_IFGAIN_M4P5 0x01 +#define RH_MRF89_IFGAIN_0 0x00 + +// DMODE1 and DMODE1: +#define RH_MRF89_OPMODE_CONTINUOUS 0x00 +#define RH_MRF89_OPMODE_BUFFER RH_MRF89_DMODE0 +#define RH_MRF89_OPMODE_PACKET RH_MRF89_DMODE1 + +//#define RH_MRF89_REG_03_BRSREG 0x03 +#define RH_MRF89_BRVAL 0x7f + +//#define RH_MRF89_REG_05_FIFOCREG 0x05 +#define RH_MRF89_FSIZE 0xc0 +#define RH_MRF89_FSIZE_64 0xc0 +#define RH_MRF89_FSIZE_48 0x80 +#define RH_MRF89_FSIZE_32 0x40 +#define RH_MRF89_FSIZE_16 0x00 + +#define RH_MRF89_FTINT 0x3f + +//#define RH_MRF89_REG_0C_PACREG 0x0c +#define RH_MRF89_PARC 0x18 +#define RH_MRF89_PARC_23 0x18 +#define RH_MRF89_PARC_15 0x10 +#define RH_MRF89_PARC_8P5 0x08 +#define RH_MRF89_PARC_3 0x00 + +//#define RH_MRF89_REG_0D_FTXRXIREG 0x0d +#define RH_MRF89_IRQ0RXS 0xc0 +#define RH_MRF89_IRQ0RXS_CONT_RSSI 0x40 +#define RH_MRF89_IRQ0RXS_CONT_SYNC 0x00 +#define RH_MRF89_IRQ0RXS_BUFFER_SYNC 0xc0 +#define RH_MRF89_IRQ0RXS_BUFFER_FIFOEMPTY 0x80 +#define RH_MRF89_IRQ0RXS_BUFFER_WRITEBYTE 0x40 +#define RH_MRF89_IRQ0RXS_BUFFER_NONE 0x00 +#define RH_MRF89_IRQ0RXS_PACKET_SYNC 0xc0 +#define RH_MRF89_IRQ0RXS_PACKET_FIFOEMPTY 0x80 +#define RH_MRF89_IRQ0RXS_PACKET_WRITEBYTE 0x40 +#define RH_MRF89_IRQ0RXS_PACKET_PLREADY 0x00 + +#define RH_MRF89_IRQ1RXS 0x30 +#define RH_MRF89_IRQ1RXS_CONT_DCLK 0x00 +#define RH_MRF89_IRQ1RXS_BUFFER_FIFO_THRESH 0x30 +#define RH_MRF89_IRQ1RXS_BUFFER_RSSI 0x20 +#define RH_MRF89_IRQ1RXS_BUFFER_FIFOFULL 0x10 +#define RH_MRF89_IRQ1RXS_BUFFER_NONE 0x00 +#define RH_MRF89_IRQ1RXS_PACKET_FIFO_THRESH 0x30 +#define RH_MRF89_IRQ1RXS_PACKET_RSSI 0x20 +#define RH_MRF89_IRQ1RXS_PACKET_FIFOFULL 0x10 +#define RH_MRF89_IRQ1RXS_PACKET_CRCOK 0x00 + +#define RH_MRF89_IRQ1TX 0x08 +#define RH_MRF89_FIFOFULL 0x04 +#define RH_MRF89_FIFOEMPTY 0x02 +#define RH_MRF89_FOVRUN 0x01 + +//#define RH_MRF89_REG_0E_FTPRIREG 0x0e +#define RH_MRF89_FIFOFM 0x80 +#define RH_MRF89_FIFOFSC 0x40 +#define RH_MRF89_TXDONE 0x20 +#define RH_MRF89_IRQ0TXST 0x10 +#define RH_MRF89_RIRQS 0x04 +#define RH_MRF89_LSTSPLL 0x02 +#define RH_MRF89_LENPLL 0x01 + +//#define RH_MRF89_REG_10_FILCREG 0x10 +#define RH_MRF89_PASFILV 0xf0 +#define RH_MRF89_PASFILV_987KHZ 0xf0 +#define RH_MRF89_PASFILV_676KHZ 0xe0 +#define RH_MRF89_PASFILV_514KHZ 0xd0 +#define RH_MRF89_PASFILV_458KHZ 0xc0 +#define RH_MRF89_PASFILV_414KHZ 0xb0 +#define RH_MRF89_PASFILV_378KHZ 0xa0 +#define RH_MRF89_PASFILV_321KHZ 0x90 +#define RH_MRF89_PASFILV_262KHZ 0x80 +#define RH_MRF89_PASFILV_234KHZ 0x70 +#define RH_MRF89_PASFILV_211KHZ 0x60 +#define RH_MRF89_PASFILV_184KHZ 0x50 +#define RH_MRF89_PASFILV_157KHZ 0x40 +#define RH_MRF89_PASFILV_137KHZ 0x30 +#define RH_MRF89_PASFILV_109KHZ 0x20 +#define RH_MRF89_PASFILV_82KHZ 0x10 +#define RH_MRF89_PASFILV_65KHZ 0x00 + +#define RH_MRF89_BUTFILV 0x0f +#define RH_MRF89_BUTFILV_25KHZ 0x00 +#define RH_MRF89_BUTFILV_50KHZ 0x01 +#define RH_MRF89_BUTFILV_75KHZ 0x02 +#define RH_MRF89_BUTFILV_100KHZ 0x03 +#define RH_MRF89_BUTFILV_125KHZ 0x04 +#define RH_MRF89_BUTFILV_150KHZ 0x05 +#define RH_MRF89_BUTFILV_175KHZ 0x06 +#define RH_MRF89_BUTFILV_200KHZ 0x07 +#define RH_MRF89_BUTFILV_225KHZ 0x08 +#define RH_MRF89_BUTFILV_250KHZ 0x09 +#define RH_MRF89_BUTFILV_275KHZ 0x0a +#define RH_MRF89_BUTFILV_300KHZ 0x0b +#define RH_MRF89_BUTFILV_325KHZ 0x0c +#define RH_MRF89_BUTFILV_350KHZ 0x0d +#define RH_MRF89_BUTFILV_375KHZ 0x0e +#define RH_MRF89_BUTFILV_400KHZ 0x0f + +//#define RH_MRF89_REG_11_PFCREG 0x11 +#define RH_MRF89_POLCFV 0xf0 + +//#define RH_MRF89_REG_12_SYNCREG 0x12 +#define RH_MRF89_POLFILEN 0x80 +#define RH_MRF89_BSYNCEN 0x40 +#define RH_MRF89_SYNCREN 0x20 +#define RH_MRF89_SYNCWSZ 0x18 +#define RH_MRF89_SYNCWSZ_32 0x18 +#define RH_MRF89_SYNCWSZ_24 0x10 +#define RH_MRF89_SYNCWSZ_16 0x08 +#define RH_MRF89_SYNCWSZ_8 0x00 +#define RH_MRF89_SYNCTEN 0x06 +#define RH_MRF89_SYNCTEN_3 0x06 +#define RH_MRF89_SYNCTEN_2 0x04 +#define RH_MRF89_SYNCTEN_1 0x02 +#define RH_MRF89_SYNCTEN_0 0x00 + +//#define RH_MRF89_REG_15_OOKCREG 0x15 +#define RH_MRF89_OOTHSV 0xe0 +#define RH_MRF89_OOTHSV_6P0DB 0xe0 +#define RH_MRF89_OOTHSV_5P0DB 0xc0 +#define RH_MRF89_OOTHSV_4P0DB 0xa0 +#define RH_MRF89_OOTHSV_3P0DB 0x80 +#define RH_MRF89_OOTHSV_2P0DB 0x60 +#define RH_MRF89_OOTHSV_1P5DB 0x40 +#define RH_MRF89_OOTHSV_1P0DB 0x20 +#define RH_MRF89_OOTHSV_0P5DB 0x00 + +#define RH_MRF89_OOKTHPV 0x1c +#define RH_MRF89_OOKTHPV_16 0x1c +#define RH_MRF89_OOKTHPV_8 0x18 +#define RH_MRF89_OOKTHPV_4 0x14 +#define RH_MRF89_OOKTHPV_2 0x10 +#define RH_MRF89_OOKTHPV_1_IN_8 0x0c +#define RH_MRF89_OOKTHPV_1_IN_4 0x08 +#define RH_MRF89_OOKTHPV_1_IN_2 0x04 +#define RH_MRF89_OOKTHPV_1_IN_1 0x00 + +#define RH_MRF89_OOKATHC 0x03 +#define RH_MRF89_OOKATHC_32PI 0x03 +#define RH_MRF89_OOKATHC_8PI 0x00 + +//#define RH_MRF89_REG_1A_TXCONREG 0x1a +#define RH_MRF89_TXIPOLFV 0xf0 + +#define RH_MRF89_TXOPVAL 0x0e +#define RH_MRF89_TXOPVAL_M8DBM 0x0e +#define RH_MRF89_TXOPVAL_M5DBM 0x0c +#define RH_MRF89_TXOPVAL_M2DBM 0x0a +#define RH_MRF89_TXOPVAL_1DBM 0x08 +#define RH_MRF89_TXOPVAL_4DBM 0x06 +#define RH_MRF89_TXOPVAL_7DBM 0x04 +#define RH_MRF89_TXOPVAL_10DBM 0x02 +#define RH_MRF89_TXOPVAL_13DBM 0x00 + +//#define RH_MRF89_REG_1B_CLKOREG 0x1b +#define RH_MRF89_CLKOCNTRL 0x80 +#define RH_MRF89_CLKOFREQ 0x7c + +//#define RH_MRF89_REG_1C_PLOADREG 0x1c +#define RH_MRF89_MCHSTREN 0x80 +#define RH_MRF89_PLDPLEN 0x7f + +//#define RH_MRF89_REG_1E_PKTCREG 0x1e +#define RH_MRF89_PKTLENF 0x80 + +#define RH_MRF89_PRESIZE 0x60 +#define RH_MRF89_PRESIZE_4 0x60 +#define RH_MRF89_PRESIZE_3 0x40 +#define RH_MRF89_PRESIZE_2 0x20 +#define RH_MRF89_PRESIZE_1 0x00 + +#define RH_MRF89_WHITEON 0x10 +#define RH_MRF89_CHKCRCEN 0x08 + +#define RH_MRF89_ADDFIL 0x06 +#define RH_MRF89_ADDFIL_NODEADDR_00_FF 0x06 +#define RH_MRF89_ADDFIL_NODEADDR_00 0x04 +#define RH_MRF89_ADDFIL_NODEADDR 0x02 +#define RH_MRF89_ADDFIL_OFF 0x00 + +#define RH_MRF89_STSCRCEN 0x01 + +//#define RH_MRF89_REG_1F_FCRCREG 0x1f +#define RH_MRF89_ACFCRC 0x80 +#define RH_MRF89_FRWAXS 0x40 + + +///////////////////////////////////////////////////////////////////// +/// \class RH_MRF89 RH_MRF89.h +/// \brief Send and receive unaddressed, unreliable datagrams by Microchip MRF89XA and compatible transceivers. +/// and modules. +/// +/// The Microchip MRF89XA http://ww1.microchip.com/downloads/en/DeviceDoc/70622C.pdf is a low cost 900MHz +/// bancd transceiver chip. +/// It is commonly used on preassembled modules with supporting circcuits and antennas, such as +/// the MRF89XAM9A http://www.microchip.com/wwwproducts/Devices.aspx?product=MRF89XAM9A +/// This class supports all such modules +/// +/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams +/// of arbitrary length to 59 octets per packet. Use one of the Manager classes to get addressing and +/// acknowledgement reliability, routing, meshes etc. +/// +/// Several MRF89XA modules can be connected to an Arduino, permitting the construction of translators +/// and frequency changers, etc. Each instance requires 2 chip select pins, and interrupt pin the standard 3 SPI pins. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// data rate, and with identical network addresses. +/// +/// Example Arduino programs are included to show the main modes of use. +/// +/// All messages sent and received by this class conform to this packet format: +/// +/// - 3 octets PREAMBLE +/// - 2 to 4 octets NETWORK ADDRESS (also call Sync Word) +/// - 1 octet message length bits packet control field +/// - 4 to 63 octets PAYLOAD, consisting of: +/// - 1 octet TO header +/// - 1 octet FROM header +/// - 1 octet ID header +/// - 1 octet FLAGS header +/// - 0 to 59 octets of user message +/// - 2 octets CRC +/// +/// The payload is whitened. No Manchester encoding is used. +/// +/// \par Connecting MRF89XA to Arduino +/// +/// The electrical connection between the MRF89XA and the Arduino require 3.3V, the 3 x SPI pins (SCK, SDI, SDO), +/// a 2 Chip Select pins (/CSCON and /CSDAT) and an interrupt. +/// +/// Caution: the MRF89XA is a 3.3V part and is not tolerant of 5V inputs. Connecting MRF89XA directly to a 5V +/// MCU such as most Arduinos will damage the MRF89XA. +/// +/// Connect the MRF89XA to most 3.3V Arduinos or Teensy 3.1 like this (use 3.3V not 5V). +/// \code +/// Teensy MRF89XAM9A +/// 3.3V-----------VIN (3.3V in) +/// pin D9-----------/CSDAT (data chip select in) +/// SS pin D10----------/CSCON (configuration chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// D2-----------IRQ1 (Interrupt 1 output) +/// IRQ0 (Interrupt 0 output, not connected) +/// GND----------GND (ground in) +/// \endcode +/// You can use other pins for /CSDAT, /CSCON, IRQ1 by passing appropriate arguments to the constructor. +/// +/// \par Example programs +/// +/// Several example programs are provided. +/// +class RH_MRF89 : public RHNRFSPIDriver +{ +public: + + /// \brief Defines register configuration values for a desired modulation + /// + /// Defines values for various configuration fields and registers to + /// achieve a desired modulation speed and frequency deviation. + typedef struct + { + uint8_t MODSEL; ///< Value for MODSEL in RH_MRF89_REG_01_DMODREG + uint8_t FDVAL; ///< Value for FDVAL in RH_MRF89_REG_02_FDEVREG + uint8_t BRVAL; ///< Value for BRVAL RH_MRF89_REG_03_BRSREG + uint8_t FILCREG; ///< Value for PASFILV | BUTFILV in RH_MRF89_REG_10_FILCREG + uint8_t TXIPOLFV; ///< Value for TXIPOLFV in RH_MRF89_REG_1A_TXCONREG + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common + /// data rates and frequency deviations. + /// Rb is the data rate in kbps. Fd is the FSK Frequency deviation in kHz. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + /// OOK is not yet supported. + /// Based on sample configs in MRF89XA.h from Microchip + typedef enum + { + FSK_Rb2Fd33 = 0, ///< FSK, No Manchester, Whitened, Rb = 2kbs, Fd = 33kHz + FSK_Rb5Fd33, ///< FSK, No Manchester, Whitened, Rb = 5kbs, Fd = 33kHz + FSK_Rb10Fd33, ///< FSK, No Manchester, Whitened, Rb = 10kbs, Fd = 33kHz + FSK_Rb20Fd40, ///< FSK, No Manchester, Whitened, Rb = 20kbs, Fd = 40kHz + FSK_Rb40Fd80, ///< FSK, No Manchester, Whitened, Rb = 40kbs, Fd = 80kHz + FSK_Rb50Fd100, ///< FSK, No Manchester, Whitened, Rb = 50kbs, Fd = 100kHz + FSK_Rb66Fd133, ///< FSK, No Manchester, Whitened, Rb = 66kbs, Fd = 133kHz + FSK_Rb100Fd200, ///< FSK, No Manchester, Whitened, Rb = 100kbs, Fd = 200kHz + FSK_Rb200Fd200 ///< FSK, No Manchester, Whitened, Rb = 200kbs, Fd = 200kHz + } ModemConfigChoice; + + /// Constructor. + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and 2 slave select pins. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] csconPin the Arduino pin number connected to the CSCON pin of the MRF89XA. + /// Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] csdatPin the Arduino pin number connected to the CSDAT pin of the MRF89XA. + /// Defaults to 9. + /// \param[in] interruptPin The interrupt Pin number that is connected to the IRQ1 pin of the MRF89XA. + /// Defaults to pin 2. (IRQ0 pin of the MRF89XA does not need to be connected). + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_MRF89(uint8_t csconPin = SS, uint8_t csdatPin = 9, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Prints the value of all chip registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegisters(); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode to idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the radio. + // the next valid packet received will cause available() to be true. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the radio. + void setModeTx(); + + /// Sets the transmitter power output level in register RH_MRF89_REG_1A_TXCONREG. + /// Be a good neighbour and set the lowest power level you need. + /// After init(), the power will be set to RH_MRF89_TXOPVAL_1DBM (1dBm) + /// The highest power available is RH_MRF89_TXOPVAL_13DBM (13dBm) + /// Caution: In some countries you may only select certain higher power levels if you + /// are also using frequency hopping. Make sure you are aware of the legal + /// limitations and regulations in your region. + /// Caution: in some countries the maximum permitted power level may depend on the Bit rate + /// \param[in] power Transmitter power level, one of RH_MRF89_TXOPVAL* + void setTxPower(uint8_t power); + + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it will be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send + /// \return true if the message length was valid and it was correctly queued for transmit + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// Sets the centre frequency in MHz. + /// Permitted ranges are: 902.0 to 928.0 and 950.0 to 960.0 (inclusive) + /// Caution not all freqs are supported on all modules: check your module specifications + /// Caution: not all available and supported frequencies are legal in every country: check + /// Regulatory Approval eg for MRF89XAM9A (in 75015B.pdf) + /// Australia 915.0 to 928.0 + bool setFrequency(float centre); + + /// Sets the length of the preamble + /// in bytes. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 4. + /// Sets the message preamble length in RH_MRF89_REG_1E_PKTCREG + /// \param[in] bytes Preamble length in bytes of 8 bits each. + void setPreambleLength(uint8_t bytes); + + /// Sets the sync words for transmit and receive in registers RH_MRF89_REG_16_SYNCV31REG + /// et seq. + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0x69, 0x81, 0x7e, 0x96 }. + /// Caution, sync words of 2 bytes and less do not work well with this chip. + /// \param[in] syncWords Array of sync words, 1 to 4 octets long + /// \param[in] len Number of sync words to set, 1 to 4. + void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0); + +protected: + + /// Called automatically when a CRCOK or TXDONE interrupt occurs. + /// Handles the interrupt. + void handleInterrupt(); + + /// Reads a single register from the MRF89XA + /// \param[in] reg Register number, one of RH_MRF89_REG + /// \return The value of the register + uint8_t spiReadRegister(uint8_t reg); + + /// Writes to a single single register on the MRF89XA + /// \param[in] reg Register number, one of RH_MRF89_REG_* + /// \param[in] val The value to write + /// \return the current value of RH_MRF89_REG_00_GCONREG (read while the command is sent) + uint8_t spiWriteRegister(uint8_t reg, uint8_t val); + + /// Writes a single byte to the MRF89XA data FIFO. + /// \param[in] data The data value to write + /// \return 0 + uint8_t spiWriteData(uint8_t data); + + /// Write a number of bytes from a buffer to the MRF89XA data FIFO. + /// \param[in] data Pointer to a buffer containing the len bytes to be written + /// \param[in] len The number of bytes to write to teh FIFO + /// \return 0; + uint8_t spiWriteData(const uint8_t* data, uint8_t len); + + /// Reads a single byte from the MRF89XA data FIFO. + /// \return The next data byte in the FIFO + uint8_t spiReadData(); + + /// Sets the operating mode in the CMOD bits in RH_MRF89_REG_00_GCONREG + /// which controls what mode the MRF89XA is running in + /// \param[in] mode One of RH_MRF89_CMOD_* + void setOpMode(uint8_t mode); + + /// Verifies that the MRF89XA PLL has locked on the slected frequency. + /// This needs to be called if the frequency is changed + bool verifyPLLLock(); + + /// Examine the revceive buffer to determine whether the message is for this node + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + + +private: + /// Low level interrupt service routine for device connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_MRF89* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + // Sigh: this chip has 2 differnt chip selects. + // We have to set one or the other as the SPI slave select pin depending + // on which block of registers we are accessing + uint8_t _csconPin; + uint8_t _csdatPin; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// Number of octets in the buffer + volatile uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_MRF89_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + volatile bool _rxBufValid; + +}; + +/// @example mrf89_client.pde +/// @example mrf89_server.pde + +#endif diff --git a/RH_NRF24.cpp b/RH_NRF24.cpp new file mode 100644 index 0000000..dabf619 --- /dev/null +++ b/RH_NRF24.cpp @@ -0,0 +1,349 @@ +// NRF24.cpp +// +// Copyright (C) 2012 Mike McCauley +// $Id: RH_NRF24.cpp,v 1.26 2018/01/06 23:50:45 mikem Exp $ + +#include + +RH_NRF24::RH_NRF24(uint8_t chipEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi) + : + RHNRFSPIDriver(slaveSelectPin, spi), + _rxBufValid(0) +{ + _configuration = RH_NRF24_EN_CRC | RH_NRF24_CRCO; // Default: 2 byte CRC enabled + _chipEnablePin = chipEnablePin; +} + +bool RH_NRF24::init() +{ + // Teensy with nRF24 is unreliable at 8MHz: + // so is Arduino with RF73 + _spi.setFrequency(RHGenericSPI::Frequency1MHz); + if (!RHNRFSPIDriver::init()) + return false; + + // Initialise the slave select pin + pinMode(_chipEnablePin, OUTPUT); + digitalWrite(_chipEnablePin, LOW); + + // Clear interrupts + spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR | RH_NRF24_TX_DS | RH_NRF24_MAX_RT); + // Enable dynamic payload length on all pipes + spiWriteRegister(RH_NRF24_REG_1C_DYNPD, RH_NRF24_DPL_ALL); + // Enable dynamic payload length, disable payload-with-ack, enable noack + spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK); + // Test if there is actually a device connected and responding + // CAUTION: RFM73 and version 2.0 silicon may require ACTIVATE + if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK)) + { + spiWrite(RH_NRF24_COMMAND_ACTIVATE, 0x73); + // Enable dynamic payload length, disable payload-with-ack, enable noack + spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK); + if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK)) + return false; + } + + clearRxBuf(); + + // Make sure we are powered down + setModeIdle(); + + // Flush FIFOs + flushTx(); + flushRx(); + + setChannel(2); // The default, in case it was set by another app without powering down + setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm); + + return true; +} + +// Use the register commands to read and write the registers +uint8_t RH_NRF24::spiReadRegister(uint8_t reg) +{ + return spiRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER); +} + +uint8_t RH_NRF24::spiWriteRegister(uint8_t reg, uint8_t val) +{ + return spiWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, val); +} + +uint8_t RH_NRF24::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len) +{ + return spiBurstRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER, dest, len); +} + +uint8_t RH_NRF24::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len) +{ + return spiBurstWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, src, len); +} + +uint8_t RH_NRF24::statusRead() +{ + // status is a side-effect of NOP, faster than reading reg 07 + return spiCommand(RH_NRF24_COMMAND_NOP); +} + +uint8_t RH_NRF24::flushTx() +{ + return spiCommand(RH_NRF24_COMMAND_FLUSH_TX); +} + +uint8_t RH_NRF24::flushRx() +{ + return spiCommand(RH_NRF24_COMMAND_FLUSH_RX); +} + +bool RH_NRF24::setChannel(uint8_t channel) +{ + spiWriteRegister(RH_NRF24_REG_05_RF_CH, channel & RH_NRF24_RF_CH); + return true; +} + +bool RH_NRF24::setOpMode(uint8_t mode) +{ + _configuration = mode; + return true; +} + +bool RH_NRF24::setNetworkAddress(uint8_t* address, uint8_t len) +{ + if (len < 3 || len > 5) + return false; + + // Set both TX_ADDR and RX_ADDR_P0 for auto-ack with Enhanced shockwave + spiWriteRegister(RH_NRF24_REG_03_SETUP_AW, len-2); // Mapping [3..5] = [1..3] + spiBurstWriteRegister(RH_NRF24_REG_0A_RX_ADDR_P0, address, len); + spiBurstWriteRegister(RH_NRF24_REG_10_TX_ADDR, address, len); + return true; +} + +bool RH_NRF24::setRF(DataRate data_rate, TransmitPower power) +{ + uint8_t value = (power << 1) & RH_NRF24_PWR; + // Ugly mapping of data rates to noncontiguous 2 bits: + if (data_rate == DataRate250kbps) + value |= RH_NRF24_RF_DR_LOW; + else if (data_rate == DataRate2Mbps) + value |= RH_NRF24_RF_DR_HIGH; + // else DataRate1Mbps, 00 + + // RFM73 needs this: + value |= RH_NRF24_LNA_HCURR; + + spiWriteRegister(RH_NRF24_REG_06_RF_SETUP, value); + // If we were using auto-ack, we would have to set the appropriate timeout in reg 4 here + // see NRF24::setRF() + return true; +} + +void RH_NRF24::setModeIdle() +{ + if (_mode != RHModeIdle) + { + spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration); + digitalWrite(_chipEnablePin, LOW); + _mode = RHModeIdle; + } +} + +bool RH_NRF24::sleep() +{ + if (_mode != RHModeSleep) + { + spiWriteRegister(RH_NRF24_REG_00_CONFIG, 0); // Power Down mode + digitalWrite(_chipEnablePin, LOW); + _mode = RHModeSleep; + return true; + } + return false; // Already there? +} + +void RH_NRF24::setModeRx() +{ + if (_mode != RHModeRx) + { + spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP | RH_NRF24_PRIM_RX); + digitalWrite(_chipEnablePin, HIGH); + _mode = RHModeRx; + } +} + +void RH_NRF24::setModeTx() +{ + if (_mode != RHModeTx) + { + // Its the CE rising edge that puts us into TX mode + // CE staying high makes us go to standby-II when the packet is sent + digitalWrite(_chipEnablePin, LOW); + // Ensure DS is not set + spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT); + spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP); + digitalWrite(_chipEnablePin, HIGH); + _mode = RHModeTx; + } +} + +bool RH_NRF24::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_NRF24_MAX_MESSAGE_LEN) + return false; + + if (!waitCAD()) + return false; // Check channel activity + + // Set up the headers + _buf[0] = _txHeaderTo; + _buf[1] = _txHeaderFrom; + _buf[2] = _txHeaderId; + _buf[3] = _txHeaderFlags; + memcpy(_buf+RH_NRF24_HEADER_LEN, data, len); + spiBurstWrite(RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK, _buf, len + RH_NRF24_HEADER_LEN); + setModeTx(); + // Radio will return to Standby II mode after transmission is complete + _txGood++; + return true; +} + +bool RH_NRF24::waitPacketSent() +{ + // If we are not currently in transmit mode, there is no packet to wait for + if (_mode != RHModeTx) + return false; + + // Wait for either the Data Sent or Max ReTries flag, signalling the + // end of transmission + // We dont actually use auto-ack, so prob dont expect to see RH_NRF24_MAX_RT + uint8_t status; + uint32_t start = millis(); + while (!((status = statusRead()) & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT))) + { + if (((uint32_t)millis() - start) > 100) // Longer than any possible message + break; // Should never happen: TX never completed. Why? + YIELD; + } + + // Must clear RH_NRF24_MAX_RT if it is set, else no further comm + if (status & RH_NRF24_MAX_RT) + flushTx(); + setModeIdle(); + spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT); + // Return true if data sent, false if MAX_RT + return status & RH_NRF24_TX_DS; +} + +bool RH_NRF24::isSending() +{ + return !(spiReadRegister(RH_NRF24_REG_00_CONFIG) & RH_NRF24_PRIM_RX) && + !(statusRead() & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT)); +} + +bool RH_NRF24::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + // Iterate over register range, but don't process registers not in use. + for (uint8_t r = RH_NRF24_REG_00_CONFIG; r <= RH_NRF24_REG_1D_FEATURE; r++) + { + if ((r <= RH_NRF24_REG_17_FIFO_STATUS) || (r >= RH_NRF24_REG_1C_DYNPD)) + { + Serial.print(r, HEX); + Serial.print(": "); + uint8_t len = 1; + // Address registers are 5 bytes in size + if ( (RH_NRF24_REG_0A_RX_ADDR_P0 == r) + || (RH_NRF24_REG_0B_RX_ADDR_P1 == r) + || (RH_NRF24_REG_10_TX_ADDR == r) ) + { + len = 5; + } + uint8_t buf[5]; + spiBurstReadRegister(r, buf, len); + for (uint8_t j = 0; j < len; ++j) + { + Serial.print(buf[j], HEX); + Serial.print(" "); + } + Serial.println(""); + } + } +#endif + + return true; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_NRF24::validateRxBuf() +{ + if (_bufLen < 4) + return; // Too short to be a real message + // Extract the 4 headers + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +bool RH_NRF24::available() +{ + if (!_rxBufValid) + { + if (_mode == RHModeTx) + return false; + setModeRx(); + if (spiReadRegister(RH_NRF24_REG_17_FIFO_STATUS) & RH_NRF24_RX_EMPTY) + return false; + // Manual says that messages > 32 octets should be discarded + uint8_t len = spiRead(RH_NRF24_COMMAND_R_RX_PL_WID); + if (len > 32) + { + flushRx(); + clearRxBuf(); + setModeIdle(); + return false; + } + // Clear read interrupt + spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR); + // Get the message into the RX buffer, so we can inspect the headers + spiBurstRead(RH_NRF24_COMMAND_R_RX_PAYLOAD, _buf, len); + _bufLen = len; + // 140 microsecs (32 octet payload) + validateRxBuf(); + if (_rxBufValid) + setModeIdle(); // Got one + } + return _rxBufValid; +} + +void RH_NRF24::clearRxBuf() +{ + _rxBufValid = false; + _bufLen = 0; +} + +bool RH_NRF24::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen-RH_NRF24_HEADER_LEN) + *len = _bufLen-RH_NRF24_HEADER_LEN; + memcpy(buf, _buf+RH_NRF24_HEADER_LEN, *len); + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +uint8_t RH_NRF24::maxMessageLength() +{ + return RH_NRF24_MAX_MESSAGE_LEN; +} diff --git a/RH_NRF24.h b/RH_NRF24.h new file mode 100644 index 0000000..9864284 --- /dev/null +++ b/RH_NRF24.h @@ -0,0 +1,647 @@ +// RH_NRF24.h +// Author: Mike McCauley +// Copyright (C) 2012 Mike McCauley +// $Id: RH_NRF24.h,v 1.21 2020/06/15 23:39:39 mikem Exp $ +// + +#ifndef RH_NRF24_h +#define RH_NRF24_h + +#include +#include + +// This is the maximum number of bytes that can be carried by the nRF24. +// We use some for headers, keeping fewer for RadioHead messages +#define RH_NRF24_MAX_PAYLOAD_LEN 32 + +// The length of the headers we add. +// The headers are inside the nRF24 payload +#define RH_NRF24_HEADER_LEN 4 + +// This is the maximum RadioHead user message length that can be supported by this library. Limited by +// the supported message lengths in the nRF24 +#define RH_NRF24_MAX_MESSAGE_LEN (RH_NRF24_MAX_PAYLOAD_LEN-RH_NRF24_HEADER_LEN) + +// SPI Command names +#define RH_NRF24_COMMAND_R_REGISTER 0x00 +#define RH_NRF24_COMMAND_W_REGISTER 0x20 +#define RH_NRF24_COMMAND_ACTIVATE 0x50 // only on RFM73 ? +#define RH_NRF24_COMMAND_R_RX_PAYLOAD 0x61 +#define RH_NRF24_COMMAND_W_TX_PAYLOAD 0xa0 +#define RH_NRF24_COMMAND_FLUSH_TX 0xe1 +#define RH_NRF24_COMMAND_FLUSH_RX 0xe2 +#define RH_NRF24_COMMAND_REUSE_TX_PL 0xe3 +#define RH_NRF24_COMMAND_R_RX_PL_WID 0x60 +#define RH_NRF24_COMMAND_W_ACK_PAYLOAD(pipe) (0xa8|(pipe&0x7)) +#define RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK 0xb0 +#define RH_NRF24_COMMAND_NOP 0xff + +// Register names +#define RH_NRF24_REGISTER_MASK 0x1f +#define RH_NRF24_REG_00_CONFIG 0x00 +#define RH_NRF24_REG_01_EN_AA 0x01 +#define RH_NRF24_REG_02_EN_RXADDR 0x02 +#define RH_NRF24_REG_03_SETUP_AW 0x03 +#define RH_NRF24_REG_04_SETUP_RETR 0x04 +#define RH_NRF24_REG_05_RF_CH 0x05 +#define RH_NRF24_REG_06_RF_SETUP 0x06 +#define RH_NRF24_REG_07_STATUS 0x07 +#define RH_NRF24_REG_08_OBSERVE_TX 0x08 +#define RH_NRF24_REG_09_RPD 0x09 +#define RH_NRF24_REG_0A_RX_ADDR_P0 0x0a +#define RH_NRF24_REG_0B_RX_ADDR_P1 0x0b +#define RH_NRF24_REG_0C_RX_ADDR_P2 0x0c +#define RH_NRF24_REG_0D_RX_ADDR_P3 0x0d +#define RH_NRF24_REG_0E_RX_ADDR_P4 0x0e +#define RH_NRF24_REG_0F_RX_ADDR_P5 0x0f +#define RH_NRF24_REG_10_TX_ADDR 0x10 +#define RH_NRF24_REG_11_RX_PW_P0 0x11 +#define RH_NRF24_REG_12_RX_PW_P1 0x12 +#define RH_NRF24_REG_13_RX_PW_P2 0x13 +#define RH_NRF24_REG_14_RX_PW_P3 0x14 +#define RH_NRF24_REG_15_RX_PW_P4 0x15 +#define RH_NRF24_REG_16_RX_PW_P5 0x16 +#define RH_NRF24_REG_17_FIFO_STATUS 0x17 +#define RH_NRF24_REG_1C_DYNPD 0x1c +#define RH_NRF24_REG_1D_FEATURE 0x1d + +// These register masks etc are named wherever possible +// corresponding to the bit and field names in the nRF24L01 Product Specification +// #define RH_NRF24_REG_00_CONFIG 0x00 +#define RH_NRF24_MASK_RX_DR 0x40 +#define RH_NRF24_MASK_TX_DS 0x20 +#define RH_NRF24_MASK_MAX_RT 0x10 +#define RH_NRF24_EN_CRC 0x08 +#define RH_NRF24_CRCO 0x04 +#define RH_NRF24_PWR_UP 0x02 +#define RH_NRF24_PRIM_RX 0x01 + +// #define RH_NRF24_REG_01_EN_AA 0x01 +#define RH_NRF24_ENAA_P5 0x20 +#define RH_NRF24_ENAA_P4 0x10 +#define RH_NRF24_ENAA_P3 0x08 +#define RH_NRF24_ENAA_P2 0x04 +#define RH_NRF24_ENAA_P1 0x02 +#define RH_NRF24_ENAA_P0 0x01 + +// #define RH_NRF24_REG_02_EN_RXADDR 0x02 +#define RH_NRF24_ERX_P5 0x20 +#define RH_NRF24_ERX_P4 0x10 +#define RH_NRF24_ERX_P3 0x08 +#define RH_NRF24_ERX_P2 0x04 +#define RH_NRF24_ERX_P1 0x02 +#define RH_NRF24_ERX_P0 0x01 + +// #define RH_NRF24_REG_03_SETUP_AW 0x03 +#define RH_NRF24_AW_3_BYTES 0x01 +#define RH_NRF24_AW_4_BYTES 0x02 +#define RH_NRF24_AW_5_BYTES 0x03 + +// #define RH_NRF24_REG_04_SETUP_RETR 0x04 +#define RH_NRF24_ARD 0xf0 +#define RH_NRF24_ARC 0x0f + +// #define RH_NRF24_REG_05_RF_CH 0x05 +#define RH_NRF24_RF_CH 0x7f + +// #define RH_NRF24_REG_06_RF_SETUP 0x06 +#define RH_NRF24_CONT_WAVE 0x80 +#define RH_NRF24_RF_DR_LOW 0x20 +#define RH_NRF24_PLL_LOCK 0x10 +#define RH_NRF24_RF_DR_HIGH 0x08 +#define RH_NRF24_PWR 0x06 +#define RH_NRF24_PWR_m18dBm 0x00 +#define RH_NRF24_PWR_m12dBm 0x02 +#define RH_NRF24_PWR_m6dBm 0x04 +#define RH_NRF24_PWR_0dBm 0x06 +#define RH_NRF24_LNA_HCURR 0x01 + +// #define RH_NRF24_REG_07_STATUS 0x07 +#define RH_NRF24_RX_DR 0x40 +#define RH_NRF24_TX_DS 0x20 +#define RH_NRF24_MAX_RT 0x10 +#define RH_NRF24_RX_P_NO 0x0e +#define RH_NRF24_STATUS_TX_FULL 0x01 + +// #define RH_NRF24_REG_08_OBSERVE_TX 0x08 +#define RH_NRF24_PLOS_CNT 0xf0 +#define RH_NRF24_ARC_CNT 0x0f + +// #define RH_NRF24_REG_09_RPD 0x09 +#define RH_NRF24_RPD 0x01 + +// #define RH_NRF24_REG_17_FIFO_STATUS 0x17 +#define RH_NRF24_TX_REUSE 0x40 +#define RH_NRF24_TX_FULL 0x20 +#define RH_NRF24_TX_EMPTY 0x10 +#define RH_NRF24_RX_FULL 0x02 +#define RH_NRF24_RX_EMPTY 0x01 + +// #define RH_NRF24_REG_1C_DYNPD 0x1c +#define RH_NRF24_DPL_ALL 0x3f +#define RH_NRF24_DPL_P5 0x20 +#define RH_NRF24_DPL_P4 0x10 +#define RH_NRF24_DPL_P3 0x08 +#define RH_NRF24_DPL_P2 0x04 +#define RH_NRF24_DPL_P1 0x02 +#define RH_NRF24_DPL_P0 0x01 + +// #define RH_NRF24_REG_1D_FEATURE 0x1d +#define RH_NRF24_EN_DPL 0x04 +#define RH_NRF24_EN_ACK_PAY 0x02 +#define RH_NRF24_EN_DYN_ACK 0x01 + + +///////////////////////////////////////////////////////////////////// +/// \class RH_NRF24 RH_NRF24.h +/// \brief Send and receive unaddressed, unreliable datagrams by nRF24L01 and compatible transceivers. +/// +/// Supported transceivers include: +/// - Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01 +/// and other compatible transceivers. +/// - nRF24L01p with PA and LNA modules that produce a higher power output similar to this one: +/// http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA +/// - Sparkfun WRL-00691 module with nRF24L01 https://www.sparkfun.com/products/691 +/// or WRL-00705 https://www.sparkfun.com/products/705 etc. +/// - Hope-RF RFM73 http://www.hoperf.com/rf/2.4g_module/RFM73.htm and +/// http://www.anarduino.com/details.jsp?pid=121 +/// and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate +/// with each other. +/// +/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams +/// of arbitrary length to 28 octets per packet. Use one of the Manager classes to get addressing and +/// acknowledgement reliability, routing, meshes etc. +/// +/// The nRF24L01 (http://www.sparkfun.com/datasheets/Wireless/Nordic/nRF24L01P_Product_Specification_1_0.pdf) +/// is a low-cost 2.4GHz ISM transceiver module. It supports a number of channel frequencies in the 2.4GHz band +/// and a range of data rates. +/// +/// This library provides functions for sending and receiving messages of up to 28 octets on any +/// frequency supported by the nRF24L01, at a selected data rate. +/// +/// Several nRF24L01 modules can be connected to an Arduino, permitting the construction of translators +/// and frequency changers, etc. +/// +/// The nRF24 transceiver is configured to use Enhanced Shockburst with no acknowledgement and no retransmits. +/// TX_ADDR and RX_ADDR_P0 are set to the network address. If you need the low level auto-acknowledgement +/// feature supported by this chip, you can use our original NRF24 library +/// at http://www.airspayce.com/mikem/arduino/NRF24 +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// data rate, and with identical network addresses. +/// +/// Example Arduino programs are included to show the main modes of use. +/// +/// \par Packet Format +/// +/// All messages sent and received by this class conform to this packet format, as specified by +/// the nRF24L01 product specification: +/// +/// - 1 octets PREAMBLE +/// - 3 to 5 octets NETWORK ADDRESS +/// - 9 bits packet control field +/// - 0 to 32 octets PAYLOAD, consisting of: +/// - 1 octet TO header +/// - 1 octet FROM header +/// - 1 octet ID header +/// - 1 octet FLAGS header +/// - 0 to 28 octets of user message +/// - 2 octets CRC +/// +/// \par Connecting nRF24L01 to Arduino +/// +/// The electrical connection between the nRF24L01 and the Arduino require 3.3V, the 3 x SPI pins (SCK, SDI, SDO), +/// a Chip Enable pin and a Slave Select pin. +/// If you are using the Sparkfun WRL-00691 module, it has a voltage regulator on board and +/// can be should with 5V VCC if possible. +/// The examples below assume the Sparkfun WRL-00691 module +/// +/// Connect the nRF24L01 to most Arduino's like this (Caution, Arduino Mega has different pins for SPI, +/// see below). Use these same connections for Teensy 3.1 (use 3.3V not 5V Vcc). +/// \code +/// Arduino Sparkfun WRL-00691 +/// 5V-----------VCC (3.3V to 7V in) +/// pin D8-----------CE (chip enable in) +/// SS pin D10----------CSN (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND----------GND (ground in) +/// \endcode +/// +/// For an Arduino Leonardo (the SPI pins do not come out on the Digital pins as for normal Arduino, but only +/// appear on the ICSP header) +/// \code +/// Leonardo Sparkfun WRL-00691 +/// 5V-----------VCC (3.3V to 7V in) +/// pin D8-----------CE (chip enable in) +/// SS pin D10----------CSN (chip select in) +/// SCK ICSP pin 3----------SCK (SPI clock in) +/// MOSI ICSP pin 4----------SDI (SPI Data in) +/// MISO ICSP pin 1----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND----------GND (ground in) +/// \endcode +/// and initialise the NRF24 object like this to explicitly set the SS pin +/// NRF24 nrf24(8, 10); +/// +/// For an Arduino Due (the SPI pins do not come out on the Digital pins as for normal Arduino, but only +/// appear on the SPI header). Use the same connections for Yun with 5V or 3.3V. +/// \code +/// Due Sparkfun WRL-00691 +/// 3.3V-----------VCC (3.3V to 7V in) +/// pin D8-----------CE (chip enable in) +/// SS pin D10----------CSN (chip select in) +/// SCK SPI pin 3----------SCK (SPI clock in) +/// MOSI SPI pin 4----------SDI (SPI Data in) +/// MISO SPI pin 1----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND----------GND (ground in) +/// \endcode +/// and initialise the NRF24 object with the default constructor +/// NRF24 nrf24; +/// +/// For an Arduino Mega: +/// \code +/// Mega Sparkfun WRL-00691 +/// 5V-----------VCC (3.3V to 7V in) +/// pin D8-----------CE (chip enable in) +/// SS pin D53----------CSN (chip select in) +/// SCK pin D52----------SCK (SPI clock in) +/// MOSI pin D51----------SDI (SPI Data in) +/// MISO pin D50----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND----------GND (ground in) +/// \endcode +/// and you can then use the constructor RH_NRF24(8, 53). +/// +/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by hardware SPI to the +/// ITDB02 Parallel LCD Module Interface pins: +/// \code +/// IBoard Signal=ITDB02 pin Sparkfun WRL-00691 +/// 3.3V 37-----------VCC (3.3V to 7V in) +/// D2 28-----------CE (chip enable in) +/// D29 27----------CSN (chip select in) +/// SCK D52 32----------SCK (SPI clock in) +/// MOSI D51 34----------SDI (SPI Data in) +/// MISO D50 30----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND 39----------GND (ground in) +/// \endcode +/// And initialise like this: +/// \code +/// RH_NRF24 nrf24(2, 29); +/// \endcode +/// +/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by software SPI to the +/// nRF24L01+ Module Interface pins. CAUTION: performance of software SPI is very slow and is not +/// compatible with other modules running hardware SPI. +/// \code +/// IBoard Signal=Module pin Sparkfun WRL-00691 +/// 3.3V 2----------VCC (3.3V to 7V in) +/// D12 3-----------CE (chip enable in) +/// D29 4----------CSN (chip select in) +/// D9 5----------SCK (SPI clock in) +/// D8 6----------SDI (SPI Data in) +/// D7 7----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND 1----------GND (ground in) +/// \endcode +/// And initialise like this: +/// \code +/// #include +/// #include +/// #include +/// Singleton instance of the radio driver +/// RHSoftwareSPI spi; +/// RH_NRF24 nrf24(12, 11, spi); +/// void setup() { +/// spi.setPins(7, 8, 9); +/// .... +/// \endcode +/// +/// +/// For Raspberry Pi with Sparkfun WRL-00691 +/// \code +/// Raspberry Pi P1 pin Sparkfun WRL-00691 +/// 5V 2-----------VCC (3.3V to 7V in) +/// GPIO25 22-----------CE (chip enable in) +/// GPIO8 24----------CSN (chip select in) +/// GPIO11 23----------SCK (SPI clock in) +/// GPIO10 19----------SDI (SPI Data in) +/// GPIO9 21----------SDO (SPI data out) +/// IRQ (Interrupt output, not connected) +/// GND 6----------GND (ground in) +/// \endcode +/// and initialise like this: +/// \code +/// RH_NRF24 nrf24(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24); +/// \endcode +/// See the example program and Makefile in examples/raspi. Requires bcm2835 library to be previously installed. +/// \code +/// cd examples/raspi +/// make +/// sudo ./RasPiRH +/// \endcode +/// \code +/// +/// You can override the default settings for the CSN and CE pins +/// in the NRF24() constructor if you wish to connect the slave select CSN to other than the normal one for your +/// +/// Caution: on the Raspberry Pi Zero, the hardware SPI, is only connected to the +/// ICSP-header. So in order to use the RF, one must either connect it to the SPI-pins +/// of the ICSP-header or use the software SPI provided by RHSoftwareSPI. +/// the mapping of the SPI-Pins for each board here: +/// https://www.arduino.cc/en/Reference/SPI +/// Arduino (D10 for Diecimila, Uno etc and D53 for Mega) +/// +/// Caution: on some Arduinos such as the Mega 2560, if you set the slave select pin to be other than the usual SS +/// pin (D53 on Mega 2560), you may need to set the usual SS pin to be an output to force the Arduino into SPI +/// master mode. +/// +/// Caution: this module has not been proved to work with Leonardo, at least without level +/// shifters between the nRF24 and the Leonardo. Tests seem to indicate that such level shifters would be required +/// with Leonardo to make it work. +/// +/// It is possible to have 2 radios conected to one arduino, provided each radio has its own +/// CSN and CE line (SCK, SDI and SDO are common to both radios) +/// +/// \par SPI Interface +/// +/// You can interface to nRF24L01 with with hardware or software SPI. Use of software SPI with the RHSoftwareSPI +/// class depends on a fast enough processor and digitalOut() functions to achieve a high enough SPI bus frequency. +/// If you observe reliable behaviour with the default hardware SPI RHHardwareSPI, but unreliable behaviour +/// with Software SPI RHSoftwareSPI, it may be due to slow CPU performance. +/// +/// Initialisation example with hardware SPI +/// \code +/// #include +/// RH_NRF24 driver; +/// RHReliableDatagram manager(driver, CLIENT_ADDRESS); +/// \endcode +/// +/// Initialisation example with software SPI +/// \code +/// #include +/// #include +/// RHSoftwareSPI spi; +/// RH_NRF24 driver(8, 10, spi); +/// RHReliableDatagram manager(driver, CLIENT_ADDRESS); +/// \endcode +/// +/// \par Example programs +/// +/// Several example programs are provided. +/// +/// \par Radio Performance +/// +/// Frequency accuracy may be debatable. For nominal frequency of 2401.000 MHz (ie channel 1), +/// my Yaesu VR-5000 receiver indicated the center frequency for my test radios +/// was 2401.121 MHz. Its not clear to me if the Yaesu +/// is the source of the error, but I tend to believe it, which would make the nRF24l01 frequency out by 121kHz. +/// +/// The measured power output for a nRF24L01p with PA and LNA set to 0dBm output is about 18dBm. +/// +/// \par Radio operating strategy and defaults +/// +/// The radio is enabled all the time, and switched between TX and RX modes depending on +/// whether there is any data to send. Sending data sets the radio to TX mode. +/// After data is sent, the radio automatically returns to Standby II mode. Calling waitAvailable() or +/// waitAvailableTimeout() starts the radio in RX mode. +/// +/// The radio is configured by default to Channel 2, 2Mbps, 0dBm power, 5 bytes address, payload width 1, CRC enabled +/// 2 byte CRC, No Auto-Ack mode. Enhanced shockburst is used. +/// TX and P0 are set to the Network address. Node addresses and decoding are handled with the RH_NRF24 module. +/// +/// \par Memory +/// +/// Memory usage of this class is minimal. The compiled client and server sketches are about 6000 bytes on Arduino. +/// The reliable client and server sketches compile to about 8500 bytes on Arduino. +/// RAM requirements are minimal. +/// +class RH_NRF24 : public RHNRFSPIDriver +{ +public: + + /// \brief Defines convenient values for setting data rates in setRF() + typedef enum + { + DataRate1Mbps = 0, ///< 1 Mbps + DataRate2Mbps, ///< 2 Mbps + DataRate250kbps ///< 250 kbps + } DataRate; + + /// \brief Convenient values for setting transmitter power in setRF() + /// These are designed to agree with the values for RF_PWR in RH_NRF24_REG_06_RF_SETUP + /// To be passed to setRF(); + typedef enum + { + // Add 20dBm for nRF24L01p with PA and LNA modules + TransmitPowerm18dBm = 0, ///< On nRF24, -18 dBm + TransmitPowerm12dBm, ///< On nRF24, -12 dBm + TransmitPowerm6dBm, ///< On nRF24, -6 dBm + TransmitPower0dBm, ///< On nRF24, 0 dBm + // Sigh, different power levels for the same bit patterns on RFM73: + // On RFM73P-S, there is a Tx power amp, so expect higher power levels, up to 20dBm. Alas + // there is no clear documentation on the power for different settings :-( + RFM73TransmitPowerm10dBm = 0, ///< On RFM73, -10 dBm + RFM73TransmitPowerm5dBm, ///< On RFM73, -5 dBm + RFM73TransmitPowerm0dBm, ///< On RFM73, 0 dBm + RFM73TransmitPower5dBm ///< On RFM73, 5 dBm. 20dBm on RFM73P-S2 ? + + } TransmitPower; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// chip enable and slave select pin. + /// After constructing, you must call init() to initialise the interface + /// and the radio module + /// \param[in] chipEnablePin the Arduino pin to use to enable the chip for transmit/receive + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the NRF24 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, + /// D10 for Maple) + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_NRF24(uint8_t chipEnablePin = 8, uint8_t slaveSelectPin = SS, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken:g + /// - Set the chip enable and chip select pins to output LOW, HIGH respectively. + /// - Initialise the SPI output pins + /// - Initialise the SPI interface library to 8MHz (Hint, if you want to lower + /// the SPI frequency (perhaps where you have other SPI shields, low voltages etc), + /// call SPI.setClockDivider() after init()). + /// -Flush the receiver and transmitter buffers + /// - Set the radio to receive with powerUpRx(); + /// \return true if everything was successful + bool init(); + + /// Reads a single register from the NRF24 + /// \param[in] reg Register number, one of RH_NRF24_REG_* + /// \return The value of the register + uint8_t spiReadRegister(uint8_t reg); + + /// Writes a single byte to the NRF24, and at the same time reads the current STATUS register + /// \param[in] reg Register number, one of RH_NRF24_REG_* + /// \param[in] val The value to write + /// \return the current STATUS (read while the command is sent) + uint8_t spiWriteRegister(uint8_t reg, uint8_t val); + + /// Reads a number of consecutive registers from the NRF24 using burst read mode + /// \param[in] reg Register number of the first register, one of RH_NRF24_REG_* + /// \param[in] dest Array to write the register values to. Must be at least len bytes + /// \param[in] len Number of bytes to read + /// \return the current STATUS (read while the command is sent) + uint8_t spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len); + + /// Write a number of consecutive registers using burst write mode + /// \param[in] reg Register number of the first register, one of RH_NRF24_REG_* + /// \param[in] src Array of new register values to write. Must be at least len bytes + /// \param[in] len Number of bytes to write + /// \return the current STATUS (read while the command is sent) + uint8_t spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len); + + /// Reads and returns the device status register NRF24_REG_02_DEVICE_STATUS + /// \return The value of the device status register + uint8_t statusRead(); + + /// Sets the transmit and receive channel number. + /// The frequency used is (2400 + channel) MHz + /// \return true on success + bool setChannel(uint8_t channel); + + /// Sets the chip configuration that will be used to set + /// the NRF24 NRF24_REG_00_CONFIG register when in Idle mode. This allows you to change some + /// chip configuration for compatibility with libraries other than this one. + /// You should not normally need to call this. + /// Defaults to NRF24_EN_CRC| RH_NRF24_CRCO, which is the standard configuration for this library + /// (2 byte CRC enabled). + /// \param[in] mode The chip configuration to be used whe in Idle mode. + /// \return true on success + bool setOpMode(uint8_t mode); + + /// Sets the Network address. + /// Only nodes with the same network address can communicate with each other. You + /// can set different network addresses in different sets of nodes to isolate them from each other. + /// Internally, this sets the nRF24 TX_ADDR and RX_ADDR_P0 to be the given network address. + /// The default network address is 0xE7E7E7E7E7 + /// \param[in] address The new network address. Must match the network address of any receiving node(s). + /// \param[in] len Number of bytes of address to set (3 to 5). + /// \return true on success, false if len is not in the range 3-5 inclusive. + bool setNetworkAddress(uint8_t* address, uint8_t len); + + /// Sets the data rate and transmitter power to use. Note that the nRF24 and the RFM73 have different + /// available power levels, and for convenience, 2 different sets of values are available in the + /// RH_NRF24::TransmitPower enum. The ones with the RFM73 only have meaning on the RFM73 and compatible + /// devces. The others are for the nRF24. + /// \param [in] data_rate The data rate to use for all packets transmitted and received. One of RH_NRF24::DataRate. + /// \param [in] power Transmitter power. One of RH_NRF24::TransmitPower. + /// \return true on success + bool setRF(DataRate data_rate, TransmitPower power); + + /// Sets the radio in power down mode, with the configuration set to the + /// last value from setOpMode(). + /// Sets chip enable to LOW. + void setModeIdle(); + + /// Sets the radio in RX mode. + /// Sets chip enable to HIGH to enable the chip in RX mode. + void setModeRx(); + + /// Sets the radio in TX mode. + /// Pulses the chip enable LOW then HIGH to enable the chip in TX mode. + void setModeTx(); + + /// Sends data to the address set by setTransmitAddress() + /// Sets the radio to TX mode + /// \param [in] data Data bytes to send. + /// \param [in] len Number of data bytes to send + /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was + /// successfully transmitted). + bool send(const uint8_t* data, uint8_t len); + + /// Blocks until the current message (if any) + /// has been transmitted + /// \return true on success, false if the chip is not in transmit mode or other transmit failure + virtual bool waitPacketSent(); + + /// Indicates if the chip is in transmit mode and + /// there is a packet currently being transmitted + /// \return true if the chip is in transmit mode and there is a transmission in progress + bool isSending(); + + /// Prints the value of all chip registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegisters(); + + /// Checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Sets the radio into Power Down mode. + /// If successful, the radio will stay in Power Down mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + +protected: + /// Flush the TX FIFOs + /// \return the value of the device status register + uint8_t flushTx(); + + /// Flush the RX FIFOs + /// \return the value of the device status register + uint8_t flushRx(); + + /// Examine the receive buffer to determine whether the message is for this node + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + +private: + /// This idle mode chip configuration + uint8_t _configuration; + + /// the number of the chip enable pin + uint8_t _chipEnablePin; + + /// Number of octets in the buffer + uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_NRF24_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + bool _rxBufValid; +}; + +/// @example nrf24_client.pde +/// @example nrf24_server.pde +/// @example nrf24_encrypted_client.pde +/// @example nrf24_encrypted_server.pde +/// @example nrf24_reliable_datagram_client.pde +/// @example nrf24_reliable_datagram_server.pde +/// @example RasPiRH.cpp + +#endif diff --git a/RH_NRF51.cpp b/RH_NRF51.cpp new file mode 100644 index 0000000..a537eec --- /dev/null +++ b/RH_NRF51.cpp @@ -0,0 +1,400 @@ +// NRF51.cpp +// +// Per: nRF51_Series_Reference_manual v3.0.pdf +// Copyright (C) 2012 Mike McCauley +// $Id: RH_NRF51.cpp,v 1.4 2017/02/01 21:46:02 mikem Exp $ + +// Set by Arduino IDE and RadioHead.h when compiling for nRF51 or nRF52 chips: + +#include + +#if RH_PLATFORM==RH_PLATFORM_NRF51 + + +RH_NRF51::RH_NRF51() + : _rxBufValid(false) +#if RH_NRF51_HAVE_ENCRYPTION + , _encrypting(false) +#endif +{ +} + +bool RH_NRF51::init() +{ + // Enable the High Frequency clock to the system as a whole + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + NRF_CLOCK->TASKS_HFCLKSTART = 1; + /* Wait for the external oscillator to start up */ + while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) + ; + + // Disable and reset the radio + NRF_RADIO->POWER = RADIO_POWER_POWER_Disabled; + NRF_RADIO->POWER = RADIO_POWER_POWER_Enabled; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + // Wait until we are in DISABLE state + while (NRF_RADIO->EVENTS_DISABLED == 0) {} + + // Physical on-air address is set in PREFIX0 + BASE0 by setNetworkAddress + NRF_RADIO->TXADDRESS = 0x00; // Use logical address 0 (PREFIX0 + BASE0) + NRF_RADIO->RXADDRESSES = 0x01; // Enable reception on logical address 0 (PREFIX0 + BASE0) + + // Configure the CRC + NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits + NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1 + + // These shorts will make the radio transition from Ready to Start to Disable automatically + // for both TX and RX, which makes for much shorter on-air times + NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos) + | (RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos); + + NRF_RADIO->PCNF0 = (8 << RADIO_PCNF0_LFLEN_Pos) // Payload size length in bits + | (1 << RADIO_PCNF0_S0LEN_Pos) // S0 is 1 octet + | (8 << RADIO_PCNF0_S1LEN_Pos); // S1 is 1 octet + + // Make sure we are powered down + setModeIdle(); + + // Set a default network address + uint8_t default_network_address[] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7}; + setNetworkAddress(default_network_address, sizeof(default_network_address)); + + setChannel(2); // The default, in case it was set by another app without powering down + setRF(RH_NRF51::DataRate2Mbps, RH_NRF51::TransmitPower0dBm); + setEncryptionKey(NULL); + return true; +} + +bool RH_NRF51::setChannel(uint8_t channel) +{ + NRF_RADIO->FREQUENCY = ((channel << RADIO_FREQUENCY_FREQUENCY_Pos) & RADIO_FREQUENCY_FREQUENCY_Msk); + return true; +} + +bool RH_NRF51::setNetworkAddress(uint8_t* address, uint8_t len) +{ + if (len < 3 || len > 5) + return false; + + // First byte is the prefix, remainder are base + NRF_RADIO->PREFIX0 = ((address[0] << RADIO_PREFIX0_AP0_Pos) & RADIO_PREFIX0_AP0_Msk); + uint32_t base; + memcpy(&base, address+1, len-1); + NRF_RADIO->BASE0 = base; + + NRF_RADIO->PCNF1 = ( + (((sizeof(_buf)) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk) // maximum length of payload + | (((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk) // expand the payload with 0 bytes + | (((len-1) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk)); // base address length in number of bytes. + + return true; +} + +bool RH_NRF51::setRF(DataRate data_rate, TransmitPower power) +{ + uint8_t mode; + uint8_t p; + + if (data_rate == DataRate2Mbps) + mode = RADIO_MODE_MODE_Nrf_2Mbit; + else if (data_rate == DataRate1Mbps) + mode = RADIO_MODE_MODE_Nrf_1Mbit; + else if (data_rate == DataRate250kbps) + mode = RADIO_MODE_MODE_Nrf_250Kbit; + else + return false;// Invalid + + if (power == TransmitPower4dBm) + p = RADIO_TXPOWER_TXPOWER_Pos4dBm; + else if (power == TransmitPower0dBm) + p = RADIO_TXPOWER_TXPOWER_0dBm; + else if (power == TransmitPowerm4dBm) + p = RADIO_TXPOWER_TXPOWER_Neg4dBm; + else if (power == TransmitPowerm8dBm) + p = RADIO_TXPOWER_TXPOWER_Neg8dBm; + else if (power == TransmitPowerm12dBm) + p = RADIO_TXPOWER_TXPOWER_Neg12dBm; + else if (power == TransmitPowerm16dBm) + p = RADIO_TXPOWER_TXPOWER_Neg16dBm; + else if (power == TransmitPowerm20dBm) + p = RADIO_TXPOWER_TXPOWER_Neg20dBm; + else if (power == TransmitPowerm30dBm) + p = RADIO_TXPOWER_TXPOWER_Neg30dBm; + else + return false; // Invalid + + + NRF_RADIO->TXPOWER = ((p << RADIO_TXPOWER_TXPOWER_Pos) & RADIO_TXPOWER_TXPOWER_Msk); + NRF_RADIO->MODE = ((mode << RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk); + + return true; +} + +void RH_NRF51::setModeIdle() +{ + if (_mode != RHModeIdle) + { + NRF_RADIO->EVENTS_DISABLED = 0U; + NRF_RADIO->TASKS_DISABLE = 1; + while (NRF_RADIO->EVENTS_DISABLED == 0U) + ; // wait for the radio to be disabled + NRF_RADIO->EVENTS_END = 0U; + _mode = RHModeIdle; + } +} + +void RH_NRF51::setModeRx() +{ + if (_mode != RHModeRx) + { + setModeIdle(); // Can only start RX from DISABLE state + +#if RH_NRF51_HAVE_ENCRYPTION + // Maybe set the AES CCA module for the correct encryption mode + if (_encrypting) + NRF_CCM->MODE = (CCM_MODE_MODE_Decryption << CCM_MODE_MODE_Pos); // Decrypt + NRF_CCM->MICSTATUS = 0; +#endif + + // Radio will transition automatically to Disable state when a message is received + NRF_RADIO->PACKETPTR = (uint32_t)_buf; + NRF_RADIO->EVENTS_READY = 0U; + NRF_RADIO->TASKS_RXEN = 1; + NRF_RADIO->EVENTS_END = 0U; // So we can detect end of reception + _mode = RHModeRx; + } +} + +void RH_NRF51::setModeTx() +{ + if (_mode != RHModeTx) + { + setModeIdle(); // Can only start RX from DISABLE state + + // Sigh: it seems that it takes longer to start the receiver than the transmitter for this type + // of radio, so if a message is received and an ACK or reply is sent to soon, the original transmitter + // may not see the reply. So we delay here to make sure the receiver is ready. + // Yes, I know this is very ugly + delay(1); + +#if RH_NRF51_HAVE_ENCRYPTION + // Maybe set the AES CCA module for the correct encryption mode + if (_encrypting) + NRF_CCM->MODE = (CCM_MODE_MODE_Encryption << CCM_MODE_MODE_Pos); // Encrypt +#endif + // Radio will transition automatically to Disable state at the end of transmission + NRF_RADIO->PACKETPTR = (uint32_t)_buf; + NRF_RADIO->EVENTS_READY = 0U; + NRF_RADIO->TASKS_TXEN = 1; + NRF_RADIO->EVENTS_END = 0U; // So we can detect end of transmission + _mode = RHModeTx; + } +} + +bool RH_NRF51::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_NRF51_MAX_MESSAGE_LEN) + return false; + +#if RH_NRF51_HAVE_ENCRYPTION + if (_encrypting && len > RH_NRF51_MAX_ENCRYPTED_MESSAGE_LEN) + return false; +#endif + + if (!waitCAD()) + return false; // Check channel activity + + // Set up the headers + _buf[0] = 0; // S0 + _buf[1] = len + RH_NRF51_HEADER_LEN; + _buf[2] = 0; // S1 + // The following octets are subject to encryption + _buf[3] = _txHeaderTo; + _buf[4] = _txHeaderFrom; + _buf[5] = _txHeaderId; + _buf[6] = _txHeaderFlags; + memcpy(_buf+RH_NRF51_HEADER_LEN, data, len); + _rxBufValid = false; + setModeTx(); + + // Radio will return to Disabled state after transmission is complete + _txGood++; + return true; +} + +bool RH_NRF51::waitPacketSent() +{ + // If we are not currently in transmit mode, there is no packet to wait for + if (_mode != RHModeTx) + return false; + + // When the Disabled event occurs we know the transmission has completed + while (!NRF_RADIO->EVENTS_END) + { + YIELD; + } + setModeIdle(); + + return true; +} + +bool RH_NRF51::isSending() +{ + return (NRF_RADIO->STATE == RADIO_STATE_STATE_Tx) ? true : false; +} + +bool RH_NRF51::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + uint16_t i; + uint32_t* p = (uint32_t*)NRF_RADIO; + for (i = 0; (p + i) < (uint32_t*) (((NRF_RADIO_Type*)NRF_RADIO) + 1); i++) + { + Serial.print("Offset: "); + Serial.print(i, DEC); + Serial.print(" "); + Serial.println(*(p+i), HEX); + } +#endif + + return true; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_NRF51::validateRxBuf() +{ + if (_buf[1] < RH_NRF51_HEADER_LEN) + return; // Too short to be a real message + // Extract the 4 headers following S0, LEN and S1 + _rxHeaderTo = _buf[3]; + _rxHeaderFrom = _buf[4]; + _rxHeaderId = _buf[5]; + _rxHeaderFlags = _buf[6]; + + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +void RH_NRF51::setEncryptionKey(uint8_t* key) +{ +#if RH_NRF51_HAVE_ENCRYPTION + if (key) + { + // Configure for on-the-fly encryption + // Set the key + memset(_encryption_cnf, 0, sizeof(_encryption_cnf)); + memcpy(_encryption_cnf, key, RH_NRF51_ENCRYPTION_KEY_LENGTH); + // AES configuration data area + // Note that the IV (Nonce) is not set, defaults to 0s + NRF_CCM->CNFPTR = (uint32_t)_encryption_cnf; + + // Set AES CCM input and putput buffers + // Make sure the _buf is encrypted and put back into _buf + NRF_CCM->INPTR = (uint32_t)_buf; + NRF_CCM->OUTPTR = (uint32_t)_buf; + // Also need to set SCRATCHPTR temp buffer os size 16+MAXPACKETSIZE in RAM + // FIXME: shared buffers if several radios + NRF_CCM->SCRATCHPTR = (uint32_t)_scratch; + + // SHORT from RADIO READY to AESCCM KSGEN using PPI predefined channel 24 + // Also RADIO ADDRESS to AESCCM CRYPT using PPI predefined channel 25 + NRF_PPI->CHENSET = (PPI_CHENSET_CH24_Enabled << PPI_CHENSET_CH24_Pos) + | (PPI_CHENSET_CH25_Enabled << PPI_CHENSET_CH25_Pos) + ; + + // SHORT from AESCCM ENDKSGEN to AESCCM CRYPT + NRF_CCM->SHORTS = (CCM_SHORTS_ENDKSGEN_CRYPT_Enabled << CCM_SHORTS_ENDKSGEN_CRYPT_Pos); + + // Enable the CCM module + NRF_CCM->ENABLE = (CCM_ENABLE_ENABLE_Enabled << CCM_ENABLE_ENABLE_Pos); + + _encrypting = true; + } + else + { + // Disable the CCM module + NRF_CCM->ENABLE = (CCM_ENABLE_ENABLE_Disabled << CCM_ENABLE_ENABLE_Pos); + _encrypting = false; + } +#endif +} + +bool RH_NRF51::available() +{ + if (!_rxBufValid) + { + if (_mode == RHModeTx) + return false; + setModeRx(); + if (!NRF_RADIO->EVENTS_END) + return false; // No message yet + setModeIdle(); +#if RH_NRF51_HAVE_ENCRYPTION + // If encryption is enabled, the decrypted message is not available yet, and there seems + // to be no way to be sure when its ready, but a delay of 2ms is enough + if (_encrypting) + delay(2); +#endif + if (!NRF_RADIO->CRCSTATUS) + { + // Bad CRC, restart the radio + _rxBad++; + setModeRx(); + return false; + } + validateRxBuf(); + if (!_rxBufValid) + setModeRx(); // Try for another + } + return _rxBufValid; +} + +void RH_NRF51::clearRxBuf() +{ + _rxBufValid = false; + _buf[1] = 0; +} + +bool RH_NRF51::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + // Skip the 4 headers that are at the beginning of the rxBuf + // the payload length is the first octet in _buf + if (*len > _buf[1]-RH_NRF51_HEADER_LEN) + *len = _buf[1]-RH_NRF51_HEADER_LEN; + memcpy(buf, _buf+RH_NRF51_HEADER_LEN, *len); + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +uint8_t RH_NRF51::maxMessageLength() +{ +#if RH_NRF51_HAVE_ENCRYPTION + if (_encrypting) + return RH_NRF51_MAX_ENCRYPTED_MESSAGE_LEN; +#endif + return RH_NRF51_MAX_MESSAGE_LEN; +} + +float RH_NRF51::get_temperature() +{ + NRF_TEMP->EVENTS_DATARDY = 0; + NRF_TEMP->TASKS_START = 1; + + while (!NRF_TEMP->EVENTS_DATARDY) + ; + return NRF_TEMP->TEMP * 0.25; +} + +#endif // NRF51 diff --git a/RH_NRF51.h b/RH_NRF51.h new file mode 100644 index 0000000..58360c2 --- /dev/null +++ b/RH_NRF51.h @@ -0,0 +1,302 @@ +// RH_NRF51.h +// Author: Mike McCauley +// Copyright (C) 2015 Mike McCauley +// $Id: RH_NRF51.h,v 1.5 2017/07/25 05:26:50 mikem Exp $ +// + +#ifndef RH_NRF51_h +#define RH_NRF51_h + +#include + +// This is the maximum number of bytes that can be carried by the nRF51. +// We use some for headers, keeping fewer for RadioHead messages +#define RH_NRF51_MAX_PAYLOAD_LEN 254 + +// The length of the headers we add. +// The headers are inside the nRF51 payload +// We add: +// S0 (not used) +// LEN +// S1 (not used) +// to +// from +// id +// flags +#define RH_NRF51_HEADER_LEN 7 + +// This is the maximum RadioHead user message length that can be supported by this library. Limited by +// the supported message lengths in the nRF51 +#define RH_NRF51_MAX_MESSAGE_LEN (RH_NRF51_MAX_PAYLOAD_LEN-RH_NRF51_HEADER_LEN) + +// Define to be 1 if you want to support AES CCA encryption using the built-in +// encryption engine. +#define RH_NRF51_HAVE_ENCRYPTION 1 + +// When encryption is enabled, have a much shorter max message length +#define RH_NRF51_MAX_ENCRYPTED_MESSAGE_LEN (27-4) + +// The required length of the AES encryption key +#define RH_NRF51_ENCRYPTION_KEY_LENGTH 16 + +// This is the size of the CCM data structure for AES encryption +// REVISIT: use a struct? +#define RH_NRF51_AES_CCM_CNF_SIZE 33 + +///////////////////////////////////////////////////////////////////// +/// \class RH_NRF51 RH_NRF51.h +/// \brief Send and receive unaddressed, unreliable datagrams by nRF51 and nRF52 compatible transceivers. +/// +/// Supported transceivers include: +/// - Nordic nRF51 based 2.4GHz radio modules, such as nRF51822 +/// and other compatible chips, such as used in RedBearLabs devices like: +/// http://store.redbearlab.com/products/redbearlab-nrf51822 +/// http://store.redbearlab.com/products/blenano +/// and +/// Sparkfun nRF52832 breakout board, with Arduino 1.6.13 and +/// Sparkfun nRF52 boards manager 0.2.3 +/// +/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams +/// of arbitrary length to 254 octets per packet. Use one of the Manager classes to get addressing and +/// acknowledgement reliability, routing, meshes etc. +/// +/// The nRF51822 (https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF51822) +/// and nRF52832 (https://learn.sparkfun.com/tutorials/nrf52832-breakout-board-hookup-guide) +/// is a complete SoC (system on a chip) with ARM microprocessor and 2.4 GHz radio, which supports a range of channels +/// and transmission bit rates. Chip antenna is on-board. +/// +/// This library provides functions for sending and receiving messages of up to 254 octets on any +/// frequency supported by the nRF51822/nRF52832, at a selected data rate. +/// +/// The nRF51 transceiver is configured to use Enhanced Shockburst with no acknowledgement and no retransmits. +/// TXADDRESS and RXADDRESSES:RXADDR0 (ie pipe 0) are the logical address used. The on-air network address +/// is set in BASE0 and PREFIX0. SHORTS is used to automatically transition the radio between Ready, Start and Disable. +/// No interrupts are used. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// data rate, and with identical network addresses. +/// +/// Example programs are included to show the main modes of use. +/// +/// \par Packet Format +/// +/// All messages sent and received by this class conform to this packet format. It is NOT compatible +/// with the one used by RH_NRF24 and the nRF24L01 product specification, mainly because the nRF24 only supports +/// 6 bits of message length. +/// +/// - 1 octets PREAMBLE +/// - 3 to 5 octets NETWORK ADDRESS +/// - 1 octet S0 (not used, required if encryption used) +/// - 8 bits PAYLOAD LENGTH +/// - 1 octet S1 (not used, required if encryption used) +/// - 0 to 251 octets PAYLOAD (possibly encrypted), consisting of: +/// - 1 octet TO header +/// - 1 octet FROM header +/// - 1 octet ID header +/// - 1 octet FLAGS header +/// - 0 to 247 octets of user message +/// - 2 octets CRC (Algorithm x^16+x^12^x^5+1 with initial value 0xFFFF). +/// +/// \par Example programs +/// +/// Several example programs are provided. +/// +/// The sample programs are designed to be built using Arduino 1.6.4 or later using the procedures outlined +/// in http://redbearlab.com/getting-started-nrf51822/ +/// or with Sparkfun nRF52832 breakout board, with Arduino 1.6.13 and +/// Sparkfun nRF52 boards manager 0.2.3 using the procedures outlined in +/// https://learn.sparkfun.com/tutorials/nrf52832-breakout-board-hookup-guide +/// +/// \par Radio Performance +/// +/// At DataRate2Mbps (2Mb/s), payload length vs airtime: +/// 0 bytes takes about 70us, 128 bytes takes 520us, 254 bytes take 1020us. +/// You can extrapolate linearly to slower data rates. +/// +/// The RF powers claimed by the chip manufacturer have not been independently verified here. +/// +/// \par Memory +/// +/// The compiled client and server sketches are about 42k bytes on Arduino. +/// The reliable client and server sketches compile to about 43k bytes on Arduino. Unfortunately the +/// Arduino build environmnet does not drop unused clsses and code, so the resulting programs include +/// all the unused classes ad code. This needs to be revisited. +/// RAM requirements are minimal. +/// +class RH_NRF51 : public RHGenericDriver +{ +public: + + /// \brief Defines convenient values for setting data rates in setRF() + typedef enum + { + DataRate1Mbps = 0, ///< 1 Mbps + DataRate2Mbps, ///< 2 Mbps + DataRate250kbps ///< 250 kbps + } DataRate; + + /// \brief Convenient values for setting transmitter power in setRF() + typedef enum + { + // Add 20dBm for nRF24L01p with PA and LNA modules + TransmitPower4dBm = 0, ///< 4 dBm + TransmitPower0dBm, ///< 0 dBm + TransmitPowerm4dBm, ///< -4 dBm + TransmitPowerm8dBm, ///< -8 dBm + TransmitPowerm12dBm, ///< -12 dBm + TransmitPowerm16dBm, ///< -16 dBm + TransmitPowerm20dBm, ///< -20 dBm + TransmitPowerm30dBm, ///< -30 dBm + } TransmitPower; + + /// Constructor. + /// After constructing, you must call init() to initialise the interface + /// and the radio module + RH_NRF51(); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken: + /// - Start the processors High Frequency clock DC/DC converter and + /// - Disable and reset the radio + /// - Set the logical channel to 0 for transmit and receive (only pipe 0 is used) + /// - Configure the CRC (2 octets, algorithm x^16+x^12^x^5+1 with initial value 0xffff) + /// - Set the default network address of 0xE7E7E7E7E7 + /// - Set channel to 2 + /// - Set data rate to DataRate2Mbps + /// - Set TX power to TransmitPower0dBm + /// \return true if everything was successful + bool init(); + + /// Sets the transmit and receive channel number. + /// The frequency used is (2400 + channel) MHz + /// \return true on success + bool setChannel(uint8_t channel); + + /// Sets the Network address. + /// Only nodes with the same network address can communicate with each other. You + /// can set different network addresses in different sets of nodes to isolate them from each other. + /// Internally, this sets the nRF51 BASE0 and PREFIX0 to be the given network address. + /// The first octet of the address is used for PREFIX0 and the rest is used for BASE0. BALEN is + /// set to the approprtae base length. + /// The default network address is 0xE7E7E7E7E7. + /// \param[in] address The new network address. Must match the network address of any receiving node(s). + /// \param[in] len Number of bytes of address to set (3 to 5). + /// \return true on success, false if len is not in the range 3-5 inclusive. + bool setNetworkAddress(uint8_t* address, uint8_t len); + + /// Sets the data rate and transmitter power to use. + /// \param [in] data_rate The data rate to use for all packets transmitted and received. One of RH_NRF51::DataRate. + /// \param [in] power Transmitter power. One of RH_NRF51::TransmitPower. + /// \return true on success + bool setRF(DataRate data_rate, TransmitPower power); + + /// Sets the radio in power down mode, with the configuration set to the + /// last value from setOpMode(). + /// Sets chip enable to LOW. + void setModeIdle(); + + /// Sets the radio in RX mode. + void setModeRx(); + + /// Sets the radio in TX mode. + void setModeTx(); + + /// Sends data to the address set by setTransmitAddress() + /// Sets the radio to TX mode. + /// Caution: when encryption is enabled, the maximum message length is reduced to 23 octets. + /// \param [in] data Data bytes to send. + /// \param [in] len Number of data bytes to send + /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was + /// successfully transmitted). False if the message is too long or was otherwise not transmitted. + bool send(const uint8_t* data, uint8_t len); + + /// Blocks until the current message (if any) + /// has been transmitted + /// \return true on success, false if the chip is not in transmit mode or other transmit failure + virtual bool waitPacketSent(); + + /// Indicates if the chip is in transmit mode and + /// there is a packet currently being transmitted + /// \return true if the chip is in transmit mode and there is a transmission in progress + bool isSending(); + + /// Prints the value of all NRF_RADIO registers. + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// Caution: there are 1024 of them (many reserved and set to 0). + /// \return true on success + bool printRegisters(); + + /// Checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// Once a message with CRC correct is received, the receiver will be returned to Idle mode. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// Enables AES encryption and sets the AES encryption key, used + /// to encrypt and decrypt all messages using the on-chip AES CCM mode encryption engine. + /// The default is disabled. + /// In the AES configuration, the message counter and IV is always set to 0, which + /// means the same keystream is used for every message with a given key. + /// Caution: when encryption is enabled, the maximum message length is reduced to 23 octets. + /// \param[in] key The key to use. Must be 16 bytes long. The same key must be installed + /// in other instances of RH_RF51, otherwise communications will not work correctly. If key is NULL, + /// encryption is disabled, which is the default. + void setEncryptionKey(uint8_t* key = NULL); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Reeads the current die temperature using the built in TEMP peripheral. + /// Blocks while the temperature is measured, which takes about 30 microseconds. + // \return the current die temperature in degrees C. + float get_temperature(); + +protected: + /// Examine the receive buffer to determine whether the message is for this node + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + +private: + /// The receiver/transmitter buffer + /// First octet is the payload length, remainder is the payload + uint8_t _buf[RH_NRF51_MAX_PAYLOAD_LEN+1]; + + /// True when there is a valid message in the buffer + bool _rxBufValid; + +#if RH_NRF51_HAVE_ENCRYPTION + /// True if an AES key has been specified and that we are therfore encrypting + /// and decrypting messages on the fly + bool _encrypting; + + /// Scratch area for AES encryption + uint8_t _scratch[RH_NRF51_MAX_PAYLOAD_LEN+1+16]; + + /// Where the AES encryption key and IV are stored + uint8_t _encryption_cnf[RH_NRF51_AES_CCM_CNF_SIZE]; +#endif +}; + +/// @example nrf51_client.pde +/// @example nrf51_server.pde +/// @example nrf51_reliable_datagram_client.pde +/// @example nrf51_reliable_datagram_server.pde +/// @example nrf51_audio_tx.pde +/// @example nrf51_audio_rx.pde +#endif diff --git a/RH_NRF905.cpp b/RH_NRF905.cpp new file mode 100644 index 0000000..3706e5c --- /dev/null +++ b/RH_NRF905.cpp @@ -0,0 +1,270 @@ +// RH_NRF905.cpp +// +// Copyright (C) 2012 Mike McCauley +// $Id: RH_NRF905.cpp,v 1.7 2017/01/12 23:58:00 mikem Exp $ + +#include + +RH_NRF905::RH_NRF905(uint8_t chipEnablePin, uint8_t txEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi) + : + RHNRFSPIDriver(slaveSelectPin, spi) +{ + _chipEnablePin = chipEnablePin; + _txEnablePin = txEnablePin; +} + +bool RH_NRF905::init() +{ +#if defined (__MK20DX128__) || defined (__MK20DX256__) + // Teensy is unreliable at 8MHz: + _spi.setFrequency(RHGenericSPI::Frequency1MHz); +#else + _spi.setFrequency(RHGenericSPI::Frequency8MHz); +#endif + if (!RHNRFSPIDriver::init()) + return false; + + // Initialise the slave select pin and the tx Enable pin + pinMode(_chipEnablePin, OUTPUT); + pinMode(_txEnablePin, OUTPUT); + digitalWrite(_chipEnablePin, LOW); + digitalWrite(_txEnablePin, LOW); + + // Configure the chip + // CRC 16 bits enabled. 16MHz crystal freq + spiWriteRegister(RH_NRF905_CONFIG_9, RH_NRF905_CONFIG_9_CRC_EN | RH_NRF905_CONFIG_9_CRC_MODE_16BIT | RH_NRF905_CONFIG_9_XOF_16MHZ); + + // Make sure we are powered down + setModeIdle(); + + // Some innocuous defaults + setChannel(108, LOW); // 433.2 MHz + setRF(RH_NRF905::TransmitPowerm10dBm); + + return true; +} + +// Use the register commands to read and write the registers +uint8_t RH_NRF905::spiReadRegister(uint8_t reg) +{ + return spiRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG); +} + +uint8_t RH_NRF905::spiWriteRegister(uint8_t reg, uint8_t val) +{ + return spiWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, val); +} + +uint8_t RH_NRF905::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len) +{ + return spiBurstRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG, dest, len); +} + +uint8_t RH_NRF905::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len) +{ + return spiBurstWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, src, len); +} + +uint8_t RH_NRF905::statusRead() +{ + // The status is a byproduct of sending a command + return spiCommand(0); +} + +bool RH_NRF905::setChannel(uint16_t channel, bool hiFrequency) +{ + spiWriteRegister(RH_NRF905_CONFIG_0, channel & RH_NRF905_CONFIG_0_CH_NO); + // Set or clear the high bit of the channel + uint8_t bit8 = (channel >> 8) & 0x01; + uint8_t reg1 = spiReadRegister(RH_NRF905_CONFIG_1); + reg1 = (reg1 & ~0x01) | bit8; + // Set or clear the HFREQ_PLL bit + reg1 &= ~RH_NRF905_CONFIG_1_HFREQ_PLL; + if (hiFrequency) + reg1 |= RH_NRF905_CONFIG_1_HFREQ_PLL; + spiWriteRegister(RH_NRF905_CONFIG_1, reg1); + return true; +} + +bool RH_NRF905::setNetworkAddress(uint8_t* address, uint8_t len) +{ + if (len < 1 || len > 4) + return false; + // Set RX_AFW and TX_AFW + spiWriteRegister(RH_NRF905_CONFIG_2, len | (len << 4)); + spiBurstWrite(RH_NRF905_REG_W_TX_ADDRESS, address, len); + spiBurstWriteRegister(RH_NRF905_CONFIG_5, address, len); + return true; +} + +bool RH_NRF905::setRF(TransmitPower power) +{ + // Enum definitions of power are the same numerical values as the register + uint8_t reg1 = spiReadRegister(RH_NRF905_CONFIG_1); + reg1 &= ~RH_NRF905_CONFIG_1_PA_PWR; + reg1 |= ((power & 0x3) << 2) & RH_NRF905_CONFIG_1_PA_PWR; + spiWriteRegister(RH_NRF905_CONFIG_1, reg1); + return true; +} + +void RH_NRF905::setModeIdle() +{ + if (_mode != RHModeIdle) + { + digitalWrite(_chipEnablePin, LOW); + digitalWrite(_txEnablePin, LOW); + _mode = RHModeIdle; + } +} + +void RH_NRF905::setModeRx() +{ + if (_mode != RHModeRx) + { + digitalWrite(_txEnablePin, LOW); + digitalWrite(_chipEnablePin, HIGH); + _mode = RHModeRx; + } +} + +void RH_NRF905::setModeTx() +{ + if (_mode != RHModeTx) + { + // Its the high transition that puts us into TX mode + digitalWrite(_txEnablePin, HIGH); + digitalWrite(_chipEnablePin, HIGH); + _mode = RHModeTx; + } +} + +bool RH_NRF905::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_NRF905_MAX_MESSAGE_LEN) + return false; + + if (!waitCAD()) + return false; // Check channel activity + + // Set up the headers + _buf[0] = _txHeaderTo; + _buf[1] = _txHeaderFrom; + _buf[2] = _txHeaderId; + _buf[3] = _txHeaderFlags; + _buf[4] = len; + memcpy(_buf+RH_NRF905_HEADER_LEN, data, len); + spiBurstWrite(RH_NRF905_REG_W_TX_PAYLOAD, _buf, len + RH_NRF905_HEADER_LEN); + setModeTx(); + // Radio will return to Standby mode after transmission is complete + _txGood++; + return true; +} + +bool RH_NRF905::waitPacketSent() +{ + if (_mode != RHModeTx) + return false; + + while (!(statusRead() & RH_NRF905_STATUS_DR)) + YIELD; + setModeIdle(); + return true; +} + +bool RH_NRF905::isSending() +{ + if (_mode != RHModeTx) + return false; + + return !(statusRead() & RH_NRF905_STATUS_DR); +} + +bool RH_NRF905::printRegister(uint8_t reg) +{ +#ifdef RH_HAVE_SERIAL + Serial.print(reg, HEX); + Serial.print(": "); + Serial.println(spiReadRegister(reg), HEX); +#endif + + return true; +} + +bool RH_NRF905::printRegisters() +{ + uint8_t registers[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}; + + uint8_t i; + for (i = 0; i < sizeof(registers); i++) + printRegister(registers[i]); + return true; +} + +// Check whether the latest received message is complete and uncorrupted +void RH_NRF905::validateRxBuf() +{ + // Check the length + uint8_t len = _buf[4]; + if (len > RH_NRF905_MAX_MESSAGE_LEN) + return; // Silly LEN header + + // Extract the 4 headers + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _bufLen = len + RH_NRF905_HEADER_LEN; // _buf still includes the headers + _rxBufValid = true; + } +} + +bool RH_NRF905::available() +{ + if (!_rxBufValid) + { + if (_mode == RHModeTx) + return false; + setModeRx(); + if (!(statusRead() & RH_NRF905_STATUS_DR)) + return false; + // Get the message into the RX buffer, so we can inspect the headers + // we still dont know how long is the user message + spiBurstRead(RH_NRF905_REG_R_RX_PAYLOAD, _buf, RH_NRF905_MAX_PAYLOAD_LEN); + validateRxBuf(); + if (_rxBufValid) + setModeIdle(); // Got one + + } + return _rxBufValid; +} + +void RH_NRF905::clearRxBuf() +{ + _rxBufValid = false; + _bufLen = 0; +} + +bool RH_NRF905::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen-RH_NRF905_HEADER_LEN) + *len = _bufLen-RH_NRF905_HEADER_LEN; + memcpy(buf, _buf+RH_NRF905_HEADER_LEN, *len); + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +uint8_t RH_NRF905::maxMessageLength() +{ + return RH_NRF905_MAX_MESSAGE_LEN; +} diff --git a/RH_NRF905.h b/RH_NRF905.h new file mode 100644 index 0000000..e9e5da9 --- /dev/null +++ b/RH_NRF905.h @@ -0,0 +1,424 @@ +// RH_NRF905.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_NRF905.h,v 1.11 2017/07/25 05:26:50 mikem Exp $ +// + +#ifndef RH_NRF905_h +#define RH_NRF905_h + +#include +#include + +// This is the maximum (and only) number of bytes that can be carried by the nRF905. +// We use some for headers, leaving fewer for RadioHead messages +#define RH_NRF905_MAX_PAYLOAD_LEN 32 + +// The length of the headers we add. +// The headers are inside the nRF905 payload +// As well as the usual TO, FROM, ID, FLAGS, we also need LEN, since +// nRF905 only has fixed width messages. +// REVISIT: could we have put the LEN into the FLAGS field? +#define RH_NRF905_HEADER_LEN 5 + +// This is the maximum RadioHead user message length that can be supported by this library. Limited by +// the supported message lengths in the nRF905 +#define RH_NRF905_MAX_MESSAGE_LEN (RH_NRF905_MAX_PAYLOAD_LEN-RH_NRF905_HEADER_LEN) + +// Register names +#define RH_NRF905_REG_MASK 0x0f +#define RH_NRF905_REG_W_CONFIG 0x00 +#define RH_NRF905_REG_R_CONFIG 0x10 +#define RH_NRF905_REG_W_TX_PAYLOAD 0x20 +#define RH_NRF905_REG_R_TX_PAYLOAD 0x21 +#define RH_NRF905_REG_W_TX_ADDRESS 0x22 +#define RH_NRF905_REG_R_TX_ADDRESS 0x23 +#define RH_NRF905_REG_R_RX_PAYLOAD 0x24 +#define RH_NRF905_REG_CHANNEL_CONFIG 0x80 + +// Configuration register +#define RH_NRF905_CONFIG_0 0x00 +#define RH_NRF905_CONFIG_0_CH_NO 0xff + +#define RH_NRF905_CONFIG_1 0x01 +#define RH_NRF905_CONFIG_1_AUTO_RETRAN 0x20 +#define RH_NRF905_CONFIG_1_RX_RED_PWR 0x10 +#define RH_NRF905_CONFIG_1_PA_PWR 0x0c +#define RH_NRF905_CONFIG_1_PA_PWR_N10DBM 0x00 +#define RH_NRF905_CONFIG_1_PA_PWR_N2DBM 0x04 +#define RH_NRF905_CONFIG_1_PA_PWR_6DBM 0x08 +#define RH_NRF905_CONFIG_1_PA_PWR_10DBM 0x0c +#define RH_NRF905_CONFIG_1_HFREQ_PLL 0x02 +#define RH_NRF905_CONFIG_1_CH_NO 0x01 + +#define RH_NRF905_CONFIG_2 0x02 +#define RH_NRF905_CONFIG_2_TX_AFW 0x70 +#define RH_NRF905_CONFIG_2_RX_AFW 0x07 + +#define RH_NRF905_CONFIG_3 0x03 +#define RH_NRF905_CONFIG_3_RX_PW 0x3f + +#define RH_NRF905_CONFIG_4 0x04 +#define RH_NRF905_CONFIG_4_TX_PW 0x3f + +#define RH_NRF905_CONFIG_5 0x05 +#define RH_NRF905_CONFIG_5_RX_ADDRESS 0xff + +#define RH_NRF905_CONFIG_6 0x06 +#define RH_NRF905_CONFIG_6_RX_ADDRESS 0xff + +#define RH_NRF905_CONFIG_7 0x07 +#define RH_NRF905_CONFIG_7_RX_ADDRESS 0xff + +#define RH_NRF905_CONFIG_8 0x08 +#define RH_NRF905_CONFIG_8_RX_ADDRESS 0xff + +#define RH_NRF905_CONFIG_9 0x09 +#define RH_NRF905_CONFIG_9_CRC_MODE_16BIT 0x80 +#define RH_NRF905_CONFIG_9_CRC_EN 0x40 +#define RH_NRF905_CONFIG_9_XOF 0x38 +#define RH_NRF905_CONFIG_9_XOF_4MHZ 0x00 +#define RH_NRF905_CONFIG_9_XOF_8MHZ 0x08 +#define RH_NRF905_CONFIG_9_XOF_12MHZ 0x10 +#define RH_NRF905_CONFIG_9_XOF_16MHZ 0x18 +#define RH_NRF905_CONFIG_9_XOF_20MHZ 0x20 +#define RH_NRF905_CONFIG_9_UP_CLK_EN 0x04 +#define RH_NRF905_CONFIG_9_UP_CLK_FREQ 0x03 +#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_4MHZ 0x00 +#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_2MHZ 0x01 +#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_1MHZ 0x02 +#define RH_NRF905_CONFIG_9_UP_CLK_FREQ_500KHZ 0x03 + +// Status register is always read as first byte +#define RH_NRF905_STATUS_AM 0x80 +#define RH_NRF905_STATUS_DR 0x20 + +///////////////////////////////////////////////////////////////////// +/// \class RH_NRF905 RH_NRF905.h +/// \brief Send and receive unaddressed, unreliable datagrams by nRF905 and compatible transceivers. +/// +/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams +/// of arbitrary length to 28 octets per packet. Use one of the Manager classes to get addressing and +/// acknowledgement reliability, routing, meshes etc. +/// +/// The nRF905 transceiver is configured to use Enhanced Shockburst with 16 Bit CRC, and 32 octet packets. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency +/// and with identical network addresses. +/// +/// The nRF905 from Nordic Semiconductor http://www.nordicsemi.com/eng/Products/Sub-1-GHz-RF/nRF905 +/// (http://www.nordicsemi.com/jpn/nordic/content_download/2452/29528/file/Product_Specification_nRF905_v1.5.pdf) +/// is a low-cost 433/868/915 MHz ISM transceiver module. It supports a number of channel frequencies at +/// 100kHz deviation and 50kHz bandwidth with Manchester encoding. +/// +/// We tested with inexpensive nRF905 modules from eBay, similar to: +/// http://www.aliexpress.com/store/product/Free-ship-NRF905-433MHz-Wireless-Transmission-Module-Transceiver-Module-with-Antenna-for-the-433MHz-ISM-band/513046_607163305.html +/// +/// This library provides functions for sending and receiving messages of up to 27 octets on any +/// frequency supported by the nRF905. +/// +/// Several nRF905 modules can be connected to an Arduino, permitting the construction of translators +/// and frequency changers, etc. +/// +/// Example Arduino programs are included to show the main modes of use. +/// +/// \par Packet Format +/// +/// All messages sent and received by this class conform to this fixed length packet format +/// +/// - 4 octets NETWORK ADDRESS +/// - 32 octets PAYLOAD, consisting of: +/// - 1 octet TO header +/// - 1 octet FROM header +/// - 1 octet ID header +/// - 1 octet FLAGS header +/// - 1 octet user message length header +/// - 0 to 27 octets of user message, trailing octets after the user message length are ignored +/// - 2 octets CRC +/// +/// All messages sent and received by this driver are 32 octets. The user message length is embedded in the message. +/// +/// \par Connecting nRF905 +/// +/// The nRF905 is a 3.3V part is is *NOT* 5V tolerant. So you MUST use a 3.3V CPU such as Teensy, Arduino Due etc +/// or else provide for level shifters between the CPU and the nRF905. Failure to consider this will probably +/// break your nRF905. +/// +/// The electrical connection between the nRF905 and the CPU require 3.3V, the 3 x SPI pins (SCK, SDI, SDO), +/// a Chip Enable pin, a Transmit Enable pin and a Slave Select pin. +/// +/// The examples below assume the commonly found cheap Chinese nRF905 modules. The RH_RF905 driver assumes the +/// the nRF905 has a 16MHz crystal. +/// +/// Connect the nRF905 to Teensy (or Arduino with suitable level shifters) like this +/// \code +/// CPU nRF905 module +/// 3V3----------VCC (3.3V) +/// pin D8-----------CE (chip enable in) +/// pin D9-----------TX_EN (transmit enable in) +/// SS pin D10----------CSN (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------MOSI (SPI Data in) +/// MISO pin D12----------MISO (SPI data out) +/// GND----------GND (ground in) +/// \endcode +/// +/// Caution: Arduino Due is a 3.3V part and is not 5V tolerant (so too is the nRF905 module +/// so they can be connected directly together. Unlike other Arduinos the Due has it default SPI +/// connections on a dedicated 6 pin SPI header in the center of the board, which is +/// physically compatible with Uno, Leonardo and Mega2560. A little dot marks pin 1 on the header. +/// You must connect to these +/// and *not* to the usual Arduino SPI pins Digital 11, 12 and 13. +/// See http://21stdigitalhome.blogspot.com.au/2013/02/arduino-due-hardware-spi.html +/// +/// Connect the nRF905 to Arduino Due like this +/// \code +/// CPU nRF905 module +/// 3V3----------VCC (3.3V) +/// pin D8-----------CE (chip enable in) +/// pin D9-----------TX_EN (transmit enable in) +/// SS pin D10----------CSN (chip select in) +/// SCK on SPI header pin 3----------SCK (SPI clock in) +/// MOSI on SPI header pin 4----------MOSI (SPI Data in) +/// MISO on SPI header pin 1----------MISO (SPI data out) +/// GND----------GND (ground in) +/// \endcode +/// +/// and you can then use the default constructor RH_NRF905(). +/// You can override the default settings for the CE, TX_EN and CSN pins +/// in the NRF905() constructor if you wish to connect the slave select CSN to other than the normal one for your +/// CPU. +/// +/// It is possible to have 2 radios conected to one CPU, provided each radio has its own +/// CSN, TX_EN and CE line (SCK, MOSI and MISO are common to both radios) +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power to be one of 4 power levels: -10, -2, 6 or 10dBm, +/// using the setRF() function, eg: +/// \code +/// nrf905.setRF(RH_NRF905::TransmitPower10dBm); +/// \endcode +/// +/// We have made some actual power measurements against +/// programmed power for an nRF905 module from www.rfinchina.com under the following conditions: +/// - Teensy 3.1 +/// - nRF905 module (with SMA antenna connector) wired to Teensy as described above, channel 108. +/// - 20cm SMA-SMA cable +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// -10 -16 +/// -2 -8 +/// 6 0 +/// 10 8 +/// \endcode +/// (Caution: we dont claim laboratory accuracy for these measurements) +/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna. +/// +/// \par Example programs +/// +/// Several example programs are provided. They work out of the box with Teensy 3.1 and Arduino Due +/// connected as show above. +/// +/// \par Radio Performance +/// +/// Frequency accuracy may be debatable. +/// +/// \par Memory +/// +/// Memory usage of this class is minimal. The compiled client and server sketches are about 16000 bytes on Teensy. +/// +class RH_NRF905 : public RHNRFSPIDriver +{ +public: + /// \brief Convenient values for setting transmitter power in setRF() + /// These are designed to agree with the values for RH_NRF905_CONFIG_1_PA_PWR after + /// left shifting by 2 + /// To be passed to setRF(); + typedef enum + { + TransmitPowerm10dBm = 0, ///< -10 dBm + TransmitPowerm2dBm, ///< -2 dBm + TransmitPower6dBm, ///< 6 dBm + TransmitPower10dBm ///< 10 dBm + } TransmitPower; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// chip enable and slave select pin. + /// After constructing, you must call init() to initialise the interface + /// and the radio module + /// \param[in] chipEnablePin the Arduino pin to use to enable the chip for transmit/receive + /// \param[in] txEnablePin the Arduino pin cponnected to the txEn pin on the radio that enable transmit mode + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the NRF905 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, + /// D10 for Maple, Teensy) + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_NRF905(uint8_t chipEnablePin = 8, uint8_t txEnablePin = 9, uint8_t slaveSelectPin = SS, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken:g + /// - Set the chip enable and chip select pins to output LOW, HIGH respectively. + /// - Initialise the SPI output pins + /// - Initialise the SPI interface library to 8MHz (Hint, if you want to lower + /// the SPI frequency (perhaps where you have other SPI shields, low voltages etc), + /// call SPI.setClockDivider() after init()). + /// -Flush the receiver and transmitter buffers + /// - Set the radio to receive with powerUpRx(); + /// \return true if everything was successful + bool init(); + + /// Reads a single register from the NRF905 + /// \param[in] reg Register number, one of NR905_REG_* + /// \return The value of the register + uint8_t spiReadRegister(uint8_t reg); + + /// Writes a single byte to the NRF905, and at the ame time reads the current STATUS register + /// \param[in] reg Register number, one of NRF905_REG_* + /// \param[in] val The value to write + /// \return the current STATUS (read while the command is sent) + uint8_t spiWriteRegister(uint8_t reg, uint8_t val); + + /// Reads a number of consecutive registers from the NRF905 using burst read mode + /// \param[in] reg Register number of the first register, one of NRF905_REG_* + /// \param[in] dest Array to write the register values to. Must be at least len bytes + /// \param[in] len Number of bytes to read + /// \return the current STATUS (read while the command is sent) + uint8_t spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len); + + /// Write a number of consecutive registers using burst write mode + /// \param[in] reg Register number of the first register, one of NRF905_REG_* + /// \param[in] src Array of new register values to write. Must be at least len bytes + /// \param[in] len Number of bytes to write + /// \return the current STATUS (read while the command is sent) + uint8_t spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len); + + /// Reads and returns the device status register NRF905_REG_02_DEVICE_STATUS + /// \return The value of the device status register + uint8_t statusRead(); + + /// Sets the transmit and receive channel number. + /// The RF frequency used is (422.4 + channel/10) * (1+hiFrequency) MHz + /// \param[in] channel The channel number. + /// \param[in] hiFrequency false for low frequency band (422.4MHz and up), true for high frequency band (845MHz and up) + /// \return true on success + bool setChannel(uint16_t channel, bool hiFrequency = false); + + /// Sets the Network address. + /// Only nodes with the same network address can communicate with each other. You + /// can set different network addresses in different sets of nodes to isolate them from each other. + /// The default network address is 0xE7E7E7E7 + /// \param[in] address The new network address. Must match the network address of any receiving node(s). + /// \param[in] len Number of bytes of address to set (1 to 4). + /// \return true on success, false if len is not in the range 1-4 inclusive. + bool setNetworkAddress(uint8_t* address, uint8_t len); + + /// Sets the transmitter power to use + /// \param [in] power Transmitter power. One of NRF905::TransmitPower. + /// \return true on success + bool setRF(TransmitPower power); + + /// Sets the radio in power down mode. + /// Sets chip enable to LOW. + /// \return true on success + void setModeIdle(); + + /// Sets the radio in RX mode. + /// Sets chip enable to HIGH to enable the chip in RX mode. + /// \return true on success + void setModeRx(); + + /// Sets the radio in TX mode. + /// Pulses the chip enable LOW then HIGH to enable the chip in TX mode. + /// \return true on success + void setModeTx(); + + /// Sends data to the address set by setTransmitAddress() + /// Sets the radio to TX mode + /// \param [in] data Data bytes to send. + /// \param [in] len Number of data bytes to set in the TX buffer. The actual size of the + /// transmitted data payload is set by setPayloadSize. Maximum message length actually + /// transmitted is RH_NRF905_MAX_MESSAGE_LEN = 27. + /// \return true on success (which does not necessarily mean the receiver got the message, only that the message was + /// successfully transmitted). Returns false if the requested message length exceeds RH_NRF905_MAX_MESSAGE_LEN. + bool send(const uint8_t* data, uint8_t len); + + /// Blocks until the current message (if any) + /// has been transmitted + /// \return true on success, false if the chip is not in transmit mode + virtual bool waitPacketSent(); + + /// Indicates if the chip is in transmit mode and + /// there is a packet currently being transmitted + /// \return true if the chip is in transmit mode and there is a transmission in progress + bool isSending(); + + /// Prints the value of a single chip register + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegister(uint8_t reg); + + /// Prints the value of all chip registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegisters(); + + /// Checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + +protected: + /// Examine the revceive buffer to determine whether the message is for this node + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + +private: + /// This idle mode chip configuration + uint8_t _configuration; + + /// the number of the chip enable pin + uint8_t _chipEnablePin; + + /// The number of the transmit enable pin + uint8_t _txEnablePin; + + /// Number of octets in the buffer + uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_NRF905_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + bool _rxBufValid; +}; + +/// @example nrf905_client.pde +/// @example nrf905_server.pde +/// @example nrf905_reliable_datagram_client.pde +/// @example nrf905_reliable_datagram_server.pde + +#endif diff --git a/RH_RF22.cpp b/RH_RF22.cpp new file mode 100644 index 0000000..0647e6e --- /dev/null +++ b/RH_RF22.cpp @@ -0,0 +1,814 @@ +// RH_RF22.cpp +// +// Copyright (C) 2011 Mike McCauley +// $Id: RH_RF22.cpp,v 1.33 2020/07/05 08:52:21 mikem Exp mikem $ + +#include + +#if RH_PLATFORM == RH_PLATFORM_ESP8266 +// This voltatile array is used in the ESP8266 platform to manage the interrupt +// service routines in a main loop, avoiding SPI calls inside the isr functions. +volatile bool flagIsr[3] = {false, false, false}; +#endif + +// Interrupt vectors for the 2 Arduino interrupt pins +// Each interrupt can be handled by a different instance of RH_RF22, allowing you to have +// 2 RH_RF22s per Arduino +RH_RF22* RH_RF22::_deviceForInterrupt[RH_RF22_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_RF22::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// These are indexed by the values of ModemConfigChoice +// Canned modem configurations generated with +// http://www.hoperf.com/upload/rf/RH_RF22B%2023B%2031B%2042B%2043B%20Register%20Settings_RevB1-v5.xls +// Stored in flash (program) memory to save SRAM +PROGMEM static const RH_RF22::ModemConfig MODEM_CONFIG_TABLE[] = +{ + { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x00, 0x08 }, // Unmodulated carrier + { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x33, 0x08 }, // FSK, PN9 random modulation, 2, 5 + + // All the following enable FIFO with reg 71 + // 1c, 1f, 20, 21, 22, 23, 24, 25, 2c, 2d, 2e, 58, 69, 6e, 6f, 70, 71, 72 + // FSK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm + { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x22, 0x08 }, // 2, 5 + { 0x1b, 0x03, 0x41, 0x60, 0x27, 0x52, 0x00, 0x07, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x22, 0x3a }, // 2.4, 36 + { 0x1d, 0x03, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x13, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x22, 0x48 }, // 4.8, 45 + { 0x1e, 0x03, 0xd0, 0x00, 0x9d, 0x49, 0x00, 0x45, 0x40, 0x0a, 0x20, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x22, 0x48 }, // 9.6, 45 + { 0x2b, 0x03, 0x34, 0x02, 0x75, 0x25, 0x07, 0xff, 0x40, 0x0a, 0x1b, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x22, 0x0f }, // 19.2, 9.6 + { 0x02, 0x03, 0x68, 0x01, 0x3a, 0x93, 0x04, 0xd5, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x22, 0x1f }, // 38.4, 19.6 + { 0x06, 0x03, 0x45, 0x01, 0xd7, 0xdc, 0x07, 0x6e, 0x40, 0x0a, 0x2d, 0x80, 0x60, 0x0e, 0xbf, 0x0c, 0x22, 0x2e }, // 57.6. 28.8 + { 0x8a, 0x03, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x40, 0x0a, 0x50, 0x80, 0x60, 0x20, 0x00, 0x0c, 0x22, 0xc8 }, // 125, 125 + + { 0x2b, 0x03, 0xa1, 0xe0, 0x10, 0xc7, 0x00, 0x09, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x04, 0x32, 0x2c, 0x22, 0x04 }, // 512 baud, FSK, 2.5 Khz fd for POCSAG compatibility + { 0x27, 0x03, 0xa1, 0xe0, 0x10, 0xc7, 0x00, 0x06, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x04, 0x32, 0x2c, 0x22, 0x07 }, // 512 baud, FSK, 4.5 Khz fd for POCSAG compatibility + + // GFSK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm + // These differ from FSK only in register 71, for the modulation type + { 0x2b, 0x03, 0xf4, 0x20, 0x41, 0x89, 0x00, 0x36, 0x40, 0x0a, 0x1d, 0x80, 0x60, 0x10, 0x62, 0x2c, 0x23, 0x08 }, // 2, 5 + { 0x1b, 0x03, 0x41, 0x60, 0x27, 0x52, 0x00, 0x07, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x23, 0x3a }, // 2.4, 36 + { 0x1d, 0x03, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x13, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x23, 0x48 }, // 4.8, 45 + { 0x1e, 0x03, 0xd0, 0x00, 0x9d, 0x49, 0x00, 0x45, 0x40, 0x0a, 0x20, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x23, 0x48 }, // 9.6, 45 + { 0x2b, 0x03, 0x34, 0x02, 0x75, 0x25, 0x07, 0xff, 0x40, 0x0a, 0x1b, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x23, 0x0f }, // 19.2, 9.6 + { 0x02, 0x03, 0x68, 0x01, 0x3a, 0x93, 0x04, 0xd5, 0x40, 0x0a, 0x1e, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x23, 0x1f }, // 38.4, 19.6 + { 0x06, 0x03, 0x45, 0x01, 0xd7, 0xdc, 0x07, 0x6e, 0x40, 0x0a, 0x2d, 0x80, 0x60, 0x0e, 0xbf, 0x0c, 0x23, 0x2e }, // 57.6. 28.8 + { 0x8a, 0x03, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x40, 0x0a, 0x50, 0x80, 0x60, 0x20, 0x00, 0x0c, 0x23, 0xc8 }, // 125, 125 + + // OOK, No Manchester, Max Rb err <1%, Xtal Tol 20ppm + { 0x51, 0x03, 0x68, 0x00, 0x3a, 0x93, 0x01, 0x3d, 0x2c, 0x11, 0x28, 0x80, 0x60, 0x09, 0xd5, 0x2c, 0x21, 0x08 }, // 1.2, 75 + { 0xc8, 0x03, 0x39, 0x20, 0x68, 0xdc, 0x00, 0x6b, 0x2a, 0x08, 0x2a, 0x80, 0x60, 0x13, 0xa9, 0x2c, 0x21, 0x08 }, // 2.4, 335 + { 0xc8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x29, 0x04, 0x29, 0x80, 0x60, 0x27, 0x52, 0x2c, 0x21, 0x08 }, // 4.8, 335 + { 0xb8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x82, 0x29, 0x80, 0x60, 0x4e, 0xa5, 0x2c, 0x21, 0x08 }, // 9.6, 335 + { 0xa8, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x41, 0x29, 0x80, 0x60, 0x9d, 0x49, 0x2c, 0x21, 0x08 }, // 19.2, 335 + { 0x98, 0x03, 0x9c, 0x00, 0xd1, 0xb7, 0x00, 0xd4, 0x28, 0x20, 0x29, 0x80, 0x60, 0x09, 0xd5, 0x0c, 0x21, 0x08 }, // 38.4, 335 + { 0x98, 0x03, 0x96, 0x00, 0xda, 0x74, 0x00, 0xdc, 0x28, 0x1f, 0x29, 0x80, 0x60, 0x0a, 0x3d, 0x0c, 0x21, 0x08 }, // 40, 335 +}; + +RH_RF22::RH_RF22(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi) + : + RHSPIDriver(slaveSelectPin, spi) +{ + _interruptPin = interruptPin; + _idleMode = RH_RF22_XTON; // Default idle state is READY mode + _polynomial = CRC_16_IBM; // Historical + _myInterruptIndex = 0xff; // Not allocated yet +} + +void RH_RF22::setIdleMode(uint8_t idleMode) +{ + _idleMode = idleMode; +} + +bool RH_RF22::init() +{ +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + flagIsr[0] = false; + flagIsr[1] = false; + flagIsr[2] = false; +#endif + + if (!RHSPIDriver::init()) + return false; + + // 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 + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + + // Software reset the device + reset(); + + // Get the device type and check it + // This also tests whether we are really connected to a device + _deviceType = spiRead(RH_RF22_REG_00_DEVICE_TYPE); + if ( _deviceType != RH_RF22_DEVICE_TYPE_RX_TRX + && _deviceType != RH_RF22_DEVICE_TYPE_TX) + { +// Serial.print("unknown device type: "); +// Serial.println(_deviceType); + return false; + } + + // Issue software reset to get all registers to default state + spiWrite(RH_RF22_REG_07_OPERATING_MODE1, RH_RF22_SWRES); + // Wait for chip ready + while (!(spiRead(RH_RF22_REG_04_INTERRUPT_STATUS2) & RH_RF22_ICHIPRDY)) + ; + + // Add by Adrien van den Bossche 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); + + // Enable interrupt output on the radio. Interrupt line will now go high until + // an interrupt occurs + spiWrite(RH_RF22_REG_05_INTERRUPT_ENABLE1, RH_RF22_ENTXFFAEM | RH_RF22_ENRXFFAFULL | RH_RF22_ENPKSENT | RH_RF22_ENPKVALID | RH_RF22_ENCRCERROR | RH_RF22_ENFFERR); + spiWrite(RH_RF22_REG_06_INTERRUPT_ENABLE2, RH_RF22_ENPREAVAL); + + // 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 actually the + // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping + // yourself based on knowledge of what Arduino board you are running on. + if (_myInterruptIndex == 0xff) + { + // First run, no interrupt allocated yet + if (_interruptCount <= RH_RF22_NUM_INTERRUPTS) + _myInterruptIndex = _interruptCount++; + else + return false; // Too many devices, not enough interrupt vectors + } + _deviceForInterrupt[_myInterruptIndex] = this; + if (_myInterruptIndex == 0) + attachInterrupt(interruptNumber, isr0, FALLING); + else if (_myInterruptIndex == 1) + attachInterrupt(interruptNumber, isr1, FALLING); + else if (_myInterruptIndex == 2) + attachInterrupt(interruptNumber, isr2, FALLING); + else + return false; // Too many devices, not enough interrupt vectors + + setModeIdle(); + + clearTxBuf(); + clearRxBuf(); + + // Most of these are the POR default + spiWrite(RH_RF22_REG_7D_TX_FIFO_CONTROL2, RH_RF22_TXFFAEM_THRESHOLD); + spiWrite(RH_RF22_REG_7E_RX_FIFO_CONTROL, RH_RF22_RXFFAFULL_THRESHOLD); + spiWrite(RH_RF22_REG_30_DATA_ACCESS_CONTROL, RH_RF22_ENPACRX | RH_RF22_ENPACTX | RH_RF22_ENCRC | (_polynomial & RH_RF22_CRC)); + + // Configure the message headers + // Here we set up the standard packet format for use by the RH_RF22 library + // 8 nibbles preamble + // 2 SYNC words 2d, d4 + // Header length 4 (to, from, id, flags) + // 1 octet of data length (0 to 255) + // 0 to 255 octets data + // 2 CRC octets as CRC16(IBM), computed on the header, length and data + // On reception the to address is check for validity against RH_RF22_REG_3F_CHECK_HEADER3 + // or the broadcast address of 0xff + // If no changes are made after this, the transmitted + // to address will be 0xff, the from address will be 0xff + // and all such messages will be accepted. This permits the out-of the box + // RH_RF22 config to act as an unaddresed, unreliable datagram service + spiWrite(RH_RF22_REG_32_HEADER_CONTROL1, RH_RF22_BCEN_HEADER3 | RH_RF22_HDCH_HEADER3); + spiWrite(RH_RF22_REG_33_HEADER_CONTROL2, RH_RF22_HDLEN_4 | RH_RF22_SYNCLEN_2); + + setPreambleLength(8); + uint8_t syncwords[] = { 0x2d, 0xd4 }; + setSyncWords(syncwords, sizeof(syncwords)); + setPromiscuous(false); + + // Set some defaults. An innocuous ISM frequency, and reasonable pull-in + setFrequency(434.0, 0.05); +// setFrequency(900.0); + // Some slow, reliable default speed and modulation + setModemConfig(FSK_Rb2_4Fd36); +// setModemConfig(FSK_Rb125Fd125); + setGpioReversed(false); + // Lowish power + setTxPower(RH_RF22_TXPOW_8DBM); + + return true; +} + +// C++ level interrupt handler for this instance +void RH_RF22::handleInterrupt() +{ + uint8_t _lastInterruptFlags[2]; + // Read the interrupt flags which clears the interrupt + spiBurstRead(RH_RF22_REG_03_INTERRUPT_STATUS1, _lastInterruptFlags, 2); + +#if 0 + // DEVELOPER TESTING ONLY + // Caution: Serial printing in this interrupt routine can cause mysterious crashes + Serial.print("interrupt "); + Serial.print(_lastInterruptFlags[0], HEX); + Serial.print(" "); + Serial.println(_lastInterruptFlags[1], HEX); + if (_lastInterruptFlags[0] == 0 && _lastInterruptFlags[1] == 0) + Serial.println("FUNNY: no interrupt!"); +#endif + +#if 0 + // DEVELOPER TESTING ONLY + // TESTING: fake an RH_RF22_IFFERROR + static int counter = 0; + if (_lastInterruptFlags[0] & RH_RF22_IPKSENT && counter++ == 10) + { + _lastInterruptFlags[0] = RH_RF22_IFFERROR; + counter = 0; + } +#endif + + if (_lastInterruptFlags[0] & RH_RF22_IFFERROR) + { + resetFifos(); // Clears the interrupt + if (_mode == RHModeTx) + restartTransmit(); + else if (_mode == RHModeRx) + clearRxBuf(); +// Serial.println("IFFERROR"); + } + // Caution, any delay here may cause a FF underflow or overflow + if (_lastInterruptFlags[0] & RH_RF22_ITXFFAEM) + { + // See if more data has to be loaded into the Tx FIFO + sendNextFragment(); +// Serial.println("ITXFFAEM"); + } + if (_lastInterruptFlags[0] & RH_RF22_IRXFFAFULL) + { + // Caution, any delay here may cause a FF overflow + // Read some data from the Rx FIFO + readNextFragment(); +// Serial.println("IRXFFAFULL"); + } + if (_lastInterruptFlags[0] & RH_RF22_IEXT) + { + // This is not enabled by the base code, but users may want to enable it + handleExternalInterrupt(); +// Serial.println("IEXT"); + } + if (_lastInterruptFlags[1] & RH_RF22_IWUT) + { + // This is not enabled by the base code, but users may want to enable it + handleWakeupTimerInterrupt(); +// Serial.println("IWUT"); + } + if (_lastInterruptFlags[0] & RH_RF22_IPKSENT) + { +// Serial.println("IPKSENT"); + _txGood++; + // Transmission does not automatically clear the tx buffer. + // Could retransmit if we wanted + // RH_RF22 transitions automatically to Idle + _mode = RHModeIdle; + } + if (_lastInterruptFlags[0] & RH_RF22_IPKVALID) + { + uint8_t len = spiRead(RH_RF22_REG_4B_RECEIVED_PACKET_LENGTH); +// Serial.println("IPKVALID"); + + // May have already read one or more fragments + // Get any remaining unread octets, based on the expected length + // First make sure we dont overflow the buffer in the case of a stupid length + // or partial bad receives + if ( len > RH_RF22_MAX_MESSAGE_LEN + || len < _bufLen) + { + _rxBad++; + _mode = RHModeIdle; + clearRxBuf(); + return; // Hmmm receiver buffer overflow. + } + + spiBurstRead(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, len - _bufLen); + _rxHeaderTo = spiRead(RH_RF22_REG_47_RECEIVED_HEADER3); + _rxHeaderFrom = spiRead(RH_RF22_REG_48_RECEIVED_HEADER2); + _rxHeaderId = spiRead(RH_RF22_REG_49_RECEIVED_HEADER1); + _rxHeaderFlags = spiRead(RH_RF22_REG_4A_RECEIVED_HEADER0); + _rxGood++; + _bufLen = len; + _mode = RHModeIdle; + _rxBufValid = true; + } + if (_lastInterruptFlags[0] & RH_RF22_ICRCERROR) + { +// Serial.println("ICRCERR"); + _rxBad++; + clearRxBuf(); + resetRxFifo(); + _mode = RHModeIdle; + setModeRx(); // Keep trying + } + if (_lastInterruptFlags[1] & RH_RF22_IPREAVAL) + { +// Serial.println("IPREAVAL"); + _lastRssi = (int8_t)(-120 + ((spiRead(RH_RF22_REG_26_RSSI) / 2))); + _lastPreambleTime = millis(); + resetRxFifo(); + clearRxBuf(); + } +} + +#if RH_PLATFORM == RH_PLATFORM_ESP8266 +void RH_RF22::loopIsr() +{ + if (flagIsr[0]) + { + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); + flagIsr[0] = false; + } + if (flagIsr[1]) + { + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); + flagIsr[1] = false; + } + if (flagIsr[2]) + { + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); + flagIsr[2] = false; + } +} +#endif + +// These are low level functions that call the interrupt handler for the correct +// instance of RH_RF22. +// 3 interrupts allows us to have 3 different devices +void RH_INTERRUPT_ATTR RH_RF22::isr0() +{ +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + flagIsr[0] = true; +#else + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +#endif +} +void RH_INTERRUPT_ATTR RH_RF22::isr1() +{ +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + flagIsr[1] = true; +#else + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +#endif +} +void RH_INTERRUPT_ATTR RH_RF22::isr2() +{ +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + flagIsr[2] = true; +#else + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +#endif +} + +void RH_RF22::reset() +{ + spiWrite(RH_RF22_REG_07_OPERATING_MODE1, RH_RF22_SWRES); + // Wait for it to settle + delay(1); // SWReset time is nominally 100usec +} + +uint8_t RH_RF22::statusRead() +{ + return spiRead(RH_RF22_REG_02_DEVICE_STATUS); +} + +uint8_t RH_RF22::adcRead(uint8_t adcsel, + uint8_t adcref , + uint8_t adcgain, + uint8_t adcoffs) +{ + uint8_t configuration = adcsel | adcref | (adcgain & RH_RF22_ADCGAIN); + spiWrite(RH_RF22_REG_0F_ADC_CONFIGURATION, configuration | RH_RF22_ADCSTART); + spiWrite(RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET, adcoffs); + + // Conversion time is nominally 305usec + // Wait for the DONE bit + while (!(spiRead(RH_RF22_REG_0F_ADC_CONFIGURATION) & RH_RF22_ADCDONE)) + ; + // Return the value + return spiRead(RH_RF22_REG_11_ADC_VALUE); +} + +uint8_t RH_RF22::temperatureRead(uint8_t tsrange, uint8_t tvoffs) +{ + spiWrite(RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION, tsrange | RH_RF22_ENTSOFFS); + spiWrite(RH_RF22_REG_13_TEMPERATURE_VALUE_OFFSET, tvoffs); + return adcRead(RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR | RH_RF22_ADCREF_BANDGAP_VOLTAGE); +} + +uint16_t RH_RF22::wutRead() +{ + uint8_t buf[2]; + spiBurstRead(RH_RF22_REG_17_WAKEUP_TIMER_VALUE1, buf, 2); + return ((uint16_t)buf[0] << 8) | buf[1]; // Dont rely on byte order +} + +// RFM-22 doc appears to be wrong: WUT for wtm = 10000, r, = 0, d = 0 is about 1 sec +void RH_RF22::setWutPeriod(uint16_t wtm, uint8_t wtr, uint8_t wtd) +{ + uint8_t period[3]; + + period[0] = ((wtr & 0xf) << 2) | (wtd & 0x3); + period[1] = wtm >> 8; + period[2] = wtm & 0xff; + spiBurstWrite(RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1, period, sizeof(period)); +} + +// Returns true if centre + (fhch * fhs) is within limits +// Caution, different versions of the RH_RF22 support different max freq +// so YMMV +bool RH_RF22::setFrequency(float centre, float afcPullInRange) +{ + uint8_t fbsel = RH_RF22_SBSEL; + uint8_t afclimiter; + if (centre < 240.0 || centre > 960.0) // 930.0 for early silicon + return false; + if (centre >= 480.0) + { + if (afcPullInRange < 0.0 || afcPullInRange > 0.318750) + return false; + centre /= 2; + fbsel |= RH_RF22_HBSEL; + afclimiter = afcPullInRange * 1000000.0 / 1250.0; + } + else + { + if (afcPullInRange < 0.0 || afcPullInRange > 0.159375) + return false; + afclimiter = afcPullInRange * 1000000.0 / 625.0; + } + centre /= 10.0; + float integerPart = floor(centre); + float fractionalPart = centre - integerPart; + + uint8_t fb = (uint8_t)integerPart - 24; // Range 0 to 23 + fbsel |= fb; + uint16_t fc = fractionalPart * 64000; + spiWrite(RH_RF22_REG_73_FREQUENCY_OFFSET1, 0); // REVISIT + spiWrite(RH_RF22_REG_74_FREQUENCY_OFFSET2, 0); + spiWrite(RH_RF22_REG_75_FREQUENCY_BAND_SELECT, fbsel); + spiWrite(RH_RF22_REG_76_NOMINAL_CARRIER_FREQUENCY1, fc >> 8); + spiWrite(RH_RF22_REG_77_NOMINAL_CARRIER_FREQUENCY0, fc & 0xff); + spiWrite(RH_RF22_REG_2A_AFC_LIMITER, afclimiter); + return !(statusRead() & RH_RF22_FREQERR); +} + +// Step size in 10kHz increments +// Returns true if centre + (fhch * fhs) is within limits +bool RH_RF22::setFHStepSize(uint8_t fhs) +{ + spiWrite(RH_RF22_REG_7A_FREQUENCY_HOPPING_STEP_SIZE, fhs); + return !(statusRead() & RH_RF22_FREQERR); +} + +// Adds fhch * fhs to centre frequency +// Returns true if centre + (fhch * fhs) is within limits +bool RH_RF22::setFHChannel(uint8_t fhch) +{ + spiWrite(RH_RF22_REG_79_FREQUENCY_HOPPING_CHANNEL_SELECT, fhch); + return !(statusRead() & RH_RF22_FREQERR); +} + +uint8_t RH_RF22::rssiRead() +{ + return spiRead(RH_RF22_REG_26_RSSI); +} + +uint8_t RH_RF22::ezmacStatusRead() +{ + return spiRead(RH_RF22_REG_31_EZMAC_STATUS); +} + +void RH_RF22::setOpMode(uint8_t mode) +{ + spiWrite(RH_RF22_REG_07_OPERATING_MODE1, mode); +} + +void RH_RF22::setModeIdle() +{ + if (_mode != RHModeIdle) + { + setOpMode(_idleMode); + _mode = RHModeIdle; + } +} + +bool RH_RF22::sleep() +{ + if (_mode != RHModeSleep) + { + setOpMode(0); + _mode = RHModeSleep; + } + return true; +} + +void RH_RF22::setModeRx() +{ + if (_mode != RHModeRx) + { + setOpMode(_idleMode | RH_RF22_RXON); + _mode = RHModeRx; + } +} + +void RH_RF22::setModeTx() +{ + if (_mode != RHModeTx) + { + setOpMode(_idleMode | RH_RF22_TXON); + // Hmmm, if you dont clear the RX FIFO here, then it appears that going + // to transmit mode in the middle of a receive can corrupt the + // RX FIFO + resetRxFifo(); + _mode = RHModeTx; + } +} + +void RH_RF22::setTxPower(uint8_t power) +{ + spiWrite(RH_RF22_REG_6D_TX_POWER, power | RH_RF22_LNA_SW); // On RF23, LNA_SW must be set. +} + +// Sets registers from a canned modem configuration structure +void RH_RF22::setModemRegisters(const ModemConfig* config) +{ + spiWrite(RH_RF22_REG_1C_IF_FILTER_BANDWIDTH, config->reg_1c); + spiWrite(RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE, config->reg_1f); + spiBurstWrite(RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE, &config->reg_20, 6); + spiBurstWrite(RH_RF22_REG_2C_OOK_COUNTER_VALUE_1, &config->reg_2c, 3); + spiWrite(RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING, config->reg_58); + spiWrite(RH_RF22_REG_69_AGC_OVERRIDE1, config->reg_69); + spiBurstWrite(RH_RF22_REG_6E_TX_DATA_RATE1, &config->reg_6e, 5); +} + +// Set one of the canned FSK Modem configs +// Returns true if its a valid choice +bool RH_RF22::setModemConfig(ModemConfigChoice index) +{ + if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig))) + return false; + + RH_RF22::ModemConfig cfg; + memcpy_P(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF22::ModemConfig)); + setModemRegisters(&cfg); + + return true; +} + +// REVISIT: top bit is in Header Control 2 0x33 +void RH_RF22::setPreambleLength(uint8_t nibbles) +{ + spiWrite(RH_RF22_REG_34_PREAMBLE_LENGTH, nibbles); +} + +// Caution doesnt set sync word len in Header Control 2 0x33 +void RH_RF22::setSyncWords(const uint8_t* syncWords, uint8_t len) +{ + spiBurstWrite(RH_RF22_REG_36_SYNC_WORD3, syncWords, len); +} + +void RH_RF22::clearRxBuf() +{ + ATOMIC_BLOCK_START; + _bufLen = 0; + _rxBufValid = false; + ATOMIC_BLOCK_END; +} + +bool RH_RF22::available() +{ + if (!_rxBufValid) + { +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + loopIsr(); +#endif + if (_mode == RHModeTx) + return false; + setModeRx(); // Make sure we are receiving + YIELD; // Wait for any previous transmit to finish + } + return _rxBufValid; +} + +#if RH_PLATFORM == RH_PLATFORM_ESP8266 +bool RH_RF22::waitPacketSent() +{ + while (_mode == RHModeTx) + { + loopIsr(); + YIELD; // Make sure the watchdog is fed + } + return true; +} +#endif + +bool RH_RF22::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; + } + clearRxBuf(); +// printBuffer("recv:", buf, *len); + return true; +} + +void RH_RF22::clearTxBuf() +{ + ATOMIC_BLOCK_START; + _bufLen = 0; + _txBufSentIndex = 0; + ATOMIC_BLOCK_END; +} + +void RH_RF22::startTransmit() +{ + sendNextFragment(); // Actually the first fragment + spiWrite(RH_RF22_REG_3E_PACKET_LENGTH, _bufLen); // Total length that will be sent + setModeTx(); // Start the transmitter, turns off the receiver +} + +// Restart the transmission of a packet that had a problem +void RH_RF22::restartTransmit() +{ + _mode = RHModeIdle; + _txBufSentIndex = 0; +// Serial.println("Restart"); + startTransmit(); +} + +bool RH_RF22::send(const uint8_t* data, uint8_t len) +{ + bool ret = true; + waitPacketSent(); + + if (!waitCAD()) + return false; // Check channel activity + + ATOMIC_BLOCK_START; + spiWrite(RH_RF22_REG_3A_TRANSMIT_HEADER3, _txHeaderTo); + spiWrite(RH_RF22_REG_3B_TRANSMIT_HEADER2, _txHeaderFrom); + spiWrite(RH_RF22_REG_3C_TRANSMIT_HEADER1, _txHeaderId); + spiWrite(RH_RF22_REG_3D_TRANSMIT_HEADER0, _txHeaderFlags); + if (!fillTxBuf(data, len)) + ret = false; + else + startTransmit(); + ATOMIC_BLOCK_END; +// printBuffer("send:", data, len); + return ret; +} + +bool RH_RF22::fillTxBuf(const uint8_t* data, uint8_t len) +{ + clearTxBuf(); + if (!len) + return false; + return appendTxBuf(data, len); +} + +bool RH_RF22::appendTxBuf(const uint8_t* data, uint8_t len) +{ + if (((uint16_t)_bufLen + len) > RH_RF22_MAX_MESSAGE_LEN) + return false; + ATOMIC_BLOCK_START; + memcpy(_buf + _bufLen, data, len); + _bufLen += len; + ATOMIC_BLOCK_END; +// printBuffer("txbuf:", _buf, _bufLen); + return true; +} + +// Assumption: there is currently <= RH_RF22_TXFFAEM_THRESHOLD bytes in the Tx FIFO +void RH_RF22::sendNextFragment() +{ + if (_txBufSentIndex < _bufLen) + { + // Some left to send? + uint8_t len = _bufLen - _txBufSentIndex; + // But dont send too much + if (len > (RH_RF22_FIFO_SIZE - RH_RF22_TXFFAEM_THRESHOLD - 1)) + len = (RH_RF22_FIFO_SIZE - RH_RF22_TXFFAEM_THRESHOLD - 1); + spiBurstWrite(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _txBufSentIndex, len); +// printBuffer("frag:", _buf + _txBufSentIndex, len); + _txBufSentIndex += len; + } +} + +// Assumption: there are at least RH_RF22_RXFFAFULL_THRESHOLD in the RX FIFO +// That means it should only be called after a RXFFAFULL interrupt +void RH_RF22::readNextFragment() +{ + if (((uint16_t)_bufLen + RH_RF22_RXFFAFULL_THRESHOLD) > RH_RF22_MAX_MESSAGE_LEN) + return; // Hmmm receiver overflow. Should never occur + + // Read the RH_RF22_RXFFAFULL_THRESHOLD octets that should be there + spiBurstRead(RH_RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, RH_RF22_RXFFAFULL_THRESHOLD); + _bufLen += RH_RF22_RXFFAFULL_THRESHOLD; +} + +// Clear the FIFOs +void RH_RF22::resetFifos() +{ + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRRX | RH_RF22_FFCLRTX); + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0); +} + +// Clear the Rx FIFO +void RH_RF22::resetRxFifo() +{ + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRRX); + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0); + _rxBufValid = false; +} + +// CLear the TX FIFO +void RH_RF22::resetTxFifo() +{ + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, RH_RF22_FFCLRTX); + spiWrite(RH_RF22_REG_08_OPERATING_MODE2, 0); +} + +// Default implmentation does nothing. Override if you wish +void RH_RF22::handleExternalInterrupt() +{ +} + +// Default implmentation does nothing. Override if you wish +void RH_RF22::handleWakeupTimerInterrupt() +{ +} + +void RH_RF22::setPromiscuous(bool promiscuous) +{ + RHSPIDriver::setPromiscuous(promiscuous); + spiWrite(RH_RF22_REG_43_HEADER_ENABLE3, promiscuous ? 0x00 : 0xff); +} + +bool RH_RF22::setCRCPolynomial(CRCPolynomial polynomial) +{ + if (polynomial >= CRC_CCITT && + polynomial <= CRC_Biacheva) + { + _polynomial = polynomial; + return true; + } + else + return false; +} + +uint8_t RH_RF22::maxMessageLength() +{ + return RH_RF22_MAX_MESSAGE_LEN; +} + +void RH_RF22::setThisAddress(uint8_t thisAddress) +{ + RHSPIDriver::setThisAddress(thisAddress); + spiWrite(RH_RF22_REG_3F_CHECK_HEADER3, thisAddress); +} + +uint32_t RH_RF22::getLastPreambleTime() +{ + return _lastPreambleTime; +} + +void RH_RF22::setGpioReversed(bool gpioReversed) +{ + // Ensure the antenna can be switched automatically according to transmit and receive + // This assumes GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit + // This assumes GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive + if (gpioReversed) + { + // Reversed for HAB-RFM22B-BOA HAB-RFM22B-BO, also Si4432 sold by Dorji.com via Tindie.com. + spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x15) ; // RX state + spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x12) ; // TX state + } + else + { + spiWrite(RH_RF22_REG_0B_GPIO_CONFIGURATION0, 0x12) ; // TX state + spiWrite(RH_RF22_REG_0C_GPIO_CONFIGURATION1, 0x15) ; // RX state + } +} + diff --git a/RH_RF22.h b/RH_RF22.h new file mode 100644 index 0000000..6163b73 --- /dev/null +++ b/RH_RF22.h @@ -0,0 +1,1393 @@ +// RH_RF22.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2011 Mike McCauley +// $Id: RH_RF22.h,v 1.38 2020/07/05 08:52:21 mikem Exp mikem $ +// + +#ifndef RH_RF22_h +#define RH_RF22_h + +#include +#include + +// This is the maximum number of interrupts the library can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_RF22_NUM_INTERRUPTS 3 + +// This is the bit in the SPI address that marks it as a write +#define RH_RF22_SPI_WRITE_MASK 0x80 + +// This is the maximum message length that can be supported by this library. Limited by +// the single message length octet in the header. +// Yes, 255 is correct even though the FIFO size in the RF22 is only +// 64 octets. We use interrupts to refill the Tx FIFO during transmission and to empty the +// Rx FIFO during reception +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +#ifndef RH_RF22_MAX_MESSAGE_LEN +//#define RH_RF22_MAX_MESSAGE_LEN 255 +#define RH_RF22_MAX_MESSAGE_LEN 50 +#endif + +// Max number of octets the RF22 Rx and Tx FIFOs can hold +#define RH_RF22_FIFO_SIZE 64 + +// These values we set for FIFO thresholds (4, 55) are actually the same as the POR values +#define RH_RF22_TXFFAEM_THRESHOLD 4 +#define RH_RF22_RXFFAFULL_THRESHOLD 55 + +// Number of registers to be passed to setModemConfig(). Obsolete. +#define RH_RF22_NUM_MODEM_CONFIG_REGS 18 + +// Register names +#define RH_RF22_REG_00_DEVICE_TYPE 0x00 +#define RH_RF22_REG_01_VERSION_CODE 0x01 +#define RH_RF22_REG_02_DEVICE_STATUS 0x02 +#define RH_RF22_REG_03_INTERRUPT_STATUS1 0x03 +#define RH_RF22_REG_04_INTERRUPT_STATUS2 0x04 +#define RH_RF22_REG_05_INTERRUPT_ENABLE1 0x05 +#define RH_RF22_REG_06_INTERRUPT_ENABLE2 0x06 +#define RH_RF22_REG_07_OPERATING_MODE1 0x07 +#define RH_RF22_REG_08_OPERATING_MODE2 0x08 +#define RH_RF22_REG_09_OSCILLATOR_LOAD_CAPACITANCE 0x09 +#define RH_RF22_REG_0A_UC_OUTPUT_CLOCK 0x0a +#define RH_RF22_REG_0B_GPIO_CONFIGURATION0 0x0b +#define RH_RF22_REG_0C_GPIO_CONFIGURATION1 0x0c +#define RH_RF22_REG_0D_GPIO_CONFIGURATION2 0x0d +#define RH_RF22_REG_0E_IO_PORT_CONFIGURATION 0x0e +#define RH_RF22_REG_0F_ADC_CONFIGURATION 0x0f +#define RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET 0x10 +#define RH_RF22_REG_11_ADC_VALUE 0x11 +#define RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION 0x12 +#define RH_RF22_REG_13_TEMPERATURE_VALUE_OFFSET 0x13 +#define RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1 0x14 +#define RH_RF22_REG_15_WAKEUP_TIMER_PERIOD2 0x15 +#define RH_RF22_REG_16_WAKEUP_TIMER_PERIOD3 0x16 +#define RH_RF22_REG_17_WAKEUP_TIMER_VALUE1 0x17 +#define RH_RF22_REG_18_WAKEUP_TIMER_VALUE2 0x18 +#define RH_RF22_REG_19_LDC_MODE_DURATION 0x19 +#define RH_RF22_REG_1A_LOW_BATTERY_DETECTOR_THRESHOLD 0x1a +#define RH_RF22_REG_1B_BATTERY_VOLTAGE_LEVEL 0x1b +#define RH_RF22_REG_1C_IF_FILTER_BANDWIDTH 0x1c +#define RH_RF22_REG_1D_AFC_LOOP_GEARSHIFT_OVERRIDE 0x1d +#define RH_RF22_REG_1E_AFC_TIMING_CONTROL 0x1e +#define RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE 0x1f +#define RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE 0x20 +#define RH_RF22_REG_21_CLOCK_RECOVERY_OFFSET2 0x21 +#define RH_RF22_REG_22_CLOCK_RECOVERY_OFFSET1 0x22 +#define RH_RF22_REG_23_CLOCK_RECOVERY_OFFSET0 0x23 +#define RH_RF22_REG_24_CLOCK_RECOVERY_TIMING_LOOP_GAIN1 0x24 +#define RH_RF22_REG_25_CLOCK_RECOVERY_TIMING_LOOP_GAIN0 0x25 +#define RH_RF22_REG_26_RSSI 0x26 +#define RH_RF22_REG_27_RSSI_THRESHOLD 0x27 +#define RH_RF22_REG_28_ANTENNA_DIVERSITY1 0x28 +#define RH_RF22_REG_29_ANTENNA_DIVERSITY2 0x29 +#define RH_RF22_REG_2A_AFC_LIMITER 0x2a +#define RH_RF22_REG_2B_AFC_CORRECTION_READ 0x2b +#define RH_RF22_REG_2C_OOK_COUNTER_VALUE_1 0x2c +#define RH_RF22_REG_2D_OOK_COUNTER_VALUE_2 0x2d +#define RH_RF22_REG_2E_SLICER_PEAK_HOLD 0x2e +#define RH_RF22_REG_30_DATA_ACCESS_CONTROL 0x30 +#define RH_RF22_REG_31_EZMAC_STATUS 0x31 +#define RH_RF22_REG_32_HEADER_CONTROL1 0x32 +#define RH_RF22_REG_33_HEADER_CONTROL2 0x33 +#define RH_RF22_REG_34_PREAMBLE_LENGTH 0x34 +#define RH_RF22_REG_35_PREAMBLE_DETECTION_CONTROL1 0x35 +#define RH_RF22_REG_36_SYNC_WORD3 0x36 +#define RH_RF22_REG_37_SYNC_WORD2 0x37 +#define RH_RF22_REG_38_SYNC_WORD1 0x38 +#define RH_RF22_REG_39_SYNC_WORD0 0x39 +#define RH_RF22_REG_3A_TRANSMIT_HEADER3 0x3a +#define RH_RF22_REG_3B_TRANSMIT_HEADER2 0x3b +#define RH_RF22_REG_3C_TRANSMIT_HEADER1 0x3c +#define RH_RF22_REG_3D_TRANSMIT_HEADER0 0x3d +#define RH_RF22_REG_3E_PACKET_LENGTH 0x3e +#define RH_RF22_REG_3F_CHECK_HEADER3 0x3f +#define RH_RF22_REG_40_CHECK_HEADER2 0x40 +#define RH_RF22_REG_41_CHECK_HEADER1 0x41 +#define RH_RF22_REG_42_CHECK_HEADER0 0x42 +#define RH_RF22_REG_43_HEADER_ENABLE3 0x43 +#define RH_RF22_REG_44_HEADER_ENABLE2 0x44 +#define RH_RF22_REG_45_HEADER_ENABLE1 0x45 +#define RH_RF22_REG_46_HEADER_ENABLE0 0x46 +#define RH_RF22_REG_47_RECEIVED_HEADER3 0x47 +#define RH_RF22_REG_48_RECEIVED_HEADER2 0x48 +#define RH_RF22_REG_49_RECEIVED_HEADER1 0x49 +#define RH_RF22_REG_4A_RECEIVED_HEADER0 0x4a +#define RH_RF22_REG_4B_RECEIVED_PACKET_LENGTH 0x4b +#define RH_RF22_REG_50_ANALOG_TEST_BUS_SELECT 0x50 +#define RH_RF22_REG_51_DIGITAL_TEST_BUS_SELECT 0x51 +#define RH_RF22_REG_52_TX_RAMP_CONTROL 0x52 +#define RH_RF22_REG_53_PLL_TUNE_TIME 0x53 +#define RH_RF22_REG_55_CALIBRATION_CONTROL 0x55 +#define RH_RF22_REG_56_MODEM_TEST 0x56 +#define RH_RF22_REG_57_CHARGE_PUMP_TEST 0x57 +#define RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING 0x58 +#define RH_RF22_REG_59_DIVIDER_CURRENT_TRIMMING 0x59 +#define RH_RF22_REG_5A_VCO_CURRENT_TRIMMING 0x5a +#define RH_RF22_REG_5B_VCO_CALIBRATION 0x5b +#define RH_RF22_REG_5C_SYNTHESIZER_TEST 0x5c +#define RH_RF22_REG_5D_BLOCK_ENABLE_OVERRIDE1 0x5d +#define RH_RF22_REG_5E_BLOCK_ENABLE_OVERRIDE2 0x5e +#define RH_RF22_REG_5F_BLOCK_ENABLE_OVERRIDE3 0x5f +#define RH_RF22_REG_60_CHANNEL_FILTER_COEFFICIENT_ADDRESS 0x60 +#define RH_RF22_REG_61_CHANNEL_FILTER_COEFFICIENT_VALUE 0x61 +#define RH_RF22_REG_62_CRYSTAL_OSCILLATOR_POR_CONTROL 0x62 +#define RH_RF22_REG_63_RC_OSCILLATOR_COARSE_CALIBRATION 0x63 +#define RH_RF22_REG_64_RC_OSCILLATOR_FINE_CALIBRATION 0x64 +#define RH_RF22_REG_65_LDO_CONTROL_OVERRIDE 0x65 +#define RH_RF22_REG_66_LDO_LEVEL_SETTINGS 0x66 +#define RH_RF22_REG_67_DELTA_SIGMA_ADC_TUNING1 0x67 +#define RH_RF22_REG_68_DELTA_SIGMA_ADC_TUNING2 0x68 +#define RH_RF22_REG_69_AGC_OVERRIDE1 0x69 +#define RH_RF22_REG_6A_AGC_OVERRIDE2 0x6a +#define RH_RF22_REG_6B_GFSK_FIR_FILTER_COEFFICIENT_ADDRESS 0x6b +#define RH_RF22_REG_6C_GFSK_FIR_FILTER_COEFFICIENT_VALUE 0x6c +#define RH_RF22_REG_6D_TX_POWER 0x6d +#define RH_RF22_REG_6E_TX_DATA_RATE1 0x6e +#define RH_RF22_REG_6F_TX_DATA_RATE0 0x6f +#define RH_RF22_REG_70_MODULATION_CONTROL1 0x70 +#define RH_RF22_REG_71_MODULATION_CONTROL2 0x71 +#define RH_RF22_REG_72_FREQUENCY_DEVIATION 0x72 +#define RH_RF22_REG_73_FREQUENCY_OFFSET1 0x73 +#define RH_RF22_REG_74_FREQUENCY_OFFSET2 0x74 +#define RH_RF22_REG_75_FREQUENCY_BAND_SELECT 0x75 +#define RH_RF22_REG_76_NOMINAL_CARRIER_FREQUENCY1 0x76 +#define RH_RF22_REG_77_NOMINAL_CARRIER_FREQUENCY0 0x77 +#define RH_RF22_REG_79_FREQUENCY_HOPPING_CHANNEL_SELECT 0x79 +#define RH_RF22_REG_7A_FREQUENCY_HOPPING_STEP_SIZE 0x7a +#define RH_RF22_REG_7C_TX_FIFO_CONTROL1 0x7c +#define RH_RF22_REG_7D_TX_FIFO_CONTROL2 0x7d +#define RH_RF22_REG_7E_RX_FIFO_CONTROL 0x7e +#define RH_RF22_REG_7F_FIFO_ACCESS 0x7f + +// These register masks etc are named wherever possible +// corresponding to the bit and field names in the RF-22 Manual +// RH_RF22_REG_00_DEVICE_TYPE 0x00 +#define RH_RF22_DEVICE_TYPE_RX_TRX 0x08 +#define RH_RF22_DEVICE_TYPE_TX 0x07 + +// RH_RF22_REG_02_DEVICE_STATUS 0x02 +#define RH_RF22_FFOVL 0x80 +#define RH_RF22_FFUNFL 0x40 +#define RH_RF22_RXFFEM 0x20 +#define RH_RF22_HEADERR 0x10 +#define RH_RF22_FREQERR 0x08 +#define RH_RF22_LOCKDET 0x04 +#define RH_RF22_CPS 0x03 +#define RH_RF22_CPS_IDLE 0x00 +#define RH_RF22_CPS_RX 0x01 +#define RH_RF22_CPS_TX 0x10 + +// RH_RF22_REG_03_INTERRUPT_STATUS1 0x03 +#define RH_RF22_IFFERROR 0x80 +#define RH_RF22_ITXFFAFULL 0x40 +#define RH_RF22_ITXFFAEM 0x20 +#define RH_RF22_IRXFFAFULL 0x10 +#define RH_RF22_IEXT 0x08 +#define RH_RF22_IPKSENT 0x04 +#define RH_RF22_IPKVALID 0x02 +#define RH_RF22_ICRCERROR 0x01 + +// RH_RF22_REG_04_INTERRUPT_STATUS2 0x04 +#define RH_RF22_ISWDET 0x80 +#define RH_RF22_IPREAVAL 0x40 +#define RH_RF22_IPREAINVAL 0x20 +#define RH_RF22_IRSSI 0x10 +#define RH_RF22_IWUT 0x08 +#define RH_RF22_ILBD 0x04 +#define RH_RF22_ICHIPRDY 0x02 +#define RH_RF22_IPOR 0x01 + +// RH_RF22_REG_05_INTERRUPT_ENABLE1 0x05 +#define RH_RF22_ENFFERR 0x80 +#define RH_RF22_ENTXFFAFULL 0x40 +#define RH_RF22_ENTXFFAEM 0x20 +#define RH_RF22_ENRXFFAFULL 0x10 +#define RH_RF22_ENEXT 0x08 +#define RH_RF22_ENPKSENT 0x04 +#define RH_RF22_ENPKVALID 0x02 +#define RH_RF22_ENCRCERROR 0x01 + +// RH_RF22_REG_06_INTERRUPT_ENABLE2 0x06 +#define RH_RF22_ENSWDET 0x80 +#define RH_RF22_ENPREAVAL 0x40 +#define RH_RF22_ENPREAINVAL 0x20 +#define RH_RF22_ENRSSI 0x10 +#define RH_RF22_ENWUT 0x08 +#define RH_RF22_ENLBDI 0x04 +#define RH_RF22_ENCHIPRDY 0x02 +#define RH_RF22_ENPOR 0x01 + +// RH_RF22_REG_07_OPERATING_MODE 0x07 +#define RH_RF22_SWRES 0x80 +#define RH_RF22_ENLBD 0x40 +#define RH_RF22_ENWT 0x20 +#define RH_RF22_X32KSEL 0x10 +#define RH_RF22_TXON 0x08 +#define RH_RF22_RXON 0x04 +#define RH_RF22_PLLON 0x02 +#define RH_RF22_XTON 0x01 + +// RH_RF22_REG_08_OPERATING_MODE2 0x08 +#define RH_RF22_ANTDIV 0xc0 +#define RH_RF22_RXMPK 0x10 +#define RH_RF22_AUTOTX 0x08 +#define RH_RF22_ENLDM 0x04 +#define RH_RF22_FFCLRRX 0x02 +#define RH_RF22_FFCLRTX 0x01 + +// RH_RF22_REG_0F_ADC_CONFIGURATION 0x0f +#define RH_RF22_ADCSTART 0x80 +#define RH_RF22_ADCDONE 0x80 +#define RH_RF22_ADCSEL 0x70 +#define RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR 0x00 +#define RH_RF22_ADCSEL_GPIO0_SINGLE_ENDED 0x10 +#define RH_RF22_ADCSEL_GPIO1_SINGLE_ENDED 0x20 +#define RH_RF22_ADCSEL_GPIO2_SINGLE_ENDED 0x30 +#define RH_RF22_ADCSEL_GPIO0_GPIO1_DIFFERENTIAL 0x40 +#define RH_RF22_ADCSEL_GPIO1_GPIO2_DIFFERENTIAL 0x50 +#define RH_RF22_ADCSEL_GPIO0_GPIO2_DIFFERENTIAL 0x60 +#define RH_RF22_ADCSEL_GND 0x70 +#define RH_RF22_ADCREF 0x0c +#define RH_RF22_ADCREF_BANDGAP_VOLTAGE 0x00 +#define RH_RF22_ADCREF_VDD_ON_3 0x08 +#define RH_RF22_ADCREF_VDD_ON_2 0x0c +#define RH_RF22_ADCGAIN 0x03 + +// RH_RF22_REG_10_ADC_SENSOR_AMP_OFFSET 0x10 +#define RH_RF22_ADCOFFS 0x0f + +// RH_RF22_REG_12_TEMPERATURE_SENSOR_CALIBRATION 0x12 +#define RH_RF22_TSRANGE 0xc0 +#define RH_RF22_TSRANGE_M64_64C 0x00 +#define RH_RF22_TSRANGE_M64_192C 0x40 +#define RH_RF22_TSRANGE_0_128C 0x80 +#define RH_RF22_TSRANGE_M40_216F 0xc0 +#define RH_RF22_ENTSOFFS 0x20 +#define RH_RF22_ENTSTRIM 0x10 +#define RH_RF22_TSTRIM 0x0f + +// RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1 0x14 +#define RH_RF22_WTR 0x3c +#define RH_RF22_WTD 0x03 + +// RH_RF22_REG_1D_AFC_LOOP_GEARSHIFT_OVERRIDE 0x1d +#define RH_RF22_AFBCD 0x80 +#define RH_RF22_ENAFC 0x40 +#define RH_RF22_AFCGEARH 0x38 +#define RH_RF22_AFCGEARL 0x07 + +// RH_RF22_REG_1E_AFC_TIMING_CONTROL 0x1e +#define RH_RF22_SWAIT_TIMER 0xc0 +#define RH_RF22_SHWAIT 0x38 +#define RH_RF22_ANWAIT 0x07 + +// RH_RF22_REG_30_DATA_ACCESS_CONTROL 0x30 +#define RH_RF22_ENPACRX 0x80 +#define RH_RF22_MSBFRST 0x00 +#define RH_RF22_LSBFRST 0x40 +#define RH_RF22_CRCHDRS 0x00 +#define RH_RF22_CRCDONLY 0x20 +#define RH_RF22_SKIP2PH 0x10 +#define RH_RF22_ENPACTX 0x08 +#define RH_RF22_ENCRC 0x04 +#define RH_RF22_CRC 0x03 +#define RH_RF22_CRC_CCITT 0x00 +#define RH_RF22_CRC_CRC_16_IBM 0x01 +#define RH_RF22_CRC_IEC_16 0x02 +#define RH_RF22_CRC_BIACHEVA 0x03 + +// RH_RF22_REG_32_HEADER_CONTROL1 0x32 +#define RH_RF22_BCEN 0xf0 +#define RH_RF22_BCEN_NONE 0x00 +#define RH_RF22_BCEN_HEADER0 0x10 +#define RH_RF22_BCEN_HEADER1 0x20 +#define RH_RF22_BCEN_HEADER2 0x40 +#define RH_RF22_BCEN_HEADER3 0x80 +#define RH_RF22_HDCH 0x0f +#define RH_RF22_HDCH_NONE 0x00 +#define RH_RF22_HDCH_HEADER0 0x01 +#define RH_RF22_HDCH_HEADER1 0x02 +#define RH_RF22_HDCH_HEADER2 0x04 +#define RH_RF22_HDCH_HEADER3 0x08 + +// RH_RF22_REG_33_HEADER_CONTROL2 0x33 +#define RH_RF22_HDLEN 0x70 +#define RH_RF22_HDLEN_0 0x00 +#define RH_RF22_HDLEN_1 0x10 +#define RH_RF22_HDLEN_2 0x20 +#define RH_RF22_HDLEN_3 0x30 +#define RH_RF22_HDLEN_4 0x40 +#define RH_RF22_VARPKLEN 0x00 +#define RH_RF22_FIXPKLEN 0x08 +#define RH_RF22_SYNCLEN 0x06 +#define RH_RF22_SYNCLEN_1 0x00 +#define RH_RF22_SYNCLEN_2 0x02 +#define RH_RF22_SYNCLEN_3 0x04 +#define RH_RF22_SYNCLEN_4 0x06 +#define RH_RF22_PREALEN8 0x01 + +// RH_RF22_REG_6D_TX_POWER 0x6d +// https://www.sparkfun.com/datasheets/Wireless/General/RFM22B.pdf +#define RH_RF22_PAPEAKVAL 0x80 +#define RH_RF22_PAPEAKEN 0x40 +#define RH_RF22_PAPEAKLVL 0x30 +#define RH_RF22_PAPEAKLVL6_5 0x00 +#define RH_RF22_PAPEAKLVL7 0x10 +#define RH_RF22_PAPEAKLVL7_5 0x20 +#define RH_RF22_PAPEAKLVL8 0x30 +#define RH_RF22_LNA_SW 0x08 +#define RH_RF22_TXPOW 0x07 +#define RH_RF22_TXPOW_4X31 0x08 // Not used in RFM22B +// For RFM22B: +#define RH_RF22_TXPOW_1DBM 0x00 +#define RH_RF22_TXPOW_2DBM 0x01 +#define RH_RF22_TXPOW_5DBM 0x02 +#define RH_RF22_TXPOW_8DBM 0x03 +#define RH_RF22_TXPOW_11DBM 0x04 +#define RH_RF22_TXPOW_14DBM 0x05 +#define RH_RF22_TXPOW_17DBM 0x06 +#define RH_RF22_TXPOW_20DBM 0x07 +// RFM23B only: +#define RH_RF22_RF23B_TXPOW_M8DBM 0x00 // -8dBm +#define RH_RF22_RF23B_TXPOW_M5DBM 0x01 // -5dBm +#define RH_RF22_RF23B_TXPOW_M2DBM 0x02 // -2dBm +#define RH_RF22_RF23B_TXPOW_1DBM 0x03 // 1dBm +#define RH_RF22_RF23B_TXPOW_4DBM 0x04 // 4dBm +#define RH_RF22_RF23B_TXPOW_7DBM 0x05 // 7dBm +#define RH_RF22_RF23B_TXPOW_10DBM 0x06 // 10dBm +#define RH_RF22_RF23B_TXPOW_13DBM 0x07 // 13dBm +// RFM23BP only: +#define RH_RF22_RF23BP_TXPOW_28DBM 0x05 // 28dBm +#define RH_RF22_RF23BP_TXPOW_29DBM 0x06 // 29dBm +#define RH_RF22_RF23BP_TXPOW_30DBM 0x07 // 30dBm + +// RH_RF22_REG_71_MODULATION_CONTROL2 0x71 +#define RH_RF22_TRCLK 0xc0 +#define RH_RF22_TRCLK_NONE 0x00 +#define RH_RF22_TRCLK_GPIO 0x40 +#define RH_RF22_TRCLK_SDO 0x80 +#define RH_RF22_TRCLK_NIRQ 0xc0 +#define RH_RF22_DTMOD 0x30 +#define RH_RF22_DTMOD_DIRECT_GPIO 0x00 +#define RH_RF22_DTMOD_DIRECT_SDI 0x10 +#define RH_RF22_DTMOD_FIFO 0x20 +#define RH_RF22_DTMOD_PN9 0x30 +#define RH_RF22_ENINV 0x08 +#define RH_RF22_FD8 0x04 +#define RH_RF22_MODTYP 0x30 +#define RH_RF22_MODTYP_UNMODULATED 0x00 +#define RH_RF22_MODTYP_OOK 0x01 +#define RH_RF22_MODTYP_FSK 0x02 +#define RH_RF22_MODTYP_GFSK 0x03 + + +// RH_RF22_REG_75_FREQUENCY_BAND_SELECT 0x75 +#define RH_RF22_SBSEL 0x40 +#define RH_RF22_HBSEL 0x20 +#define RH_RF22_FB 0x1f + +// Define this to include Serial printing in diagnostic routines +#define RH_RF22_HAVE_SERIAL + +///////////////////////////////////////////////////////////////////// +/// \class RH_RF22 RH_RF22.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF22 and compatible radio transceiver. +/// +/// Works with RF22, RF23 based radio modules, and compatible chips and modules, including: +/// - RF22 bare module: http://www.sparkfun.com/products/10153 +/// (Caution, that is a 3.3V part, and requires a 3.3V CPU such as Teensy etc or level shifters) +/// - RF22 shield: http://www.sparkfun.com/products/11018 +/// - RF22 integrated board http://www.anarduino.com/miniwireless +/// - RFM23BP bare module: http://www.anarduino.com/details.jsp?pid=130 +/// - Silicon Labs Si4430/31/32 based modules. S4432 is equivalent to RF22. Si4431/30 is equivalent to RF23. +/// +/// Data based on https://www.sparkfun.com/datasheets/Wireless/General/RFM22B.pdf +/// +/// \par Overview +/// +/// This base class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 255 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// On transmission, the TO and FROM addresses default to 0x00, unless changed by a subclass. +/// On reception the TO addressed is checked against the node address (defaults to 0x00) or the +/// broadcast address (which is 0xff). The ID and FLAGS are set to 0, and not checked by this class. +/// This permits use of the this base RH_RF22 class as an +/// unaddressed, unreliable datagram service without the use of one the RadioHead Manager classes. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// \par Details +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF +/// RF22 and RF23 based radio modules, and compatible chips and modules, +/// including the RFM22B transceiver module such as +/// this bare module: http://www.sparkfun.com/products/10153 +/// and this shield: http://www.sparkfun.com/products/11018 +/// and this module: http://www.hoperfusa.com/details.jsp?pid=131 +/// and this integrated board: http://www.anarduino.com/miniwireless +/// and RF23BP modules such as this http://www.anarduino.com/details.jsp?pid=130 +/// +/// The Hope-RF (http://www.hoperf.com) RFM22B (http://www.hoperf.com/rf_fsk/fsk/RFM22B.htm) +/// is a low-cost ISM transceiver module. It supports FSK, GFSK, OOK over a wide +/// range of frequencies and programmable data rates. +/// Manual can be found at https://www.sparkfun.com/datasheets/Wireless/General/RFM22.PDF +/// +/// This library provides functions for sending and receiving messages of up to 255 octets on any +/// frequency supported by the RF22B, in a range of predefined data rates and frequency deviations. +/// Frequency can be set with 312Hz precision to any frequency from 240.0MHz to 960.0MHz. +/// +/// Up to 3 RF22B modules can be connected to an Arduino, permitting the construction of translators +/// and frequency changers, etc. +/// +/// The following modulation types are suppported with a range of modem configurations for +/// common data rates and frequency deviations: +/// - GFSK Gaussian Frequency Shift Keying +/// - FSK Frequency Shift Keying +/// - OOK On-Off Keying +/// +/// Support for other RF22B features such as on-chip temperature measurement, analog-digital +/// converter, transmitter power control etc is also provided. +/// +/// Tested on Arduino Diecimila, Uno and Mega with arduino-0021, 1.0.5 +/// on OpenSuSE 13.1 and avr-libc-1.6.1-1.15, +/// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5. +/// With HopeRF RFM22 modules that appear to have RF22B chips on board: +/// - Device Type Code = 0x08 (RX/TRX) +/// - Version Code = 0x06 +/// Works on Duo. Works with Sparkfun RFM22 Wireless shields. Works with RFM22 modules from http://www.hoperfusa.com/ +/// Works with Arduino 1.0 to at least 1.0.5. Works on Maple, Flymaple, Uno32 (with ChipKIT Core with Arduino IDE). +/// +/// \par Packet Format +/// +/// All messages sent and received by this Driver must conform to this packet format: +/// +/// - 8 nibbles (4 octets) PREAMBLE +/// - 2 octets SYNC 0x2d, 0xd4 +/// - 4 octets HEADER: (TO, FROM, ID, FLAGS) +/// - 1 octet LENGTH (0 to 255), number of octets in DATA +/// - 0 to 255 octets DATA +/// - 2 octets CRC computed with CRC16(IBM), computed on HEADER, LENGTH and DATA +/// +/// For technical reasons, the message format is not protocol compatible with the +/// 'HopeRF Radio Transceiver Message Library for Arduino' http://www.airspayce.com/mikem/arduino/HopeRF from the same author. Nor is it compatible with +/// 'Virtual Wire' http://www.airspayce.com/mikem/arduino/VirtualWire.pdf also from the same author. +/// +/// \par Connecting RFM-22 to Arduino +/// +/// If you have the Sparkfun RFM22 Shield (https://www.sparkfun.com/products/11018) +/// the connections described below are done for you on the shield, no changes required, +/// just add headers and plug it in to an Arduino (but not and Arduino Mega, see below) +/// +/// The physical connection between the RF22B and the Arduino requires 3.3V, +/// the 3 x SPI pins (SCK, SDI, SDO), a Slave Select pin and an interrupt pin. +/// +/// Note: some devices may need a pullup resister on the SDO line. +/// +/// Note also that on the RFM22B (but not the RFM23B), it is required to control the TX_ANT and +/// RX_ANT pins of the RFM22 in order to control the antenna connection properly. The RH_RF22 +/// driver is configured by default so that GPIO0 and GPIO1 outputs can +/// control TX_ANT and RX_ANT input pins respectively automatically. On RFM22, +/// you must connect GPIO0 +/// to TX_ANT and GPIO1 to RX_ANT for this automatic antenna switching to +/// occur. See setGpioReversed() for more details. These connections are not required on RFM23B. +/// +/// If you are using the Sparkfun RF22 shield, it will work with any 5V arduino without modification. +/// Connect the RFM-22 module to most Arduino's like this (Caution, Arduino Mega has different pins for SPI, +/// see below). +/// \code +/// Arduino RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 3V3----------VCC (3.3V in) +/// interrupt 0 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// For an Arduino Mega: +/// \code +/// Mega RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 3V3----------VCC (3.3V in) +/// interrupt 0 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D52----------SCK (SPI clock in) +/// MOSI pin D51----------SDI (SPI Data in) +/// MISO pin D50----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// For Chipkit Uno32. Caution: you must also ensure jumper JP4 on the Uno32 is set to RD4 +/// \code +/// Arduino RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 3V3----------VCC (3.3V in) +/// interrupt 0 pin D38----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// For Teensy 3.1 +/// \code +/// Teensy RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 3V3----------VCC (3.3V in) +/// interrupt 2 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// For an Arduino Due (the SPI pins do not come out on the Digital pins as for normal Arduino, but only +/// appear on the SPI header) +/// \code +/// Due RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 5V-----------VCC (5V in) +/// interrupt 0 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK SPI pin 3----------SCK (SPI clock in) +/// MOSI SPI pin 4----------SDI (SPI Data in) +/// MISO SPI pin 1----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// and use the default constructor: +/// RH_RF22 driver; +/// For connecting an Arduino to an RFM23BP module. Note that the antenna control pins are reversed +/// compared to the RF22. +/// \code +/// Arduino RFM-23BP +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 5V-----------VCC (5V in) +/// interrupt 0 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control receiver antenna RXON) +/// \--RXON (RX antenna control in) +/// /--GPIO1 (GPIO1 out to control transmitter antenna TXON) +/// \--TXON (TX antenna control in) +/// \endcode +/// +/// and you can then use the default constructor RH_RF22(). +/// You can override the default settings for the SS pin and the interrupt +/// in the RH_RF22 constructor if you wish to connect the slave select SS to other than the normal one for your +/// Arduino (D10 for Diecimila, Uno etc and D53 for Mega) +/// or the interrupt request to other than pin D2 (Caution, different processors have different constraints as to the +/// pins available for interrupts). +/// +/// Caution: some people have had problems with some batches of +/// RFM23BP chips burning out their nIRQ outputs for unknown +/// reasons when run at 5V. Some users assert that running RFM23BP with voltage +/// dividers at 3.3V is to be preferred. We have not tested or verified +/// either the cause or the supposed cure. +// +/// +/// If you have an Arduino Zero, you should note that you cannot use Pin 2 for the interrupt line +/// (Pin 2 is for the NMI only), instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this: +/// \code +/// // Slave Select is pin 10, interrupt is Pin 3 +/// RH_RF22 driver(10, 3); +/// \endcode +/// +/// If you have an ESP32 (we tested with the Geekworm EASY-KIT ESP32-B1 which has a ESP-WROOM-32 chip) +/// \code +/// ESP32 RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// 3V3----------VCC (3.3V in) +/// interrupt pin GPIO15-------NIRQ (interrupt request out) +/// SS pin GPIO13-------NSEL (chip select in) +/// SCK pin GPIO18-------SCK (SPI clock in) +/// MOSI pin GPIO23-------SDI (SPI Data in) +/// MISO pin GPIO19-------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// and initialise like this: +/// \code +/// RH_RF22 driver(13, 15); +/// \endcode +/// You can of course use other pins for NSEL and NIRQ if you prefer. +/// +/// To connect an STM32 F4 Discovery board to RF22 using Arduino and Arduino_STM32 +/// connect the pins like this: +/// \code +/// STM32 RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// VDD----------VCC (3.3V in) +/// interrupt pin PB1----------NIRQ (interrupt request out) +/// SS pin PB0----------NSEL (chip select in) +/// SCK pin PB3----------SCK (SPI clock in) +/// MOSI pin PB5----------SDI (SPI Data in) +/// MISO pin PB4----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// and initialise like this: +/// \code +/// RH_RF22 driver(PB0, PB1); +/// \endcode +/// You can of use other pins for NSEL and NIRQ if you prefer. +/// +/// To connect an ATTiny Mega x16 such as AtTiny 3216 etc +/// (running at 5V) etc RF22 using Arduino using Spencer Kondes +/// megaTinyCore https://github.com/SpenceKonde/megaTinyCore connect the pins like this: +/// (pin numbering based on https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/ATtiny_x16.md) +/// \code +/// AtTiny x16 RFM-22B +/// GND----------GND-\ (ground in) +/// SDN-/ (shutdown in) +/// VDD----------VCC (5V in) +/// interrupt pin PA6----------NIRQ (interrupt request out) +/// SS pin PC0----------NSEL (chip select in) +/// SCK pin PA3----------SCK (SPI clock in) +/// MOSI pin PA1----------SDI (SPI Data in) +/// MISO pin PA2----------SDO (SPI data out) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// and initialise like this: +/// \code +/// RH_RF22 driver(10, 2); +/// \endcode +/// You can of use other pins for NSEL and NIRQ if you prefer. +/// +/// +/// Note: It is possible to have 2 radios connected to one Arduino, provided each radio has its own +/// SS and interrupt line (SCK, SDI and SDO are common to both radios) +/// +/// Caution: on some Arduinos such as the Mega 2560, if you set the slave select pin to be other than the usual SS +/// pin (D53 on Mega 2560), you may need to set the usual SS pin to be an output to force the Arduino into SPI +/// master mode. +/// +/// Caution: Power supply requirements of the RF22 module may be relevant in some circumstances: +/// RF22 modules are capable of pulling 80mA+ at full power, where Arduino's 3.3V line can +/// give 50mA. You may need to make provision for alternate power supply for +/// the RF22, especially if you wish to use full transmit power, and/or you have +/// other shields demanding power. Inadequate power for the RF22 is reported to cause symptoms such as: +/// - reset's/bootups terminate with "init failed" messages +/// -random termination of communication after 5-30 packets sent/received +/// -"fake ok" state, where initialization passes fluently, but communication doesn't happen +/// -shields hang Arduino boards, especially during the flashing +/// +/// Caution: some RF22 breakout boards (such as the HAB-RFM22B-BOA HAB-RFM22B-BO) reportedly +/// have the TX_ANT and RX_ANT pre-connected to GPIO0 and GPIO1 round the wrong way. You can work with this +/// if you use setGpioReversed(). +/// +/// Caution: If you are using a bare RF22 module without IO level shifters, you may have difficulty connecting +/// to a 5V arduino. The RF22 module is 3.3V and its IO pins are 3.3V not 5V. Some Arduinos (Diecimila and +/// Uno) seem to work OK with this, and some (Mega) do not always work reliably. Your Mileage May Vary. +/// For best result, use level shifters, or use a RF22 shield or board with level shifters built in, +/// such as the Sparkfun RFM22 shield http://www.sparkfun.com/products/11018. +/// You could also use a 3.3V IO Arduino such as a Pro. +/// It is recognised that it is difficult to connect +/// the Sparkfun RFM22 shield to a Mega, since the SPI pins on the Mega are different to other Arduinos, +/// But it is possible, by bending the SPI pins (D10, D11, D12, D13) on the +/// shield out of the way before plugging it in to the Mega and jumpering the shield pins to the Mega like this: +/// \code +/// RF22 Shield Mega +/// D10 D53 +/// D13 D52 +/// D11 D51 +/// D12 D50 +/// \endcode +/// +/// \par Interrupts +/// +/// The Driver uses interrupts to react to events in the RF22 module, +/// such as the reception of a new packet, or the completion of transmission of a packet. +/// The RH_RF22 interrupt service routine reads status from and writes data +/// to the the RF22 module via the SPI interface. It is very important therefore, +/// that if you are using the RF22 library with another SPI based deviced, that you +/// disable interrupts while you transfer data to and from that other device. +/// Use cli() to disable interrupts and sei() to reenable them. +/// +/// \par SPI Interface +/// +/// The RF22 module uses the SPI bus to communicate with the Arduino. Arduino +/// IDE includes a hardware SPI class to communicate with SPI devices using +/// the SPI facilities built into the Atmel chips, over the standard designated +/// SPI pins MOSI, MISO, SCK, which are usually on Arduino pins 11, 12 and 13 +/// respectively (or 51, 50, 52 on a Mega). +/// +/// By default, the RH_RF22 Driver uses the Hardware SPI interface to +/// communicate with the RF22 module. However, if your RF22 SPI is connected to +/// the Arduino through non-standard pins, or the standard Hardware SPI +/// interface will not work for you, you can instead use a bit-banged Software +/// SPI class RHSoftwareSPI, which can be configured to work on any Arduino digital IO pins. +/// See the documentation of RHSoftwareSPI for details. +/// +/// The advantages of the Software SPI interface are that it can be used on +/// any Arduino pins, not just the usual dedicated hardware pins. The +/// disadvantage is that it is significantly slower then hardware. +/// If you observe reliable behaviour with the default hardware SPI RHHardwareSPI, but unreliable behaviour +/// with Software SPI RHSoftwareSPI, it may be due to slow CPU performance. +/// +/// Initialisation example with hardware SPI +/// \code +/// #include +/// RH_RF22 driver; +/// RHReliableDatagram manager(driver, CLIENT_ADDRESS); +/// \endcode +/// +/// Initialisation example with software SPI +/// \code +/// #include +/// #include +/// RHSoftwareSPI spi; +/// RH_RF22 driver(10, 2, spi); +/// RHReliableDatagram manager(driver, CLIENT_ADDRESS); +/// \endcode +/// +/// \par Memory +/// +/// The RH_RF22 Driver requires non-trivial amounts of memory. The sample programs all compile to +/// about 9 to 14kbytes each on Arduino, which will fit in the flash proram memory of most Arduinos. However, +/// the RAM requirements are more critical. Most sample programs above will run on Duemilanova, +/// but not on Diecimila. Even on Duemilanova, the RAM requirements are very close to the +/// available memory of 2kbytes. Therefore, you should be vary sparing with RAM use in programs that use +/// the RH_RF22 Driver on Duemilanova. +/// +/// The sample RHRouter and RHMesh programs compile to about 14kbytes, +/// and require more RAM than the others. +/// They will not run on Duemilanova or Diecimila, but will run on Arduino Mega. +/// +/// It is often hard to accurately identify when you are hitting RAM limits on Arduino. +/// The symptoms can include: +/// - Mysterious crashes and restarts +/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements) +/// - Hanging +/// - Output from Serial.print() not appearing +/// +/// With an Arduino Mega, with 8 kbytes of SRAM, there is much more RAM headroom for +/// your own elaborate programs. +/// This library is reported to work with Arduino Pro Mini, but that has not been tested by me. +/// +/// The RF22M modules use an inexpensive crystal to control the frequency synthesizer, and therfore you can expect +/// the transmitter and receiver frequencies to be subject to the usual inaccuracies of such crystals. The RF22 +/// contains an AFC circuit to compensate for differences in transmitter and receiver frequencies. +/// It does this by altering the receiver frequency during reception by up to the pull-in frequency range. +/// This RF22 library enables the AFC and by default sets the pull-in frequency range to +/// 0.05MHz, which should be sufficient to handle most situations. However, if you observe unexplained packet losses +/// or failure to operate correctly all the time it may be because your modules have a wider frequency difference, and +/// you may need to set the afcPullInRange to a different value, using setFrequency(); +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power on the RF22 and RF23 transceivers +/// with the RH_RF22::setTxPower() function. The argument can be any of the +/// RH_RF22_TXPOW_* (for RFM22) or RH_RF22_RF23B_TXPOW_* (for RFM23) values. +/// The default is RH_RF22_TXPOW_8DBM/RH_RF22_RF23B_TXPOW_1DBM . Eg: +/// \code +/// driver.setTxPower(RH_RF22_TXPOW_2DBM); +/// \endcode +/// +/// The RF23BP has higher power capability, there are +/// several power settings that are specific to the RF23BP only: +/// +/// - RH_RF22_RF23BP_TXPOW_28DBM +/// - RH_RF22_RF23BP_TXPOW_29DBM +/// - RH_RF22_RF23BP_TXPOW_30DBM +/// +/// CAUTION: the high power settings available on the RFM23BP require +/// significant power supply current. For example at +30dBm, the typical chip +/// supply current is 550mA. This will overwhelm some small CPU board power +/// regulators and USB supplies. If you use this chip at high power make sure +/// you have an adequate supply current providing full 5V to the RFM23BP (and +/// the CPU if required), otherwise you can expect strange behaviour like +/// hanging, stopping, incorrect power levels, RF power amp overheating etc. +/// You must also ensure that the RFM23BP GPIO pins are connected to the +/// antenna switch control pins like so: +//// +/// \code +/// GPIO0 <-> RXON +/// GPIO1 <-> TXON +/// \endcode +/// +/// The RF output impedance of the RFM22BP module is 50 ohms. In our +/// experiments we found that the most critical issue (besides a suitable +/// power supply) is to ensure that the antenna impedance is also near 50 +/// ohms. Connecting a simple 1/4 wavelength (ie a 17.3cm single wire) +/// directly to the antenna output will not work at full 30dBm power, +/// and will result in the transmitter hanging and/or the power amp +/// overheating. Connect a proper 50 ohm impedance transmission line or +/// antenna, and prevent RF radiation into the radio and arduino modules, +/// in order to get full, reliable power. Our tests show that a 433MHz +/// RFM23BP feeding a 50 ohm transmission line with a VHF discone antenna at +/// the end results in full power output and the power amp transistor on the +/// RFM22BP module runnning slightly warm but not hot. We recommend you use +/// the services of a competent RF engineer when trying to use this high power +/// module. +/// +/// Note: with RFM23BP, the reported maximum possible power when operating on 3.3V is 27dBm. +/// The BP version is an RFM23 with a PA +/// external to the Silicon Labs radio chip. +/// The RFM23BP only supports the top three power settings because those three +/// output levels from the RFM23 provide enough drive to the PA to make it +/// saturate. Less drive and the PA will dissipate more heat. However, those +/// three levels don't change the output power from the PA. +/// +/// We have made some actual power measurements against +/// programmed power for Sparkfun RFM22 wireless module under the following conditions: +/// - Sparkfun RFM22 wireless module, Duemilanove, USB power +/// - 10cm RG58C/U soldered direct to RFM22 module ANT and GND +/// - bnc connecteor +/// - 12dB attenuator +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// 1 -5.6 +/// 2 -3.8 +/// 5 -2.2 +/// 8 -0.6 +/// 11 1.2 +/// 14 11.6 +/// 17 14.4 +/// 20 18.0 +/// \endcode +/// (Caution: we dont claim laboratory accuracy for these measurements) +/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna. +/// +/// \par Performance +/// +/// Some simple speed performance tests have been conducted. +/// In general packet transmission rate will be limited by the modulation scheme. +/// Also, if your code does any slow operations like Serial printing it will also limit performance. +/// We disabled any printing in the tests below. +/// We tested with RH_RF22::GFSK_Rb125Fd125, which is probably the fastest scheme available. +/// We tested with a 13 octet message length, over a very short distance of 10cm. +/// +/// Transmission (no reply) tests with modulation RH_RF22::GFSK_Rb125Fd125 and a +/// 13 octet message show about 330 messages per second transmitted. +/// +/// Transmit-and-wait-for-a-reply tests with modulation RH_RF22::GFSK_Rb125Fd125 and a +/// 13 octet message (send and receive) show about 160 round trips per second. +/// +/// \par Compatibility with RF22 library +/// The RH_RF22 driver is based on our earlier RF22 library http://www.airspayce.com/mikem/arduino/RF22 +/// We have tried hard to be as compatible as possible with the earlier RF22 library, but there are some differences: +/// - Different constructor. +/// - Indexes for some modem configurations have changed (we recommend you use the symbolic names, not integer indexes). +/// +/// The major difference is that under RadioHead, you are +/// required to create 2 objects (ie RH_RF22 and a manager) instead of just one object under RF22 +/// (ie RHMesh, RHRouter, RHReliableDatagram or RHDatagram). +/// It may be sufficient or you to change for example: +/// \code +/// RF22ReliableDatagram rf22(CLIENT_ADDRESS); +/// \endcode +/// to: +/// \code +/// RH_RF22 driver; +/// RHReliableDatagram rf22(driver, CLIENT_ADDRESS); +/// \endcode +/// and any instance of RF22_MAX_MESSAGE_LEN to RH_RF22_MAX_MESSAGE_LEN +/// +/// RadioHead version 1.6 changed the way the interrupt pin number is +/// specified on Arduino and Uno32 platforms. If your code previously +/// specifed a non-default interrupt pin number in the RH_RF22 constructor, +/// you may need to review your code to specify the correct interrrupt pin +/// (and not the interrupt number as before). +class RH_RF22 : public RHSPIDriver +{ +public: + /// \brief Defines register values for a set of modem configuration registers + /// + /// Defines register values for a set of modem configuration registers + /// that can be passed to setModemConfig() + /// if none of the choices in ModemConfigChoice suit your need + /// setModemConfig() writes the register values to the appropriate RH_RF22 registers + /// to set the desired modulation type, data rate and deviation/bandwidth. + /// Suitable values for these registers can be computed using the register calculator at + /// http://www.hoperf.com/upload/rf/RF22B%2023B%2031B%2042B%2043B%20Register%20Settings_RevB1-v5.xls + typedef struct + { + uint8_t reg_1c; ///< Value for register RH_RF22_REG_1C_IF_FILTER_BANDWIDTH + uint8_t reg_1f; ///< Value for register RH_RF22_REG_1F_CLOCK_RECOVERY_GEARSHIFT_OVERRIDE + uint8_t reg_20; ///< Value for register RH_RF22_REG_20_CLOCK_RECOVERY_OVERSAMPLING_RATE + uint8_t reg_21; ///< Value for register RH_RF22_REG_21_CLOCK_RECOVERY_OFFSET2 + uint8_t reg_22; ///< Value for register RH_RF22_REG_22_CLOCK_RECOVERY_OFFSET1 + uint8_t reg_23; ///< Value for register RH_RF22_REG_23_CLOCK_RECOVERY_OFFSET0 + uint8_t reg_24; ///< Value for register RH_RF22_REG_24_CLOCK_RECOVERY_TIMING_LOOP_GAIN1 + uint8_t reg_25; ///< Value for register RH_RF22_REG_25_CLOCK_RECOVERY_TIMING_LOOP_GAIN0 + uint8_t reg_2c; ///< Value for register RH_RF22_REG_2C_OOK_COUNTER_VALUE_1 + uint8_t reg_2d; ///< Value for register RH_RF22_REG_2D_OOK_COUNTER_VALUE_2 + uint8_t reg_2e; ///< Value for register RH_RF22_REG_2E_SLICER_PEAK_HOLD + uint8_t reg_58; ///< Value for register RH_RF22_REG_58_CHARGE_PUMP_CURRENT_TRIMMING + uint8_t reg_69; ///< Value for register RH_RF22_REG_69_AGC_OVERRIDE1 + uint8_t reg_6e; ///< Value for register RH_RF22_REG_6E_TX_DATA_RATE1 + uint8_t reg_6f; ///< Value for register RH_RF22_REG_6F_TX_DATA_RATE0 + uint8_t reg_70; ///< Value for register RH_RF22_REG_70_MODULATION_CONTROL1 + uint8_t reg_71; ///< Value for register RH_RF22_REG_71_MODULATION_CONTROL2 + uint8_t reg_72; ///< Value for register RH_RF22_REG_72_FREQUENCY_DEVIATION + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common modulation types, + /// and data rates. If you need another configuration, use the register calculator. + /// and call setModemRegisters() with your desired settings. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + typedef enum + { + UnmodulatedCarrier = 0, ///< Unmodulated carrier for testing + FSK_PN9_Rb2Fd5, ///< FSK, No Manchester, Rb = 2kbs, Fd = 5kHz, PN9 random modulation for testing + + FSK_Rb2Fd5, ///< FSK, No Manchester, Rb = 2kbs, Fd = 5kHz + FSK_Rb2_4Fd36, ///< FSK, No Manchester, Rb = 2.4kbs, Fd = 36kHz + FSK_Rb4_8Fd45, ///< FSK, No Manchester, Rb = 4.8kbs, Fd = 45kHz + FSK_Rb9_6Fd45, ///< FSK, No Manchester, Rb = 9.6kbs, Fd = 45kHz + FSK_Rb19_2Fd9_6, ///< FSK, No Manchester, Rb = 19.2kbs, Fd = 9.6kHz + FSK_Rb38_4Fd19_6, ///< FSK, No Manchester, Rb = 38.4kbs, Fd = 19.6kHz + FSK_Rb57_6Fd28_8, ///< FSK, No Manchester, Rb = 57.6kbs, Fd = 28.8kHz + FSK_Rb125Fd125, ///< FSK, No Manchester, Rb = 125kbs, Fd = 125kHz + FSK_Rb_512Fd2_5, ///< FSK, No Manchester, Rb = 512bs, Fd = 2.5kHz, for POCSAG compatibility + FSK_Rb_512Fd4_5, ///< FSK, No Manchester, Rb = 512bs, Fd = 4.5kHz, for POCSAG compatibility + + GFSK_Rb2Fd5, ///< GFSK, No Manchester, Rb = 2kbs, Fd = 5kHz + GFSK_Rb2_4Fd36, ///< GFSK, No Manchester, Rb = 2.4kbs, Fd = 36kHz + GFSK_Rb4_8Fd45, ///< GFSK, No Manchester, Rb = 4.8kbs, Fd = 45kHz + GFSK_Rb9_6Fd45, ///< GFSK, No Manchester, Rb = 9.6kbs, Fd = 45kHz + GFSK_Rb19_2Fd9_6, ///< GFSK, No Manchester, Rb = 19.2kbs, Fd = 9.6kHz + GFSK_Rb38_4Fd19_6, ///< GFSK, No Manchester, Rb = 38.4kbs, Fd = 19.6kHz + GFSK_Rb57_6Fd28_8, ///< GFSK, No Manchester, Rb = 57.6kbs, Fd = 28.8kHz + GFSK_Rb125Fd125, ///< GFSK, No Manchester, Rb = 125kbs, Fd = 125kHz + + OOK_Rb1_2Bw75, ///< OOK, No Manchester, Rb = 1.2kbs, Rx Bandwidth = 75kHz + OOK_Rb2_4Bw335, ///< OOK, No Manchester, Rb = 2.4kbs, Rx Bandwidth = 335kHz + OOK_Rb4_8Bw335, ///< OOK, No Manchester, Rb = 4.8kbs, Rx Bandwidth = 335kHz + OOK_Rb9_6Bw335, ///< OOK, No Manchester, Rb = 9.6kbs, Rx Bandwidth = 335kHz + OOK_Rb19_2Bw335, ///< OOK, No Manchester, Rb = 19.2kbs, Rx Bandwidth = 335kHz + OOK_Rb38_4Bw335, ///< OOK, No Manchester, Rb = 38.4kbs, Rx Bandwidth = 335kHz + OOK_Rb40Bw335 ///< OOK, No Manchester, Rb = 40kbs, Rx Bandwidth = 335kHz + + } ModemConfigChoice; + + /// \brief Defines the available choices for CRC + /// Types of permitted CRC polynomials, to be passed to setCRCPolynomial() + /// They deliberately have the same numeric values as the crc[1:0] field of Register + /// RH_RF22_REG_30_DATA_ACCESS_CONTROL + typedef enum + { + CRC_CCITT = 0, ///< CCITT + CRC_16_IBM = 1, ///< CRC-16 (IBM) The default used by RH_RF22 driver + CRC_IEC_16 = 2, ///< IEC-16 + CRC_Biacheva = 3 ///< Biacheva + } CRCPolynomial; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RH_RF22 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the RF22 NIRQ interrupt line. + /// Defaults to pin 2, as required by sparkfun RFM22 module shields. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_RF22(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken: + /// - Initialise the slave select pin and the SPI interface library + /// - Software reset the RH_RF22 module + /// - Checks the connected RH_RF22 module is either a RH_RF22_DEVICE_TYPE_RX_TRX or a RH_RF22_DEVICE_TYPE_TX + /// - Attaches an interrupt handler + /// - Configures the RH_RF22 module + /// - Sets the frequency to 434.0 MHz + /// - Sets the modem data rate to FSK_Rb2_4Fd36 + /// \return true if everything was successful + bool init(); + + /// Issues a software reset to the + /// RH_RF22 module. Blocks for 1ms to ensure the reset is complete. + void reset(); + + /// Reads and returns the device status register RH_RF22_REG_02_DEVICE_STATUS + /// \return The value of the device status register + uint8_t statusRead(); + + /// Reads a value from the on-chip analog-digital converter + /// \param[in] adcsel Selects the ADC input to measure. One of RH_RF22_ADCSEL_*. Defaults to the + /// internal temperature sensor + /// \param[in] adcref Specifies the refernce voltage to use. One of RH_RF22_ADCREF_*. + /// Defaults to the internal bandgap voltage. + /// \param[in] adcgain Amplifier gain selection. + /// \param[in] adcoffs Amplifier offseet (0 to 15). + /// \return The analog value. 0 to 255. + uint8_t adcRead(uint8_t adcsel = RH_RF22_ADCSEL_INTERNAL_TEMPERATURE_SENSOR, + uint8_t adcref = RH_RF22_ADCREF_BANDGAP_VOLTAGE, + uint8_t adcgain = 0, + uint8_t adcoffs = 0); + + /// Reads the on-chip temperature sensor + /// \param[in] tsrange Specifies the temperature range to use. One of RH_RF22_TSRANGE_* + /// \param[in] tvoffs Specifies the temperature value offset. This is actually signed value + /// added to the measured temperature value + /// \return The measured temperature. + uint8_t temperatureRead(uint8_t tsrange = RH_RF22_TSRANGE_M64_64C, uint8_t tvoffs = 0); + + /// Reads the wakeup timer value in registers RH_RF22_REG_17_WAKEUP_TIMER_VALUE1 + /// and RH_RF22_REG_18_WAKEUP_TIMER_VALUE2 + /// \return The wakeup timer value + uint16_t wutRead(); + + /// Sets the wakeup timer period registers RH_RF22_REG_14_WAKEUP_TIMER_PERIOD1, + /// RH_RF22_REG_15_WAKEUP_TIMER_PERIOD2 and RH_RF22_R 0) + /// \return true if the message length was valid and it was correctly queued for transmit + bool send(const uint8_t* data, uint8_t len); + + /// Sets the length of the preamble + /// in 4-bit nibbles. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 8. + /// Sets the message preamble length in RH_RF22_REG_34_PREAMBLE_LENGTH + /// \param[in] nibbles Preamble length in nibbles of 4 bits each. + void setPreambleLength(uint8_t nibbles); + + /// Sets the sync words for transmit and receive in registers RH_RF22_REG_36_SYNC_WORD3 + /// to RH_RF22_REG_39_SYNC_WORD0 + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0x2d, 0xd4 }. + /// \param[in] syncWords Array of sync words, 1 to 4 octets long + /// \param[in] len Number of sync words to set, 1 to 4. + void setSyncWords(const uint8_t* syncWords, uint8_t len); + + /// Tells the receiver to accept messages with any TO address, not just messages + /// addressed to thisAddress or the broadcast address + /// \param[in] promiscuous true if you wish to receive messages with any TO address + virtual void setPromiscuous(bool promiscuous); + + /// Sets the CRC polynomial to be used to generate the CRC for both receive and transmit + /// otherwise the default of CRC_16_IBM will be used. + /// \param[in] polynomial One of RH_RF22::CRCPolynomial choices CRC_* + /// \return true if polynomial is a valid option for this radio. + bool setCRCPolynomial(CRCPolynomial polynomial); + + /// Configures GPIO pins for reversed GPIO connections to the antenna switch. + /// Normally on RF22 modules, GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit + /// and GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive. The RH_RF22 driver + /// configures the GPIO pins during init() so the antenna switch works as expected. + /// However, some RF22 modules, such as HAB-RFM22B-BOA HAB-RFM22B-BO, also Si4432 sold by Dorji.com via Tindie.com + /// have these GPIO pins reversed, so that GPIO0 is connected to RX_ANT. + /// Call this function with a true argument after init() and before transmitting + /// in order to configure the module for reversed GPIO pins. + /// \param[in] gpioReversed Set to true if your RF22 module has reversed GPIO antenna switch connections. + void setGpioReversed(bool gpioReversed = false); + + /// Returns the time in millis since the last preamble was received, and when the last + /// RSSI measurement was made. + uint32_t getLastPreambleTime(); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + +protected: + /// This is a low level function to handle the interrupts for one instance of RH_RF22. + /// Called automatically by isr*() + /// Should not need to be called. + void handleInterrupt(); + + /// Clears the receiver buffer. + /// Internal use only + void clearRxBuf(); + + /// Clears the transmitter buffer + /// Internal use only + void clearTxBuf(); + + /// Fills the transmitter buffer with the data of a mesage to be sent + /// \param[in] data Array of data bytes to be sent (1 to 255) + /// \param[in] len Number of data bytes in data (> 0) + /// \return true if the message length is valid + bool fillTxBuf(const uint8_t* data, uint8_t len); + + /// Appends the transmitter buffer with the data of a mesage to be sent + /// \param[in] data Array of data bytes to be sent (0 to 255) + /// \param[in] len Number of data bytes in data + /// \return false if the resulting message would exceed RH_RF22_MAX_MESSAGE_LEN, else true + bool appendTxBuf(const uint8_t* data, uint8_t len); + + /// Internal function to load the next fragment of + /// the current message into the transmitter FIFO + /// Internal use only + void sendNextFragment(); + + /// function to copy the next fragment from + /// the receiver FIF) into the receiver buffer + void readNextFragment(); + + /// Clears the RF22 Rx and Tx FIFOs + /// Internal use only + void resetFifos(); + + /// Clears the RF22 Rx FIFO + /// Internal use only + void resetRxFifo(); + + /// Clears the RF22 Tx FIFO + /// Internal use only + void resetTxFifo(); + + /// This function will be called by handleInterrupt() if an RF22 external interrupt occurs. + /// This can only happen if external interrupts are enabled in the RF22 + /// (which they are not by default). + /// Subclasses may override this function to get control when an RF22 external interrupt occurs. + virtual void handleExternalInterrupt(); + + /// This function will be called by handleInterrupt() if an RF22 wakeup timer interrupt occurs. + /// This can only happen if wakeup timer interrupts are enabled in theRF22 + /// (which they are not by default). + /// Subclasses may override this function to get control when an RF22 wakeup timer interrupt occurs. + virtual void handleWakeupTimerInterrupt(); + + /// Start the transmission of the contents + /// of the Tx buffer + void startTransmit(); + + /// ReStart the transmission of the contents + /// of the Tx buffer after a atransmission failure + void restartTransmit(); + + void setThisAddress(uint8_t thisAddress); + + /// Sets the radio operating mode for the case when the driver is idle (ie not + /// transmitting or receiving), allowing you to control the idle mode power requirements + /// at the expense of slower transitions to transmit and receive modes. + /// By default, the idle mode is RH_RF22_XTON, + /// but eg setIdleMode(RH_RF22_PLL) will provide a much lower + /// idle current but slower transitions. Call this function after init(). + /// \param[in] idleMode The chip operating mode to use when the driver is idle. One of the valid definitions for RH_RF22_REG_07_OPERATING_MODE + void setIdleMode(uint8_t idleMode); + +#if RH_PLATFORM == RH_PLATFORM_ESP8266 + /// \brief Method only for ESP8266 to avoid SPI calls from the ISRs + /// + /// This method is used only with ESP8266 platform and must be called from + /// the main loop. It checks if the Isr flags have been asserted and calls, + /// from the main loop, the interrupt handler methods (where SPI calls are + /// needed to process the communication). + void loopIsr(); +#endif + +protected: + /// Low level interrupt service routine for RF22 connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for RF22 connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for RF22 connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_RF22* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// The radio mode to use when mode is idle + uint8_t _idleMode; + + /// The device type reported by the RF22 + uint8_t _deviceType; + + /// The selected CRC polynomial + CRCPolynomial _polynomial; + + // These volatile members may get changed in the interrupt service routine + /// Number of octets in the receiver buffer + volatile uint8_t _bufLen; + + /// The receiver buffer + uint8_t _buf[RH_RF22_MAX_MESSAGE_LEN]; + + /// True when there is a valid message in the Rx buffer + volatile bool _rxBufValid; + + /// Index into TX buffer of the next to send chunk + volatile uint8_t _txBufSentIndex; + + /// Time in millis since the last preamble was received (and the last time the RSSI was measured) + uint32_t _lastPreambleTime; +}; + +/// @example rf22_client.pde +/// @example rf22_server.pde +/// @example rf22_cw.ino + +#endif diff --git a/RH_RF24.cpp b/RH_RF24.cpp new file mode 100644 index 0000000..3011478 --- /dev/null +++ b/RH_RF24.cpp @@ -0,0 +1,1054 @@ +// RH_RF24.cpp +// +// Copyright (C) 2011 Mike McCauley +// $Id: RH_RF24.cpp,v 1.24 2019/09/02 05:21:52 mikem Exp $ + +#include + +// Use one of the pre-built radio configuration files +// You can use other WDS generated sample configs accorinding to your needs +// or generate a custom one with WDS and include it here +// See RF24configs/README for file name encoding standard +//#include "RF24configs/radio_config_Si4464_27_434_2GFSK_5_10.h" +#include "RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h" +//#include "RF24configs/radio_config_Si4464_30_434_2GFSK_10_20.h" +//#include "RF24configs/radio_config_Si4464_30_915_2GFSK_5_10.h" +//#include "RF24configs/radio_config_Si4464_30_915_2GFSK_10_20.h" + + +// Interrupt vectors for the 3 Arduino interrupt pins +// Each interrupt can be handled by a different instance of RH_RF24, allowing you to have +// 2 or more RF24s per Arduino +RH_RF24* RH_RF24::_deviceForInterrupt[RH_RF24_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_RF24::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// This configuration data is defined in radio_config_Si4460.h +// which was generated with the Silicon Labs WDS program +PROGMEM const uint8_t RF24_CONFIGURATION_DATA[] = RADIO_CONFIGURATION_DATA_ARRAY; + +RH_RF24::RH_RF24(uint8_t slaveSelectPin, uint8_t interruptPin, uint8_t sdnPin, RHGenericSPI& spi) + : + RHSPIDriver(slaveSelectPin, spi) +{ + _interruptPin = interruptPin; + _sdnPin = sdnPin; + _idleMode = RH_RF24_DEVICE_STATE_READY; + _myInterruptIndex = 0xff; // Not allocated yet +} + +void RH_RF24::setIdleMode(uint8_t idleMode) +{ + _idleMode = idleMode; +} + +bool RH_RF24::init() +{ + if (!RHSPIDriver::init()) + return false; + + // 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 + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + + // Initialise the radio + power_on_reset(); + cmd_clear_all_interrupts(); + + // Get the device type and check it + // This also tests whether we are really connected to a device + uint8_t buf[8]; + if (!command(RH_RF24_CMD_PART_INFO, 0, 0, buf, sizeof(buf))) + return false; // SPI error? Not connected? + _deviceType = (buf[1] << 8) | buf[2]; + // Check PART to be either 0x4460, 0x4461, 0x4463, 0x4464 + if (_deviceType != 0x4460 && + _deviceType != 0x4461 && + _deviceType != 0x4463 && + _deviceType != 0x4464) + return false; // Unknown radio type, or not connected + + // Here we use a configuration generated by the Silicon Labs Wireless Development Suite + // #included above + // We override a few things later that we ned to be sure of. + configure(RF24_CONFIGURATION_DATA); + + // Add by Adrien van den Bossche 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_RF24_NUM_INTERRUPTS) + _myInterruptIndex = _interruptCount++; + else + return false; // Too many devices, not enough interrupt vectors + } + _deviceForInterrupt[_myInterruptIndex] = this; + if (_myInterruptIndex == 0) + attachInterrupt(interruptNumber, isr0, FALLING); + else if (_myInterruptIndex == 1) + attachInterrupt(interruptNumber, isr1, FALLING); + else if (_myInterruptIndex == 2) + attachInterrupt(interruptNumber, isr2, FALLING); + else + return false; // Too many devices, not enough interrupt vectors + + // Ensure we get the interrupts we need, irrespective of whats in the radio_config + uint8_t int_ctl[] = {RH_RF24_MODEM_INT_STATUS_EN | RH_RF24_PH_INT_STATUS_EN, 0xff, 0xff, 0x00 }; + set_properties(RH_RF24_PROPERTY_INT_CTL_ENABLE, int_ctl, sizeof(int_ctl)); + + // RSSI Latching should be configured in MODEM_RSSI_CONTROL in radio_config + + // PKT_TX_THRESHOLD and PKT_RX_THRESHOLD should be set to about 0x30 in radio_config + + // Configure important RH_RF24 registers + // Here we set up the standard packet format for use by the RH_RF24 library: + // We will use FIFO Mode, with automatic packet generation + // We have 2 fields: + // Field 1 contains only the (variable) length of field 2, with CRC + // Field 2 contains the variable length payload and the CRC + // Hmmm, having no CRC on field 1 and CRC on field 2 causes CRC errors when resetting after an odd + // number of packets! Anyway its prob a good thing at the cost of some airtime. + // Hmmm, enabling WHITEN stops it working! + uint8_t pkt_config1[] = { 0x00 }; + set_properties(RH_RF24_PROPERTY_PKT_CONFIG1, pkt_config1, sizeof(pkt_config1)); + + uint8_t pkt_len[] = { 0x02, 0x01, 0x00 }; + set_properties(RH_RF24_PROPERTY_PKT_LEN, pkt_len, sizeof(pkt_len)); + + uint8_t pkt_field1[] = { 0x00, 0x01, 0x00, RH_RF24_FIELD_CONFIG_CRC_START | RH_RF24_FIELD_CONFIG_SEND_CRC | RH_RF24_FIELD_CONFIG_CHECK_CRC | RH_RF24_FIELD_CONFIG_CRC_ENABLE }; + set_properties(RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8, pkt_field1, sizeof(pkt_field1)); + + uint8_t pkt_field2[] = { 0x00, sizeof(_buf), 0x00, RH_RF24_FIELD_CONFIG_CRC_START | RH_RF24_FIELD_CONFIG_SEND_CRC | RH_RF24_FIELD_CONFIG_CHECK_CRC | RH_RF24_FIELD_CONFIG_CRC_ENABLE }; + set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8, pkt_field2, sizeof(pkt_field2)); + + // Clear all other fields so they are never used, irrespective of the radio_config + uint8_t pkt_fieldn[] = { 0x00, 0x00, 0x00, 0x00 }; + set_properties(RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn)); + set_properties(RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn)); + set_properties(RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8, pkt_fieldn, sizeof(pkt_fieldn)); + + // The following can be changed later by the user if necessary. + // Set up default configuration + setCRCPolynomial(CRC_16_IBM); + uint8_t syncwords[] = { 0x2d, 0xd4 }; + setSyncWords(syncwords, sizeof(syncwords)); // Same as RF22's + // 3 would be sufficient, but this is the same as RF22's + // actualy, 4 seems to work much better for some modulations + setPreambleLength(4); + // Default freq comes from the radio config file + // About 2.4dBm on RFM24: + setTxPower(0x10); + + return true; +} + +// C++ level interrupt handler for this instance +void RH_RF24::handleInterrupt() +{ + uint8_t status[8]; + command(RH_RF24_CMD_GET_INT_STATUS, NULL, 0, status, sizeof(status)); + + // Decode and handle the interrupt bits we are interested in +// if (status[0] & RH_RF24_INT_STATUS_CHIP_INT_STATUS) + if (status[0] & RH_RF24_INT_STATUS_MODEM_INT_STATUS) + { +// if (status[4] & RH_RF24_INT_STATUS_INVALID_PREAMBLE) + if (status[4] & RH_RF24_INT_STATUS_INVALID_SYNC) + { + // After INVALID_SYNC, sometimes the radio gets into a silly state and subsequently reports it for every packet + // Need to reset the radio and clear the RX FIFO, cause sometimes theres junk there too + _mode = RHModeIdle; + clearRxFifo(); + clearBuffer(); + } + } + if (status[0] & RH_RF24_INT_STATUS_PH_INT_STATUS) + { + if (status[2] & RH_RF24_INT_STATUS_CRC_ERROR) + { + // CRC Error + // Radio automatically went to _idleMode + _mode = RHModeIdle; + _rxBad++; + + clearRxFifo(); + clearBuffer(); + } + if (status[2] & RH_RF24_INT_STATUS_PACKET_SENT) + { + _txGood++; + // Transmission does not automatically clear the tx buffer. + // Could retransmit if we wanted + // RH_RF24 configured to transition automatically to Idle after packet sent + _mode = RHModeIdle; + clearBuffer(); + } + if (status[2] & RH_RF24_INT_STATUS_PACKET_RX) + { + // A complete message has been received with good CRC + // Get the RSSI, configured to latch at sync detect in radio_config + uint8_t modem_status[6]; + command(RH_RF24_CMD_GET_MODEM_STATUS, NULL, 0, modem_status, sizeof(modem_status)); + _lastRssi = modem_status[3]; + _lastPreambleTime = millis(); + + // Save it in our buffer + readNextFragment(); + // And see if we have a valid message + validateRxBuf(); + // Radio will have transitioned automatically to the _idleMode + _mode = RHModeIdle; + } + if (status[2] & RH_RF24_INT_STATUS_TX_FIFO_ALMOST_EMPTY) + { + // TX FIFO almost empty, maybe send another chunk, if there is one + sendNextFragment(); + } + if (status[2] & RH_RF24_INT_STATUS_RX_FIFO_ALMOST_FULL) + { + // Some more data to read, get it + readNextFragment(); + } + } +} + +// Check whether the latest received message is complete and uncorrupted +void RH_RF24::validateRxBuf() +{ + // Validate headers etc + if (_bufLen >= RH_RF24_HEADER_LEN) + { + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + // Its for us + _rxGood++; + _rxBufValid = true; + } + } +} + +bool RH_RF24::clearRxFifo() +{ + uint8_t fifo_clear[] = { 0x02 }; + return command(RH_RF24_CMD_FIFO_INFO, fifo_clear, sizeof(fifo_clear)); +} + +void RH_RF24::clearBuffer() +{ + _bufLen = 0; + _txBufSentIndex = 0; + _rxBufValid = false; +} + +// These are low level functions that call the interrupt handler for the correct +// instance of RH_RF24. +// 3 interrupts allows us to have 3 different devices +void RH_INTERRUPT_ATTR RH_RF24::isr0() +{ + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF24::isr1() +{ + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF24::isr2() +{ + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +} + +bool RH_RF24::available() +{ + if (_mode == RHModeTx) + return false; + if (!_rxBufValid) + setModeRx(); // Make sure we are receiving + return _rxBufValid; +} + +bool RH_RF24::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + // CAUTION: first 4 octets of _buf contain the headers + if (buf && len && (_bufLen >= RH_RF24_HEADER_LEN)) + { + ATOMIC_BLOCK_START; + if (*len > _bufLen - RH_RF24_HEADER_LEN) + *len = _bufLen - RH_RF24_HEADER_LEN; + memcpy(buf, _buf + RH_RF24_HEADER_LEN, *len); + ATOMIC_BLOCK_END; + } + clearBuffer(); // Got the most recent message + return true; +} + +bool RH_RF24::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_RF24_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 + + // Put the payload in the FIFO + // First the length in fixed length field 1. This wont appear in the receiver fifo since + // we have turned off IN_FIFO in PKT_LEN + _buf[0] = len + RH_RF24_HEADER_LEN; + // Now the rest of the payload in variable length field 2 + // First the headers + _buf[1] = _txHeaderTo; + _buf[2] = _txHeaderFrom; + _buf[3] = _txHeaderId; + _buf[4] = _txHeaderFlags; + // Then the message + memcpy(_buf + 1 + RH_RF24_HEADER_LEN, data, len); + _bufLen = len + 1 + RH_RF24_HEADER_LEN; + _txBufSentIndex = 0; + + // Set the field 2 length to the variable payload length + uint8_t l[] = { (uint8_t)(len + RH_RF24_HEADER_LEN)}; + set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0, l, sizeof(l)); + + sendNextFragment(); + setModeTx(); + return true; +} + +// This is different to command() since we must not wait for CTS +bool RH_RF24::writeTxFifo(uint8_t *data, uint8_t len) +{ + ATOMIC_BLOCK_START; + // First send the command + digitalWrite(_slaveSelectPin, LOW); + _spi.beginTransaction(); + _spi.transfer(RH_RF24_CMD_TX_FIFO_WRITE); + // Now write any write data + while (len--) + _spi.transfer(*data++); + digitalWrite(_slaveSelectPin, HIGH); + _spi.endTransaction(); + ATOMIC_BLOCK_END; + return true; +} + +void RH_RF24::sendNextFragment() +{ + if (_txBufSentIndex < _bufLen) + { + // Some left to send? + uint8_t len = _bufLen - _txBufSentIndex; + // But dont send too much, see how much room is left + uint8_t fifo_info[2]; + command(RH_RF24_CMD_FIFO_INFO, NULL, 0, fifo_info, sizeof(fifo_info)); + // fifo_info[1] is space left in TX FIFO + if (len > fifo_info[1]) + len = fifo_info[1]; + + writeTxFifo(_buf + _txBufSentIndex, len); + _txBufSentIndex += len; + } +} + +void RH_RF24::readNextFragment() +{ + // Get the packet length from the RX FIFO length + uint8_t fifo_info[1]; + command(RH_RF24_CMD_FIFO_INFO, NULL, 0, fifo_info, sizeof(fifo_info)); + uint8_t fifo_len = fifo_info[0]; + + // Check for overflow + if ((_bufLen + fifo_len) > sizeof(_buf)) + { + // Overflow pending + _rxBad++; + setModeIdle(); + clearRxFifo(); + clearBuffer(); + return; + } + // So we have room + // Now read the fifo_len bytes from the RX FIFO + // This is different to command() since we dont wait for CTS + digitalWrite(_slaveSelectPin, LOW); + _spi.transfer(RH_RF24_CMD_RX_FIFO_READ); + uint8_t* p = _buf + _bufLen; + uint8_t l = fifo_len; + while (l--) + *p++ = _spi.transfer(0); + digitalWrite(_slaveSelectPin, HIGH); + _bufLen += fifo_len; +} + +uint8_t RH_RF24::maxMessageLength() +{ + return RH_RF24_MAX_MESSAGE_LEN; +} + +// Sets registers from a canned modem configuration structure +void RH_RF24::setModemRegisters(const ModemConfig* config) +{ +#ifdef RH_HAVE_SERIAL + Serial.println("Programming Error: setModemRegisters is obsolete. Generate custom radio config file with WDS instead"); +#endif + (void)config; // Prevent warnings +} + +// Set one of the canned Modem configs +// Returns true if its a valid choice +bool RH_RF24::setModemConfig(ModemConfigChoice index) +{ +#ifdef RH_HAVE_SERIAL + Serial.println("Programming Error: setModemRegisters is obsolete. Generate custom radio config file with WDS instead"); + (void)index; // Prevent warnings +#endif + return false; +} + +void RH_RF24::setPreambleLength(uint16_t bytes) +{ + uint8_t config[] = { (uint8_t)bytes, 0x14, 0x00, 0x00, + RH_RF24_PREAMBLE_FIRST_1 | RH_RF24_PREAMBLE_LENGTH_BYTES | RH_RF24_PREAMBLE_STANDARD_1010}; + set_properties(RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH, config, sizeof(config)); +} + +bool RH_RF24::setCRCPolynomial(CRCPolynomial polynomial) +{ + if (polynomial >= CRC_NONE && + polynomial <= CRC_Castagnoli) + { + // Caution this only has effect if CRCs are enabled + uint8_t config[] = { (uint8_t)((polynomial & RH_RF24_CRC_MASK) | RH_RF24_CRC_SEED_ALL_1S) }; + return set_properties(RH_RF24_PROPERTY_PKT_CRC_CONFIG, config, sizeof(config)); + } + else + return false; +} + +void RH_RF24::setSyncWords(const uint8_t* syncWords, uint8_t len) +{ + if (len > 4 || len < 1) + return; + uint8_t config[] = { (uint8_t)(len-1), 0, 0, 0, 0}; + memcpy(config+1, syncWords, len); + set_properties(RH_RF24_PROPERTY_SYNC_CONFIG, config, sizeof(config)); +} + +bool RH_RF24::setFrequency(float centre, float afcPullInRange) +{ + (void)afcPullInRange; // Not used + // See Si446x Data Sheet section 5.3.1 + // Also the Si446x PLL Synthesizer / VCO_CNT Calculator Rev 0.4 + uint8_t outdiv; + uint8_t band; + if (_deviceType == 0x4460 || + _deviceType == 0x4461 || + _deviceType == 0x4463) + { + // Non-continuous frequency bands + if (centre <= 1050.0 && centre >= 850.0) + outdiv = 4, band = 0; + else if (centre <= 525.0 && centre >= 425.0) + outdiv = 8, band = 2; + else if (centre <= 350.0 && centre >= 284.0) + outdiv = 12, band = 3; + else if (centre <= 175.0 && centre >= 142.0) + outdiv = 24, band = 5; + else + return false; + } + else + { + // 0x4464 + // Continuous frequency bands + if (centre <= 960.0 && centre >= 675.0) + outdiv = 4, band = 1; + else if (centre < 675.0 && centre >= 450.0) + outdiv = 6, band = 2; + else if (centre < 450.0 && centre >= 338.0) + outdiv = 8, band = 3; + else if (centre < 338.0 && centre >= 225.0) + outdiv = 12, band = 4; + else if (centre < 225.0 && centre >= 169.0) + outdiv = 16, band = 4; + else if (centre < 169.0 && centre >= 119.0) + outdiv = 24, band = 5; + else + return false; + } + + // Set the MODEM_CLKGEN_BAND (not documented) + uint8_t modem_clkgen[] = { (uint8_t)(band + 8) }; + if (!set_properties(RH_RF24_PROPERTY_MODEM_CLKGEN_BAND, modem_clkgen, sizeof(modem_clkgen))) + return false; + + centre *= 1000000.0; // Convert to Hz + + // Now generate the RF frequency properties + // Need the Xtal/XO freq from the radio_config file: + uint32_t xtal_frequency = RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ; + unsigned long f_pfd = 2 * xtal_frequency / outdiv; + unsigned int n = ((unsigned int)(centre / f_pfd)) - 1; + float ratio = centre / (float)f_pfd; + float rest = ratio - (float)n; + unsigned long m = (unsigned long)(rest * 524288UL); + unsigned int m2 = m / 0x10000; + unsigned int m1 = (m - m2 * 0x10000) / 0x100; + unsigned int m0 = (m - m2 * 0x10000 - m1 * 0x100); + + // PROP_FREQ_CONTROL_GROUP + uint8_t freq_control[] = { (uint8_t)n, (uint8_t)m2, (uint8_t)m1, (uint8_t)m0 }; + return set_properties(RH_RF24_PROPERTY_FREQ_CONTROL_INTE, freq_control, sizeof(freq_control)); +} + +void RH_RF24::setModeIdle() +{ + if (_mode != RHModeIdle) + { + // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch + uint8_t config[] = { RH_RF24_GPIO_HIGH, RH_RF24_GPIO_HIGH }; + command(RH_RF24_CMD_GPIO_PIN_CFG, config, sizeof(config)); + + uint8_t state[] = { _idleMode }; + command(RH_RF24_CMD_CHANGE_STATE, state, sizeof(state)); + _mode = RHModeIdle; + } +} + +bool RH_RF24::sleep() +{ + if (_mode != RHModeSleep) + { + // This will change to SLEEP or STANDBY, depending on the value of GLOBAL_CLK_CFG:CLK_32K_SEL. + // which default to 0, eg STANDBY + uint8_t state[] = { RH_RF24_DEVICE_STATE_SLEEP }; + command(RH_RF24_CMD_CHANGE_STATE, state, sizeof(state)); + + _mode = RHModeSleep; + } + return true; +} + +void RH_RF24::setModeRx() +{ + if (_mode != RHModeRx) + { + // CAUTION: we cant clear the rx buffers here, else we set up a race condition + // with the _rxBufValid test in available() + + // Tell the receiver the max data length we will accept (a TX may have changed it) + uint8_t l[] = { sizeof(_buf) }; + set_properties(RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0, l, sizeof(l)); + + // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch + uint8_t gpio_config[] = { RH_RF24_GPIO_HIGH, RH_RF24_GPIO_LOW }; + command(RH_RF24_CMD_GPIO_PIN_CFG, gpio_config, sizeof(gpio_config)); + + uint8_t rx_config[] = { 0x00, RH_RF24_CONDITION_RX_START_IMMEDIATE, 0x00, 0x00, _idleMode, _idleMode, _idleMode}; + command(RH_RF24_CMD_START_RX, rx_config, sizeof(rx_config)); + _mode = RHModeRx; + } +} + +void RH_RF24::setModeTx() +{ + if (_mode != RHModeTx) + { + // Set the antenna switch pins using the GPIO, assuming we have an RFM module with antenna switch + uint8_t config[] = { RH_RF24_GPIO_LOW, RH_RF24_GPIO_HIGH }; + command(RH_RF24_CMD_GPIO_PIN_CFG, config, sizeof(config)); + + uint8_t tx_params[] = { 0x00, + (uint8_t)((_idleMode << 4) | RH_RF24_CONDITION_RETRANSMIT_NO | RH_RF24_CONDITION_START_IMMEDIATE)}; + command(RH_RF24_CMD_START_TX, tx_params, sizeof(tx_params)); + _mode = RHModeTx; + } +} + +void RH_RF24::setTxPower(uint8_t power) +{ + uint8_t pa_bias_clkduty = 0; + // These calculations valid for advertised power from Si chips at Vcc = 3.3V + // you may get lower power from RFM modules, depending on Vcc voltage, antenna etc + if (_deviceType == 0x4460) + { + // 0x4f = 13dBm + pa_bias_clkduty = 0xc0; + if (power > 0x4f) + power = 0x4f; + } + else if (_deviceType == 0x4461) + { + // 0x7f = 16dBm + pa_bias_clkduty = 0xc0; + if (power > 0x7f) + power = 0x7f; + } + else if (_deviceType == 0x4463 || _deviceType == 0x4464 ) + { + // 0x7f = 20dBm + pa_bias_clkduty = 0x00; // Per WDS suggestion + if (power > 0x7f) + power = 0x7f; + } + uint8_t power_properties[] = {0x08, 0x00, 0x00 }; // PA_MODE from WDS sugggestions (why?) + power_properties[1] = power; + power_properties[2] = pa_bias_clkduty; + set_properties(RH_RF24_PROPERTY_PA_MODE, power_properties, sizeof(power_properties)); +} + +// Caution: There was a bug in A1 hardware that will not handle 1 byte commands. +bool RH_RF24::command(uint8_t cmd, const uint8_t* write_buf, uint8_t write_len, uint8_t* read_buf, uint8_t read_len) +{ + bool done = false; + + ATOMIC_BLOCK_START; + // First send the command + digitalWrite(_slaveSelectPin, LOW); + _spi.transfer(cmd); + + // Now write any write data + if (write_buf && write_len) + { + while (write_len--) + _spi.transfer(*write_buf++); + } + // Sigh, the RFM26 at least has problems if we deselect too quickly :-( + // Innocuous timewaster: + digitalWrite(_slaveSelectPin, LOW); + // And finalise the command + digitalWrite(_slaveSelectPin, HIGH); + + uint16_t count; // Number of times we have tried to get CTS + for (count = 0; !done && count < RH_RF24_CTS_RETRIES; count++) + { + // Wait for the CTS + digitalWrite(_slaveSelectPin, LOW); + + _spi.transfer(RH_RF24_CMD_READ_BUF); + if (_spi.transfer(0) == RH_RF24_REPLY_CTS) + { + // Now read any expected reply data + if (read_buf && read_len) + { + while (read_len--) + *read_buf++ = _spi.transfer(0); + } + done = true; + } + // Sigh, the RFM26 at least has problems if we deselect too quickly :-( + // Innocuous timewaster: + digitalWrite(_slaveSelectPin, LOW); + // Finalise the read + digitalWrite(_slaveSelectPin, HIGH); + } + ATOMIC_BLOCK_END; + return done; // False if too many attempts at CTS +} + +bool RH_RF24::configure(const uint8_t* commands) +{ + // Command strings are constructed in radio_config_Si4460.h + // Each command starts with a count of the bytes in that command: + // + uint8_t next_cmd_len; + + while (memcpy_P(&next_cmd_len, commands, 1), next_cmd_len > 0) + { + uint8_t buf[20]; // As least big as the biggest permitted command/property list of 15 + memcpy_P(buf, commands+1, next_cmd_len); + command(buf[0], buf+1, next_cmd_len - 1); + commands += (next_cmd_len + 1); + } + return true; +} + +void RH_RF24::power_on_reset() +{ + // Sigh: its necessary to control the SDN pin to reset this ship. + // Tying it to GND does not produce reliable startups + // Per Si4464 Data Sheet 3.3.2 + digitalWrite(_sdnPin, HIGH); // So we dont get a glitch after setting pinMode OUTPUT + pinMode(_sdnPin, OUTPUT); + delay(10); + digitalWrite(_sdnPin, LOW); + delay(10); +} + +bool RH_RF24::cmd_clear_all_interrupts() +{ + uint8_t write_buf[] = { 0x00, 0x00, 0x00 }; + return command(RH_RF24_CMD_GET_INT_STATUS, write_buf, sizeof(write_buf)); +} + +bool RH_RF24::set_properties(uint16_t firstProperty, const uint8_t* values, uint8_t count) +{ + uint8_t buf[15]; + + buf[0] = firstProperty >> 8; // GROUP + buf[1] = count; // NUM_PROPS + buf[2] = firstProperty & 0xff; // START_PROP + uint8_t i; + for (i = 0; i < 12 && i < count; i++) + buf[3 + i] = values[i]; // DATAn + return command(RH_RF24_CMD_SET_PROPERTY, buf, count + 3); +} + +bool RH_RF24::get_properties(uint16_t firstProperty, uint8_t* values, uint8_t count) +{ + if (count > 16) + count = 16; + uint8_t buf[3]; + buf[0] = firstProperty >> 8; // GROUP + buf[1] = count; // NUM_PROPS + buf[2] = firstProperty & 0xff; // START_PROP + return command(RH_RF24_CMD_GET_PROPERTY, buf, sizeof(buf), values, count); +} + +float RH_RF24::get_temperature() +{ + uint8_t write_buf[] = { 0x10 }; + uint8_t read_buf[8]; + // Takes nearly 4ms + command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf)); + uint16_t temp_adc = (read_buf[4] << 8) | read_buf[5]; + return ((800 + read_buf[6]) / 4096.0) * temp_adc - ((read_buf[7] / 2) + 256); +} + +float RH_RF24::get_battery_voltage() +{ + uint8_t write_buf[] = { 0x08 }; + uint8_t read_buf[8]; + // Takes nearly 4ms + command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf)); + uint16_t battery_adc = (read_buf[2] << 8) | read_buf[3]; + return 3.0 * battery_adc / 1280; +} + +float RH_RF24::get_gpio_voltage(uint8_t gpio) +{ + uint8_t write_buf[] = { 0x04 }; + uint8_t read_buf[8]; + write_buf[0] |= (gpio & 0x3); + // Takes nearly 4ms + command(RH_RF24_CMD_GET_ADC_READING, write_buf, sizeof(write_buf), read_buf, sizeof(read_buf)); + uint16_t gpio_adc = (read_buf[0] << 8) | read_buf[1]; + return 3.0 * gpio_adc / 1280; +} + +uint8_t RH_RF24::frr_read(uint8_t reg) +{ + uint8_t ret=0; + + // Do not wait for CTS + ATOMIC_BLOCK_START; + // First send the command + digitalWrite(_slaveSelectPin, LOW); + _spi.transfer(RH_RF24_PROPERTY_FRR_CTL_A_MODE + reg); + // Get the fast response + ret = _spi.transfer(0); + digitalWrite(_slaveSelectPin, HIGH); + ATOMIC_BLOCK_END; + return ret; +} + +// List of command replies to be printed by prinRegisters() +PROGMEM static const RH_RF24::CommandInfo commands[] = +{ + { RH_RF24_CMD_PART_INFO, 8 }, + { RH_RF24_CMD_FUNC_INFO, 6 }, + { RH_RF24_CMD_GPIO_PIN_CFG, 7 }, + { RH_RF24_CMD_FIFO_INFO, 2 }, + { RH_RF24_CMD_PACKET_INFO, 2 }, + { RH_RF24_CMD_GET_INT_STATUS, 8 }, + { RH_RF24_CMD_GET_PH_STATUS, 2 }, + { RH_RF24_CMD_GET_MODEM_STATUS, 8 }, + { RH_RF24_CMD_GET_CHIP_STATUS, 3 }, + { RH_RF24_CMD_REQUEST_DEVICE_STATE, 2 }, +}; +#define NUM_COMMAND_INFO (sizeof(commands)/sizeof(CommandInfo)) + +// List of properties to be printed by printRegisters() +PROGMEM static const uint16_t properties[] = +{ + RH_RF24_PROPERTY_GLOBAL_XO_TUNE, + RH_RF24_PROPERTY_GLOBAL_CLK_CFG, + RH_RF24_PROPERTY_GLOBAL_LOW_BATT_THRESH, + RH_RF24_PROPERTY_GLOBAL_CONFIG, + RH_RF24_PROPERTY_GLOBAL_WUT_CONFIG, + RH_RF24_PROPERTY_GLOBAL_WUT_M_15_8, + RH_RF24_PROPERTY_GLOBAL_WUT_M_7_0, + RH_RF24_PROPERTY_GLOBAL_WUT_R, + RH_RF24_PROPERTY_GLOBAL_WUT_LDC, + RH_RF24_PROPERTY_INT_CTL_ENABLE, + RH_RF24_PROPERTY_INT_CTL_PH_ENABLE, + RH_RF24_PROPERTY_INT_CTL_MODEM_ENABLE, + RH_RF24_PROPERTY_INT_CTL_CHIP_ENABLE, + RH_RF24_PROPERTY_FRR_CTL_A_MODE, + RH_RF24_PROPERTY_FRR_CTL_B_MODE, + RH_RF24_PROPERTY_FRR_CTL_C_MODE, + RH_RF24_PROPERTY_FRR_CTL_D_MODE, + RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH, + RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_1, + RH_RF24_PROPERTY_PREAMBLE_CONFIG_NSTD, + RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_2, + RH_RF24_PROPERTY_PREAMBLE_CONFIG, + RH_RF24_PROPERTY_PREAMBLE_PATTERN_31_24, + RH_RF24_PROPERTY_PREAMBLE_PATTERN_23_16, + RH_RF24_PROPERTY_PREAMBLE_PATTERN_15_8, + RH_RF24_PROPERTY_PREAMBLE_PATTERN_7_0, + RH_RF24_PROPERTY_SYNC_CONFIG, + RH_RF24_PROPERTY_SYNC_BITS_31_24, + RH_RF24_PROPERTY_SYNC_BITS_23_16, + RH_RF24_PROPERTY_SYNC_BITS_15_8, + RH_RF24_PROPERTY_SYNC_BITS_7_0, + RH_RF24_PROPERTY_PKT_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_CONFIG1, + RH_RF24_PROPERTY_PKT_LEN, + RH_RF24_PROPERTY_PKT_LEN_FIELD_SOURCE, + RH_RF24_PROPERTY_PKT_LEN_ADJUST, + RH_RF24_PROPERTY_PKT_TX_THRESHOLD, + RH_RF24_PROPERTY_PKT_RX_THRESHOLD, + RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_1_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_2_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_3_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_4_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG, + RH_RF24_PROPERTY_PKT_FIELD_5_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_RX_FIELD_1_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_RX_FIELD_2_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_RX_FIELD_3_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_RX_FIELD_4_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_12_8, + RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_7_0, + RH_RF24_PROPERTY_PKT_RX_FIELD_5_CONFIG, + RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG, + RH_RF24_PROPERTY_MODEM_MOD_TYPE, + RH_RF24_PROPERTY_MODEM_MAP_CONTROL, + RH_RF24_PROPERTY_MODEM_DSM_CTRL, + RH_RF24_PROPERTY_MODEM_DATA_RATE_2, + RH_RF24_PROPERTY_MODEM_DATA_RATE_1, + RH_RF24_PROPERTY_MODEM_DATA_RATE_0, + RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3, + RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_2, + RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_1, + RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_0, + RH_RF24_PROPERTY_MODEM_FREQ_DEV_2, + RH_RF24_PROPERTY_MODEM_FREQ_DEV_1, + RH_RF24_PROPERTY_MODEM_FREQ_DEV_0, + RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY, + RH_RF24_PROPERTY_MODEM_MDM_CTRL, + RH_RF24_PROPERTY_MODEM_IF_CONTROL, + RH_RF24_PROPERTY_MODEM_IF_FREQ_2, + RH_RF24_PROPERTY_MODEM_IF_FREQ_1, + RH_RF24_PROPERTY_MODEM_IF_FREQ_0, + RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1, + RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0, + RH_RF24_PROPERTY_MODEM_BCR_OSR_1, + RH_RF24_PROPERTY_MODEM_BCR_OSR_0, + RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2, + RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1, + RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0, + RH_RF24_PROPERTY_MODEM_BCR_GAIN_1, + RH_RF24_PROPERTY_MODEM_BCR_GAIN_0, + RH_RF24_PROPERTY_MODEM_BCR_GEAR, + RH_RF24_PROPERTY_MODEM_BCR_MISC1, + RH_RF24_PROPERTY_MODEM_AFC_GEAR, + RH_RF24_PROPERTY_MODEM_AFC_WAIT, + RH_RF24_PROPERTY_MODEM_AFC_GAIN_1, + RH_RF24_PROPERTY_MODEM_AFC_GAIN_0, + RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1, + RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0, + RH_RF24_PROPERTY_MODEM_AFC_MISC, + RH_RF24_PROPERTY_MODEM_AGC_CONTROL, + RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE, + RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY, + RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY, + RH_RF24_PROPERTY_MODEM_FSK4_GAIN1, + RH_RF24_PROPERTY_MODEM_FSK4_GAIN0, + RH_RF24_PROPERTY_MODEM_FSK4_TH1, + RH_RF24_PROPERTY_MODEM_FSK4_TH0, + RH_RF24_PROPERTY_MODEM_FSK4_MAP, + RH_RF24_PROPERTY_MODEM_OOK_PDTC, + RH_RF24_PROPERTY_MODEM_OOK_CNT1, + RH_RF24_PROPERTY_MODEM_OOK_MISC, + RH_RF24_PROPERTY_MODEM_RAW_SEARCH, + RH_RF24_PROPERTY_MODEM_RAW_CONTROL, + RH_RF24_PROPERTY_MODEM_RAW_EYE_1, + RH_RF24_PROPERTY_MODEM_RAW_EYE_0, + RH_RF24_PROPERTY_MODEM_ANT_DIV_MODE, + RH_RF24_PROPERTY_MODEM_ANT_DIV_CONTROL, + RH_RF24_PROPERTY_MODEM_RSSI_THRESH, + RH_RF24_PROPERTY_MODEM_RSSI_JUMP_THRESH, + RH_RF24_PROPERTY_MODEM_RSSI_CONTROL, + RH_RF24_PROPERTY_MODEM_RSSI_CONTROL2, + RH_RF24_PROPERTY_MODEM_RSSI_COMP, + RH_RF24_PROPERTY_MODEM_ANT_DIV_CONT, + RH_RF24_PROPERTY_MODEM_CLKGEN_BAND, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2, + RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2, + RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3, + RH_RF24_PROPERTY_PA_MODE, + RH_RF24_PROPERTY_PA_PWR_LVL, + RH_RF24_PROPERTY_PA_BIAS_CLKDUTY, + RH_RF24_PROPERTY_PA_TC, + RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF, + RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT, + RH_RF24_PROPERTY_SYNTH_VCO_KV, + RH_RF24_PROPERTY_SYNTH_LPFILT3, + RH_RF24_PROPERTY_SYNTH_LPFILT2, + RH_RF24_PROPERTY_SYNTH_LPFILT1, + RH_RF24_PROPERTY_SYNTH_LPFILT0, + RH_RF24_PROPERTY_MATCH_VALUE_1, + RH_RF24_PROPERTY_MATCH_MASK_1, + RH_RF24_PROPERTY_MATCH_CTRL_1, + RH_RF24_PROPERTY_MATCH_VALUE_2, + RH_RF24_PROPERTY_MATCH_MASK_2, + RH_RF24_PROPERTY_MATCH_CTRL_2, + RH_RF24_PROPERTY_MATCH_VALUE_3, + RH_RF24_PROPERTY_MATCH_MASK_3, + RH_RF24_PROPERTY_MATCH_CTRL_3, + RH_RF24_PROPERTY_MATCH_VALUE_4, + RH_RF24_PROPERTY_MATCH_MASK_4, + RH_RF24_PROPERTY_MATCH_CTRL_4, + RH_RF24_PROPERTY_FREQ_CONTROL_INTE, + RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_2, + RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_1, + RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_0, + RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_1, + RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_0, + RH_RF24_PROPERTY_FREQ_CONTROL_VCOCNT_RX_ADJ, + RH_RF24_PROPERTY_RX_HOP_CONTROL, + RH_RF24_PROPERTY_RX_HOP_TABLE_SIZE, + RH_RF24_PROPERTY_RX_HOP_TABLE_ENTRY_0, +}; +#define NUM_PROPERTIES (sizeof(properties)/sizeof(uint16_t)) + +bool RH_RF24::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + uint8_t i; + // First print the commands that return interesting data + for (i = 0; i < NUM_COMMAND_INFO; i++) + { + CommandInfo cmd; + memcpy_P(&cmd, &commands[i], sizeof(cmd)); + uint8_t buf[10]; // Big enough for the biggest command reply + if (command(cmd.cmd, NULL, 0, buf, cmd.replyLen)) + { + // Print the results: + Serial.print("cmd: "); + Serial.print(cmd.cmd, HEX); + Serial.print(" : "); + uint8_t j; + for (j = 0; j < cmd.replyLen; j++) + { + Serial.print(buf[j], HEX); + Serial.print(" "); + } + Serial.println(""); + } + } + + // Now print the properties + for (i = 0; i < NUM_PROPERTIES; i++) + { + uint16_t prop; + memcpy_P(&prop, &properties[i], sizeof(prop)); + uint8_t result; + get_properties(prop, &result, 1); + Serial.print("prop: "); + Serial.print(prop, HEX); + Serial.print(": "); + Serial.print(result, HEX); + Serial.println(""); + } +#endif + return true; +} diff --git a/RH_RF24.h b/RH_RF24.h new file mode 100644 index 0000000..351e630 --- /dev/null +++ b/RH_RF24.h @@ -0,0 +1,1164 @@ +// RH_RF24.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_RF24.h,v 1.18 2017/07/25 05:26:50 mikem Exp $ +// +// Supports RF24/RF26 and RFM24/RFM26 modules in FIFO mode +// also Si4464/63/62/61/60-A1 +// Si4063 is the same but Tx only +// +// Per http://www.hoperf.cn/upload/rf/RFM24.pdf +// and http://www.hoperf.cn/upload/rf/RFM26.pdf +// Sigh: the HopeRF documentation is utter rubbish: full of errors and incomplete. The Si446x docs are better: +// http://www.silabs.com/Support%20Documents/TechnicalDocs/Si4464-63-61-60.pdf +// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN626.pdf +// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN627.pdf +// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN647.pdf +// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN633.pdf +// http://www.silabs.com/Support%20Documents/TechnicalDocs/AN736.pdf +// http://nicerf.com/manage/upfile/indexbanner/635231050196868750.pdf (API description) +// http://www.silabs.com/Support%20Documents/Software/Si446x%20RX_HOP%20PLL%20Calculator.xlsx +#ifndef RH_RF24_h +#define RH_RF24_h + +#include +#include + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_RF24_NUM_INTERRUPTS 3 + +// Maximum payload length the RF24 can support, limited by our 1 octet message length +#define RH_RF24_MAX_PAYLOAD_LEN 255 + +// The length of the headers we add. +// The headers are inside the RF24's payload +#define RH_RF24_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this driver. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for message length 4 bytes of address and header and payload to be included in payload size limit. +#ifndef RH_RF24_MAX_MESSAGE_LEN +#define RH_RF24_MAX_MESSAGE_LEN (RH_RF24_MAX_PAYLOAD_LEN - RH_RF24_HEADER_LEN - 1) +#endif + +// Max number of times we will try to read CTS from the radio +#define RH_RF24_CTS_RETRIES 2500 + +// RF24/RF26 API commands from table 10 +// also Si446X API DESCRIPTIONS table 1 +#define RH_RF24_CMD_NOP 0x00 +#define RH_RF24_CMD_PART_INFO 0x01 +#define RH_RF24_CMD_POWER_UP 0x02 +#define RH_RF24_CMD_PATCH_IMAGE 0x04 +#define RH_RF24_CMD_FUNC_INFO 0x10 +#define RH_RF24_CMD_SET_PROPERTY 0x11 +#define RH_RF24_CMD_GET_PROPERTY 0x12 +#define RH_RF24_CMD_GPIO_PIN_CFG 0x13 +#define RH_RF24_CMD_GET_ADC_READING 0x14 +#define RH_RF24_CMD_FIFO_INFO 0x15 +#define RH_RF24_CMD_PACKET_INFO 0x16 +#define RH_RF24_CMD_IRCAL 0x17 +#define RH_RF24_CMD_PROTOCOL_CFG 0x18 +#define RH_RF24_CMD_GET_INT_STATUS 0x20 +#define RH_RF24_CMD_GET_PH_STATUS 0x21 +#define RH_RF24_CMD_GET_MODEM_STATUS 0x22 +#define RH_RF24_CMD_GET_CHIP_STATUS 0x23 +#define RH_RF24_CMD_START_TX 0x31 +#define RH_RF24_CMD_START_RX 0x32 +#define RH_RF24_CMD_REQUEST_DEVICE_STATE 0x33 +#define RH_RF24_CMD_CHANGE_STATE 0x34 +#define RH_RF24_CMD_RX_HOP 0x36 +#define RH_RF24_CMD_READ_BUF 0x44 +#define RH_RF24_CMD_FAST_RESPONSE_A 0x50 +#define RH_RF24_CMD_FAST_RESPONSE_B 0x51 +#define RH_RF24_CMD_FAST_RESPONSE_C 0x53 +#define RH_RF24_CMD_FAST_RESPONSE_D 0x57 +#define RH_RF24_CMD_TX_FIFO_WRITE 0x66 +#define RH_RF24_CMD_RX_FIFO_READ 0x77 + +// The Clear To Send signal from the radio +#define RH_RF24_REPLY_CTS 0xff + +//#define RH_RF24_CMD_START_TX 0x31 +#define RH_RF24_CONDITION_TX_COMPLETE_STATE 0xf0 +#define RH_RF24_CONDITION_RETRANSMIT_NO 0x00 +#define RH_RF24_CONDITION_RETRANSMIT_YES 0x04 +#define RH_RF24_CONDITION_START_IMMEDIATE 0x00 +#define RH_RF24_CONDITION_START_AFTER_WUT 0x01 + +//#define RH_RF24_CMD_START_RX 0x32 +#define RH_RF24_CONDITION_RX_START_IMMEDIATE 0x00 + +//#define RH_RF24_CMD_REQUEST_DEVICE_STATE 0x33 +#define RH_RF24_DEVICE_STATE_NO_CHANGE 0x00 +#define RH_RF24_DEVICE_STATE_SLEEP 0x01 +#define RH_RF24_DEVICE_STATE_SPI_ACTIVE 0x02 +#define RH_RF24_DEVICE_STATE_READY 0x03 +#define RH_RF24_DEVICE_STATE_ALSO_READY 0x04 +#define RH_RF24_DEVICE_STATE_TUNE_TX 0x05 +#define RH_RF24_DEVICE_STATE_TUNE_RX 0x06 +#define RH_RF24_DEVICE_STATE_TX 0x07 +#define RH_RF24_DEVICE_STATE_RX 0x08 + +// Properties for API Description AN625 Section 2.2 +#define RH_RF24_PROPERTY_GLOBAL_XO_TUNE 0x0000 +#define RH_RF24_PROPERTY_GLOBAL_CLK_CFG 0x0001 +#define RH_RF24_PROPERTY_GLOBAL_LOW_BATT_THRESH 0x0002 +#define RH_RF24_PROPERTY_GLOBAL_CONFIG 0x0003 +#define RH_RF24_PROPERTY_GLOBAL_WUT_CONFIG 0x0004 +#define RH_RF24_PROPERTY_GLOBAL_WUT_M_15_8 0x0005 +#define RH_RF24_PROPERTY_GLOBAL_WUT_M_7_0 0x0006 +#define RH_RF24_PROPERTY_GLOBAL_WUT_R 0x0007 +#define RH_RF24_PROPERTY_GLOBAL_WUT_LDC 0x0008 +#define RH_RF24_PROPERTY_INT_CTL_ENABLE 0x0100 +#define RH_RF24_PROPERTY_INT_CTL_PH_ENABLE 0x0101 +#define RH_RF24_PROPERTY_INT_CTL_MODEM_ENABLE 0x0102 +#define RH_RF24_PROPERTY_INT_CTL_CHIP_ENABLE 0x0103 +#define RH_RF24_PROPERTY_FRR_CTL_A_MODE 0x0200 +#define RH_RF24_PROPERTY_FRR_CTL_B_MODE 0x0201 +#define RH_RF24_PROPERTY_FRR_CTL_C_MODE 0x0202 +#define RH_RF24_PROPERTY_FRR_CTL_D_MODE 0x0203 +#define RH_RF24_PROPERTY_PREAMBLE_TX_LENGTH 0x1000 +#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_1 0x1001 +#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_NSTD 0x1002 +#define RH_RF24_PROPERTY_PREAMBLE_CONFIG_STD_2 0x1003 +#define RH_RF24_PROPERTY_PREAMBLE_CONFIG 0x1004 +#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_31_24 0x1005 +#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_23_16 0x1006 +#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_15_8 0x1007 +#define RH_RF24_PROPERTY_PREAMBLE_PATTERN_7_0 0x1008 +#define RH_RF24_PROPERTY_SYNC_CONFIG 0x1100 +#define RH_RF24_PROPERTY_SYNC_BITS_31_24 0x1101 +#define RH_RF24_PROPERTY_SYNC_BITS_23_16 0x1102 +#define RH_RF24_PROPERTY_SYNC_BITS_15_8 0x1103 +#define RH_RF24_PROPERTY_SYNC_BITS_7_0 0x1104 +#define RH_RF24_PROPERTY_PKT_CRC_CONFIG 0x1200 +#define RH_RF24_PROPERTY_PKT_CONFIG1 0x1206 +#define RH_RF24_PROPERTY_PKT_LEN 0x1208 +#define RH_RF24_PROPERTY_PKT_LEN_FIELD_SOURCE 0x1209 +#define RH_RF24_PROPERTY_PKT_LEN_ADJUST 0x120a +#define RH_RF24_PROPERTY_PKT_TX_THRESHOLD 0x120b +#define RH_RF24_PROPERTY_PKT_RX_THRESHOLD 0x120c +#define RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_12_8 0x120d +#define RH_RF24_PROPERTY_PKT_FIELD_1_LENGTH_7_0 0x120e +#define RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG 0x120f +#define RH_RF24_PROPERTY_PKT_FIELD_1_CRC_CONFIG 0x1210 +#define RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_12_8 0x1211 +#define RH_RF24_PROPERTY_PKT_FIELD_2_LENGTH_7_0 0x1212 +#define RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG 0x1213 +#define RH_RF24_PROPERTY_PKT_FIELD_2_CRC_CONFIG 0x1214 +#define RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_12_8 0x1215 +#define RH_RF24_PROPERTY_PKT_FIELD_3_LENGTH_7_0 0x1216 +#define RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG 0x1217 +#define RH_RF24_PROPERTY_PKT_FIELD_3_CRC_CONFIG 0x1218 +#define RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_12_8 0x1219 +#define RH_RF24_PROPERTY_PKT_FIELD_4_LENGTH_7_0 0x121a +#define RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG 0x121b +#define RH_RF24_PROPERTY_PKT_FIELD_4_CRC_CONFIG 0x121c +#define RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_12_8 0x121d +#define RH_RF24_PROPERTY_PKT_FIELD_5_LENGTH_7_0 0x121e +#define RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG 0x121f +#define RH_RF24_PROPERTY_PKT_FIELD_5_CRC_CONFIG 0x1220 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_12_8 0x1221 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_LENGTH_7_0 0x1222 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CONFIG 0x1223 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG 0x1224 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_12_8 0x1225 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_LENGTH_7_0 0x1226 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CONFIG 0x1227 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG 0x1228 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_12_8 0x1229 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_LENGTH_7_0 0x122a +#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CONFIG 0x122b +#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG 0x122c +#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_12_8 0x122d +#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_LENGTH_7_0 0x122e +#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CONFIG 0x122f +#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG 0x1230 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_12_8 0x1231 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_LENGTH_7_0 0x1232 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CONFIG 0x1233 +#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG 0x1234 +#define RH_RF24_PROPERTY_MODEM_MOD_TYPE 0x2000 +#define RH_RF24_PROPERTY_MODEM_MAP_CONTROL 0x2001 +#define RH_RF24_PROPERTY_MODEM_DSM_CTRL 0x2002 +#define RH_RF24_PROPERTY_MODEM_DATA_RATE_2 0x2003 +#define RH_RF24_PROPERTY_MODEM_DATA_RATE_1 0x2004 +#define RH_RF24_PROPERTY_MODEM_DATA_RATE_0 0x2005 +#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3 0x2006 +#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_2 0x2007 +#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_1 0x2008 +#define RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_0 0x2009 +#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_2 0x200a +#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_1 0x200b +#define RH_RF24_PROPERTY_MODEM_FREQ_DEV_0 0x200c +#define RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY 0x2018 +#define RH_RF24_PROPERTY_MODEM_MDM_CTRL 0x2019 +#define RH_RF24_PROPERTY_MODEM_IF_CONTROL 0x201a +#define RH_RF24_PROPERTY_MODEM_IF_FREQ_2 0x201b +#define RH_RF24_PROPERTY_MODEM_IF_FREQ_1 0x201c +#define RH_RF24_PROPERTY_MODEM_IF_FREQ_0 0x201d +#define RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1 0x201e +#define RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0 0x201f +#define RH_RF24_PROPERTY_MODEM_BCR_OSR_1 0x2022 +#define RH_RF24_PROPERTY_MODEM_BCR_OSR_0 0x2023 +#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2 0x2024 +#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1 0x2025 +#define RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0 0x2026 +#define RH_RF24_PROPERTY_MODEM_BCR_GAIN_1 0x2027 +#define RH_RF24_PROPERTY_MODEM_BCR_GAIN_0 0x2028 +#define RH_RF24_PROPERTY_MODEM_BCR_GEAR 0x2029 +#define RH_RF24_PROPERTY_MODEM_BCR_MISC1 0x202a +#define RH_RF24_PROPERTY_MODEM_AFC_GEAR 0x202c +#define RH_RF24_PROPERTY_MODEM_AFC_WAIT 0x202d +#define RH_RF24_PROPERTY_MODEM_AFC_GAIN_1 0x202e +#define RH_RF24_PROPERTY_MODEM_AFC_GAIN_0 0x202f +#define RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1 0x2030 +#define RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0 0x2031 +#define RH_RF24_PROPERTY_MODEM_AFC_MISC 0x2032 +#define RH_RF24_PROPERTY_MODEM_AGC_CONTROL 0x2035 +#define RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE 0x2038 +#define RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY 0x2039 +#define RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY 0x203a +#define RH_RF24_PROPERTY_MODEM_FSK4_GAIN1 0x203b +#define RH_RF24_PROPERTY_MODEM_FSK4_GAIN0 0x203c +#define RH_RF24_PROPERTY_MODEM_FSK4_TH1 0x203d +#define RH_RF24_PROPERTY_MODEM_FSK4_TH0 0x203e +#define RH_RF24_PROPERTY_MODEM_FSK4_MAP 0x203f +#define RH_RF24_PROPERTY_MODEM_OOK_PDTC 0x2040 +#define RH_RF24_PROPERTY_MODEM_OOK_CNT1 0x2042 +#define RH_RF24_PROPERTY_MODEM_OOK_MISC 0x2043 +#define RH_RF24_PROPERTY_MODEM_RAW_SEARCH 0x2044 +#define RH_RF24_PROPERTY_MODEM_RAW_CONTROL 0x2045 +#define RH_RF24_PROPERTY_MODEM_RAW_EYE_1 0x2046 +#define RH_RF24_PROPERTY_MODEM_RAW_EYE_0 0x2047 +#define RH_RF24_PROPERTY_MODEM_ANT_DIV_MODE 0x2048 +#define RH_RF24_PROPERTY_MODEM_ANT_DIV_CONTROL 0x2049 +#define RH_RF24_PROPERTY_MODEM_RSSI_THRESH 0x204a +#define RH_RF24_PROPERTY_MODEM_RSSI_JUMP_THRESH 0x204b +#define RH_RF24_PROPERTY_MODEM_RSSI_CONTROL 0x204c +#define RH_RF24_PROPERTY_MODEM_RSSI_CONTROL2 0x204d +#define RH_RF24_PROPERTY_MODEM_RSSI_COMP 0x204e +#define RH_RF24_PROPERTY_MODEM_ANT_DIV_CONT 0x2049 +#define RH_RF24_PROPERTY_MODEM_CLKGEN_BAND 0x2051 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0 0x2100 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0 0x2101 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0 0x2102 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0 0x2103 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0 0x2104 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0 0x2105 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0 0x2106 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0 0x2107 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0 0x2108 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0 0x2109 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0 0x210a +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0 0x210b +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0 0x210c +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0 0x210d +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0 0x210e +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1 0x210f +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2 0x2110 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3 0x2111 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0 0x2112 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0 0x2113 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0 0x2114 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0 0x2115 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0 0x2116 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0 0x2117 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0 0x2118 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0 0x2119 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0 0x211a +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0 0x211b +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0 0x211c +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0 0x211d +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0 0x211e +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0 0x211f +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0 0x2120 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1 0x2121 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2 0x2122 +#define RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3 0x2123 +#define RH_RF24_PROPERTY_PA_MODE 0x2200 +#define RH_RF24_PROPERTY_PA_PWR_LVL 0x2201 +#define RH_RF24_PROPERTY_PA_BIAS_CLKDUTY 0x2202 +#define RH_RF24_PROPERTY_PA_TC 0x2203 +#define RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF 0x2300 +#define RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT 0x2301 +#define RH_RF24_PROPERTY_SYNTH_VCO_KV 0x2302 +#define RH_RF24_PROPERTY_SYNTH_LPFILT3 0x2303 +#define RH_RF24_PROPERTY_SYNTH_LPFILT2 0x2304 +#define RH_RF24_PROPERTY_SYNTH_LPFILT1 0x2305 +#define RH_RF24_PROPERTY_SYNTH_LPFILT0 0x2306 +#define RH_RF24_PROPERTY_MATCH_VALUE_1 0x3000 +#define RH_RF24_PROPERTY_MATCH_MASK_1 0x3001 +#define RH_RF24_PROPERTY_MATCH_CTRL_1 0x3002 +#define RH_RF24_PROPERTY_MATCH_VALUE_2 0x3003 +#define RH_RF24_PROPERTY_MATCH_MASK_2 0x3004 +#define RH_RF24_PROPERTY_MATCH_CTRL_2 0x3005 +#define RH_RF24_PROPERTY_MATCH_VALUE_3 0x3006 +#define RH_RF24_PROPERTY_MATCH_MASK_3 0x3007 +#define RH_RF24_PROPERTY_MATCH_CTRL_3 0x3008 +#define RH_RF24_PROPERTY_MATCH_VALUE_4 0x3009 +#define RH_RF24_PROPERTY_MATCH_MASK_4 0x300a +#define RH_RF24_PROPERTY_MATCH_CTRL_4 0x300b +#define RH_RF24_PROPERTY_FREQ_CONTROL_INTE 0x4000 +#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_2 0x4001 +#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_1 0x4002 +#define RH_RF24_PROPERTY_FREQ_CONTROL_FRAC_0 0x4003 +#define RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_1 0x4004 +#define RH_RF24_PROPERTY_FREQ_CONTROL_CHANNEL_STEP_SIZE_0 0x4005 +#define RH_RF24_PROPERTY_FREQ_CONTROL_VCOCNT_RX_ADJ 0x4007 +#define RH_RF24_PROPERTY_RX_HOP_CONTROL 0x5000 +#define RH_RF24_PROPERTY_RX_HOP_TABLE_SIZE 0x5001 +#define RH_RF24_PROPERTY_RX_HOP_TABLE_ENTRY_0 0x5002 + +//#define RH_RF24_CMD_GPIO_PIN_CFG 0x13 +#define RH_RF24_GPIO_NO_CHANGE 0 +#define RH_RF24_GPIO_DISABLED 1 +#define RH_RF24_GPIO_LOW 2 +#define RH_RF24_GPIO_HIGH 3 +#define RH_RF24_GPIO_INPUT 4 +#define RH_RF24_GPIO_32_KHZ_CLOCK 5 +#define RH_RF24_GPIO_BOOT_CLOCK 6 +#define RH_RF24_GPIO_DIVIDED_MCU_CLOCK 7 +#define RH_RF24_GPIO_CTS 8 +#define RH_RF24_GPIO_INV_CTS 9 +#define RH_RF24_GPIO_HIGH_ON_CMD_OVERLAP 10 +#define RH_RF24_GPIO_SPI_DATA_OUT 11 +#define RH_RF24_GPIO_HIGH_AFTER_RESET 12 +#define RH_RF24_GPIO_HIGH_AFTER_CALIBRATION 13 +#define RH_RF24_GPIO_HIGH_AFTER_WUT 14 +#define RH_RF24_GPIO_UNUSED_0 15 +#define RH_RF24_GPIO_TX_DATA_CLOCK 16 +#define RH_RF24_GPIO_RX_DATA_CLOCK 17 +#define RH_RF24_GPIO_UNUSED_1 18 +#define RH_RF24_GPIO_TX_DATA 19 +#define RH_RF24_GPIO_RX_DATA 20 +#define RH_RF24_GPIO_RX_RAW_DATA 21 +#define RH_RF24_GPIO_ANTENNA_1_SWITCH 22 +#define RH_RF24_GPIO_ANTENNA_2_SWITCH 23 +#define RH_RF24_GPIO_VALID_PREAMBLE 24 +#define RH_RF24_GPIO_INVALID_PREAMBLE 25 +#define RH_RF24_GPIO_SYNC_DETECTED 26 +#define RH_RF24_GPIO_RSSI_ABOVE_CAT 27 +#define RH_RF24_GPIO_TX_STATE 32 +#define RH_RF24_GPIO_RX_STATE 33 +#define RH_RF24_GPIO_RX_FIFO_ALMOST_FULL 34 +#define RH_RF24_GPIO_TX_FIFO_ALMOST_EMPTY 35 +#define RH_RF24_GPIO_BATT_LOW 36 +#define RH_RF24_GPIO_RSSI_ABOVE_CAT_LOW 37 +#define RH_RF24_GPIO_HOP 38 +#define RH_RF24_GPIO_HOP_TABLE_WRAPPED 39 + +// #define RH_RF24_CMD_GET_INT_STATUS 0x20 +#define RH_RF24_INT_STATUS_CHIP_INT_STATUS 0x04 +#define RH_RF24_INT_STATUS_MODEM_INT_STATUS 0x02 +#define RH_RF24_INT_STATUS_PH_INT_STATUS 0x01 +#define RH_RF24_INT_STATUS_FILTER_MATCH 0x80 +#define RH_RF24_INT_STATUS_FILTER_MISS 0x40 +#define RH_RF24_INT_STATUS_PACKET_SENT 0x20 +#define RH_RF24_INT_STATUS_PACKET_RX 0x10 +#define RH_RF24_INT_STATUS_CRC_ERROR 0x08 +#define RH_RF24_INT_STATUS_TX_FIFO_ALMOST_EMPTY 0x02 +#define RH_RF24_INT_STATUS_RX_FIFO_ALMOST_FULL 0x01 +#define RH_RF24_INT_STATUS_INVALID_SYNC 0x20 +#define RH_RF24_INT_STATUS_RSSI_JUMP 0x10 +#define RH_RF24_INT_STATUS_RSSI 0x08 +#define RH_RF24_INT_STATUS_INVALID_PREAMBLE 0x04 +#define RH_RF24_INT_STATUS_PREAMBLE_DETECT 0x02 +#define RH_RF24_INT_STATUS_SYNC_DETECT 0x01 +#define RH_RF24_INT_STATUS_CAL 0x40 +#define RH_RF24_INT_STATUS_FIFO_UNDERFLOW_OVERFLOW_ERROR 0x20 +#define RH_RF24_INT_STATUS_STATE_CHANGE 0x10 +#define RH_RF24_INT_STATUS_CMD_ERROR 0x08 +#define RH_RF24_INT_STATUS_CHIP_READY 0x04 +#define RH_RF24_INT_STATUS_LOW_BATT 0x02 +#define RH_RF24_INT_STATUS_WUT 0x01 + +//#define RH_RF24_PROPERTY_GLOBAL_CLK_CFG 0x0001 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_EN 0x40 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_30 0x30 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_15 0x28 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_10 0x20 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_7_5 0x18 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_3 0x10 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_2 0x08 +#define RH_RF24_CLK_CFG_DIVIDED_CLK_SEL_1 0x00 +#define RH_RF24_CLK_CFG_CLK_32K_SEL_EXTERNAL 0x02 +#define RH_RF24_CLK_CFG_CLK_32K_SEL_RC 0x01 +#define RH_RF24_CLK_CFG_CLK_32K_SEL_DISABLED 0x00 + +//#define RH_RF24_PROPERTY_FRR_CTL_A_MODE 0x0200 +//#define RH_RF24_PROPERTY_FRR_CTL_B_MODE 0x0201 +//#define RH_RF24_PROPERTY_FRR_CTL_C_MODE 0x0202 +//#define RH_RF24_PROPERTY_FRR_CTL_D_MODE 0x0203 +#define RH_RF24_FRR_MODE_DISABLED 0 +#define RH_RF24_FRR_MODE_GLOBAL_STATUS 1 +#define RH_RF24_FRR_MODE_GLOBAL_INTERRUPT_PENDING 2 +#define RH_RF24_FRR_MODE_PACKET_HANDLER_STATUS 3 +#define RH_RF24_FRR_MODE_PACKET_HANDLER_INTERRUPT_PENDING 4 +#define RH_RF24_FRR_MODE_MODEM_STATUS 5 +#define RH_RF24_FRR_MODE_MODEM_INTERRUPT_PENDING 6 +#define RH_RF24_FRR_MODE_CHIP_STATUS 7 +#define RH_RF24_FRR_MODE_CHIP_INTERRUPT_PENDING 8 +#define RH_RF24_FRR_MODE_CURRENT_STATE 9 +#define RH_RF24_FRR_MODE_LATCHED_RSSI 10 + +//#define RH_RF24_PROPERTY_INT_CTL_ENABLE 0x0100 +#define RH_RF24_CHIP_INT_STATUS_EN 0x04 +#define RH_RF24_MODEM_INT_STATUS_EN 0x02 +#define RH_RF24_PH_INT_STATUS_EN 0x01 + +//#define RH_RF24_PROPERTY_PREAMBLE_CONFIG 0x1004 +#define RH_RF24_PREAMBLE_FIRST_1 0x20 +#define RH_RF24_PREAMBLE_FIRST_0 0x00 +#define RH_RF24_PREAMBLE_LENGTH_NIBBLES 0x00 +#define RH_RF24_PREAMBLE_LENGTH_BYTES 0x10 +#define RH_RF24_PREAMBLE_MAN_CONST 0x08 +#define RH_RF24_PREAMBLE_MAN_ENABLE 0x02 +#define RH_RF24_PREAMBLE_NON_STANDARD 0x00 +#define RH_RF24_PREAMBLE_STANDARD_1010 0x01 +#define RH_RF24_PREAMBLE_STANDARD_0101 0x02 + +//#define RH_RF24_PROPERTY_SYNC_CONFIG 0x1100 +#define RH_RF24_SYNC_CONFIG_SKIP_TX 0x80 +#define RH_RF24_SYNC_CONFIG_RX_ERRORS_MASK 0x70 +#define RH_RF24_SYNC_CONFIG_4FSK 0x08 +#define RH_RF24_SYNC_CONFIG_MANCH 0x04 +#define RH_RF24_SYNC_CONFIG_LENGTH_MASK 0x03 + +//#define RH_RF24_PROPERTY_PKT_CRC_CONFIG 0x1200 +#define RH_RF24_CRC_SEED_ALL_0S 0x00 +#define RH_RF24_CRC_SEED_ALL_1S 0x80 +#define RH_RF24_CRC_MASK 0x0f +#define RH_RF24_CRC_NONE 0x00 +#define RH_RF24_CRC_ITU_T 0x01 +#define RH_RF24_CRC_IEC_16 0x02 +#define RH_RF24_CRC_BIACHEVA 0x03 +#define RH_RF24_CRC_16_IBM 0x04 +#define RH_RF24_CRC_CCITT 0x05 +#define RH_RF24_CRC_KOOPMAN 0x06 +#define RH_RF24_CRC_IEEE_802_3 0x07 +#define RH_RF24_CRC_CASTAGNOLI 0x08 + +//#define RH_RF24_PROPERTY_PKT_CONFIG1 0x1206 +#define RH_RF24_PH_FIELD_SPLIT 0x80 +#define RH_RF24_PH_RX_DISABLE 0x40 +#define RH_RF24_4FSK_EN 0x20 +#define RH_RF24_RX_MULTI_PKT 0x10 +#define RH_RF24_MANCH_POL 0x08 +#define RH_RF24_CRC_INVERT 0x04 +#define RH_RF24_CRC_ENDIAN 0x02 +#define RH_RF24_BIT_ORDER 0x01 + +//#define RH_RF24_PROPERTY_PKT_FIELD_1_CONFIG 0x120f +//#define RH_RF24_PROPERTY_PKT_FIELD_2_CONFIG 0x1213 +//#define RH_RF24_PROPERTY_PKT_FIELD_3_CONFIG 0x1217 +//#define RH_RF24_PROPERTY_PKT_FIELD_4_CONFIG 0x121b +//#define RH_RF24_PROPERTY_PKT_FIELD_5_CONFIG 0x121f +#define RH_RF24_FIELD_CONFIG_4FSK 0x10 +#define RH_RF24_FIELD_CONFIG_WHITEN 0x02 +#define RH_RF24_FIELD_CONFIG_MANCH 0x01 + +//#define RH_RF24_PROPERTY_PKT_RX_FIELD_1_CRC_CONFIG 0x1224 +//#define RH_RF24_PROPERTY_PKT_RX_FIELD_2_CRC_CONFIG 0x1228 +//#define RH_RF24_PROPERTY_PKT_RX_FIELD_3_CRC_CONFIG 0x122c +//#define RH_RF24_PROPERTY_PKT_RX_FIELD_4_CRC_CONFIG 0x1230 +//#define RH_RF24_PROPERTY_PKT_RX_FIELD_5_CRC_CONFIG 0x1234 +#define RH_RF24_FIELD_CONFIG_CRC_START 0x80 +#define RH_RF24_FIELD_CONFIG_SEND_CRC 0x20 +#define RH_RF24_FIELD_CONFIG_CHECK_CRC 0x08 +#define RH_RF24_FIELD_CONFIG_CRC_ENABLE 0x02 + + + + +//#define RH_RF24_PROPERTY_MODEM_MOD_TYPE 0x2000 +#define RH_RF24_TX_DIRECT_MODE_TYPE_SYNCHRONOUS 0x00 +#define RH_RF24_TX_DIRECT_MODE_TYPE_ASYNCHRONOUS 0x80 +#define RH_RF24_TX_DIRECT_MODE_GPIO0 0x00 +#define RH_RF24_TX_DIRECT_MODE_GPIO1 0x20 +#define RH_RF24_TX_DIRECT_MODE_GPIO2 0x40 +#define RH_RF24_TX_DIRECT_MODE_GPIO3 0x60 +#define RH_RF24_MOD_SOURCE_PACKET_HANDLER 0x00 +#define RH_RF24_MOD_SOURCE_DIRECT_MODE 0x08 +#define RH_RF24_MOD_SOURCE_RANDOM_GENERATOR 0x10 +#define RH_RF24_MOD_TYPE_CW 0x00 +#define RH_RF24_MOD_TYPE_OOK 0x01 +#define RH_RF24_MOD_TYPE_2FSK 0x02 +#define RH_RF24_MOD_TYPE_2GFSK 0x03 +#define RH_RF24_MOD_TYPE_4FSK 0x04 +#define RH_RF24_MOD_TYPE_4GFSK 0x05 + +// RH_RF24_PROPERTY_PA_MODE 0x2200 +#define RH_RF24_PA_MODE_1_GROUP 0x04 +#define RH_RF24_PA_MODE_2_GROUPS 0x08 +#define RH_RF24_PA_MODE_CLASS_E 0x00 +#define RH_RF24_PA_MODE_SWITCH_CURRENT 0x01 + + +///////////////////////////////////////////////////////////////////// +/// \class RH_RF24 RH_RF24.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF24 and compatible radio transceiver. +/// +/// Works with +/// - Silicon Labs Si4460/1/2/3/4 transceiver chips +/// - The equivalent HopeRF RF24/25/26/27 transceiver chips +/// - HopeRF Complete modules: RFM24W/26W/27W +/// +/// \par Overview +/// +/// This class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 250 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF +/// RF24 and compatible radio modules, such as the RFM24W module. +/// +/// The Hope-RF (http://www.hoperf.com) RF24 family is a low-cost ISM transceiver +/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and +/// programmable data rates. HopeRF also sell these chips on modules which includes +/// a crystal and antenna coupling circuits: RFM24W, RFM26W and RFM27W +/// +/// This Driver provides functions for sending and receiving messages of up +/// to 250 octets on any frequency supported by the RF24, in a range of +/// predefined data rates and frequency deviations. Frequency can be set +/// to any frequency from 142.0MHz to 1050.0MHz. Caution: most modules only support a more limited +/// range of frequencies due to antenna tuning. +/// +/// Up to 2 RFM24 modules can be connected to an Arduino (3 on a Mega), +/// permitting the construction of translators and frequency changers, etc. +/// +/// The following modulation types are suppported with a range of modem configurations for +/// common data rates and frequency deviations: +/// - OOK On-Off Keying +/// - GFSK Gaussian Frequency Shift Keying +/// - FSK Frequency Shift Keying +/// +/// Support for other RF24 features such as on-chip temperature measurement, +/// transmitter power control etc is also provided. +/// +/// RH_RF24 uses interrupts to detect and handle events in the radio chip. The RF24 family has +/// TX and RX FIFOs of 64 bytes, but through the use of interrupt, the RH_RF24 driver can send longer +/// messages by filling or emptying the FIFOs on-the-fly. +/// +/// Tested on Anarduino Mini http://www.anarduino.com/mini/ with arduino-1.0.5 +/// on OpenSuSE 13.1. Also on Anarduino Mini with arduino-1.8.1 on Kubuntu 16.04 +/// +/// \par Packet Format +/// +/// All messages sent and received by this RH_RF24 Driver conform to this packet format: +/// +/// - 4 octets PREAMBLE (configurable) +/// - 2 octets SYNC 0x2d, 0xd4 (configurable, so you can use this as a network filter) +/// - Field containing 1 octet of message length and 2 octet CRC protecting this field +/// - Field 2 containing at least 4 octets, and 2 octet CRC protecting this field: +/// + 4 octets HEADER: (TO, FROM, ID, FLAGS) +/// + 0 to 250 octets DATA +/// + 2 octets CRC, computed on HEADER and DATA +/// +/// \par Connecting RFM-24 to Arduino +/// +/// For RFM24/RFM26 and Teensy 3.1 or Anarduino Mini +/// \code +/// Teensy RFM-24/RFM26 +/// GND----------GND (ground in) +/// 3V3----------VCC (3.3V in) +/// interrupt 2 pin D2-----------NIRQ (interrupt request out) +/// SS pin D10----------NSEL (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------SDI (SPI Data in) +/// MISO pin D12----------SDO (SPI data out) +/// D9-----------SDN (shutdown in) +/// /--GPIO0 (GPIO0 out to control transmitter antenna TX_ANT) +/// \--TX_ANT (TX antenna control in) RFM22B only +/// /--GPIO1 (GPIO1 out to control receiver antenna RX_ANT) +/// \--RX_ANT (RX antenna control in) RFM22B only +/// \endcode +/// Caution: tying the radio SDN pin to ground (though it might appear from the data sheets to make sense) +/// does not always produce a reliable radio startup. So this driver controls the SDN pin directly. +/// Note: the GPIO0-TX_ANT and GPIO1-RX_ANT connections are not required for the 11dBm RFM24W, +/// which has no antenna switch. +/// +/// If you have an Arduino Zero, you should note that you cannot use Pin 2 for the interrupt line +/// (Pin 2 is for the NMI only), instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this: +/// \code +/// // Slave Select is pin 10, interrupt is Pin 3 +/// RH_RF24 driver(10, 3); +/// \endcode +/// +/// \par Customising and configuring +/// +/// The RH_RF24 module uses a radio configuration header file to configure the basic radio operation +/// frequency and modulation scheme. The radio configuration header file must be generated with the +/// Silicon Labs Wireless Development Suite (WDS) program and \#included by RH_RF24.cpp +/// +/// The library will work out of the box and without further configuring with these parameters: +/// - Si4464 or equvalent +/// - 30MHz Crytstal +/// - 434MHz base frequncy band +/// - 2GFSK modulation +/// - 5kbps data rate +/// - 10kHz deviation +/// using the radio configuration header file +/// RF24configs/radio_config_Si4464_30_434_2GFSK_5_10.h +/// which is included in RadioHead. +/// +/// In order to use different frequency bands or modulation schemes, you must generate a new +/// radio configuration header file +/// with WDS, or select one of a small set of prebuilt headers from the RF24configs folder (see README in that +/// folder for details of the filename format). +/// +/// To generate a new header file: +/// +/// - Install Silicon Labs Wireless Development Suite (WDS) 3.2.11.0 or later +/// (Windows only, we were not able to get it to run under Wine on Linux) +/// - Run WDS +/// - Menu->Start Simulation +/// - Select radio chip type Si4464, press Select Radio +/// - Select Radio Configuration Application, press Select Application +/// - Click on Standard Packet Tx +/// - On the Frequency and Power tab, Select the Frequency, crystal frequency etc. The PA power level is irrelevant, +/// since power is set programatically +/// - On the RF parameters tab, select the modulation type and rates +/// - Press Generate Source, Save custom radio configuration header file +/// - Enter a new unique file name in the RF24configs folder in RadioHead +/// - Edit RH_RF24.cpp to use this new header file +/// - Recompile RH_RF24 +/// +/// \par RSSI +/// +/// The RSSI (Received Signal Strength Indicator) is measured and latched after the message sync bytes are received. +/// The latched RSSI is available from the lastRssi() member functionafter the complete message is received. +/// Although lastRssi() +/// supposedly returns a signed integer, in the case of this radio it actually returns an unsigned 8 bit integer (uint8_t) +/// and you will have to cast the return value to use it: +/// \code +/// uint8_t lastRssi = (uint8_t)rf24.lastRssi(); +/// \endcode +/// The units of RSSI are arbitrary and relative, with larger unsigned numbers indicating a stronger signal. Values up to 255 +/// are seen with radios in close proximity to each other. Lower limit of receivable strength is about 70. +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power on the RF24/25/26/27 transceiver +/// with the RH_RF24::setTxPower() function. The argument can be any of +/// 0x00 to 0x4f (for RFM24/Si4460) or +/// 0x00 to 0x7f (for others) +/// 0x00 will yield no measurable power. For other settings there is a non-linear correlation with actual +/// RF power output (see below) +/// The default is 0x10. Eg: +/// \code +/// driver.setTxPower(0x10); +/// \endcode +/// +/// We have made some actual power measurements against +/// programmed power +/// - Anarduino Mini with RFM24-433 and RFM26-433 at Vcc = 3.3V, in CW mode, 434MHz +/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND +/// - bnc connecteor +/// - 12dB attenuator +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Digitech QM-1460 digital multimeter +/// \code +/// Program power Measured Power dBm +/// HEX RFM24 RFM26 +/// 0x00 not measurable not measurable +/// 0x01 -20.4 -20.6 +/// 0x0f 2.4 4.8 +/// 0x1f 9.4 11.0 +/// 0x2f 11.2 14.2 +/// 0x3f 11.6 16.4 +/// 0x4f 11.6 18.0 +/// 0x5f 18.6 +/// 0x6f 19.0 +/// 0x7f 19.2 +/// \endcode +/// Caution: the actual radiated power output will depend heavily on the power supply voltage and the antenna. + +class RH_RF24 : public RHSPIDriver +{ +public: + /// \brief Defines property values for a set of modem configuration registers + /// + /// Defines property values for a set of modem configuration registers + /// that can be passed to setModemRegisters() if none of the choices in + /// ModemConfigChoice suit your need setModemRegisters() writes the + /// property values from this structure to the appropriate RF24 properties + /// to set the desired modulation type, data rate and deviation/bandwidth. + /// OBSOLETE: no need ever to use this now + typedef struct + { + uint8_t prop_2000; ///< Value for property RH_RF24_PROPERTY_MODEM_MOD_TYPE + uint8_t prop_2003; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_2 + uint8_t prop_2004; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_1 + uint8_t prop_2005; ///< Value for property RH_RF24_PROPERTY_MODEM_DATA_RATE_0 + uint8_t prop_2006; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_3 + uint8_t prop_2007; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_2 + uint8_t prop_2008; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_1 + uint8_t prop_2009; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_NCO_MODE_0 + uint8_t prop_200a; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_2 + uint8_t prop_200b; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_1 + uint8_t prop_200c; ///< Value for property RH_RF24_PROPERTY_MODEM_FREQ_DEV_0 + uint8_t prop_2018; ///< Value for property RH_RF24_PROPERTY_MODEM_TX_RAMP_DELAY + uint8_t prop_201e; ///< Value for property RH_RF24_PROPERTY_MODEM_DECIMATION_CFG1 + uint8_t prop_201f; ///< Value for property RH_RF24_PROPERTY_MODEM_DECIMATION_CFG0 + uint8_t prop_2022; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_OSR_1 + uint8_t prop_2023; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_OSR_0 + uint8_t prop_2024; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_2 + uint8_t prop_2025; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_1 + uint8_t prop_2026; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_NCO_OFFSET_0 + uint8_t prop_2027; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GAIN_1 + uint8_t prop_2028; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GAIN_0 + uint8_t prop_2029; ///< Value for property RH_RF24_PROPERTY_MODEM_BCR_GEAR + uint8_t prop_202d; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_WAIT + uint8_t prop_202e; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_GAIN_1 + uint8_t prop_202f; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_GAIN_0 + uint8_t prop_2030; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_LIMITER_1 + uint8_t prop_2031; ///< Value for property RH_RF24_PROPERTY_MODEM_AFC_LIMITER_0 + uint8_t prop_2035; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_CONTROL + uint8_t prop_2038; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_WINDOW_SIZE + uint8_t prop_2039; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_RFPD_DECAY + uint8_t prop_203a; ///< Value for property RH_RF24_PROPERTY_MODEM_AGC_IFPD_DECAY + uint8_t prop_203b; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_GAIN1 + uint8_t prop_203c; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_GAIN0 + uint8_t prop_203d; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_TH1 + uint8_t prop_203e; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_TH0 + uint8_t prop_203f; ///< Value for property RH_RF24_PROPERTY_MODEM_FSK4_MAP + uint8_t prop_2040; ///< Value for property RH_RF24_PROPERTY_MODEM_OOK_PDTC + uint8_t prop_2043; ///< Value for property RH_RF24_PROPERTY_MODEM_OOK_MISC + uint8_t prop_2045; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_CONTROL + uint8_t prop_2046; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_EYE_1 + uint8_t prop_2047; ///< Value for property RH_RF24_PROPERTY_MODEM_RAW_EYE_0 + uint8_t prop_204e; ///< Value for property RH_RF24_PROPERTY_MODEM_RSSI_COMP + uint8_t prop_2100; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE13_7_0 + uint8_t prop_2101; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE12_7_0 + uint8_t prop_2102; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE11_7_0 + uint8_t prop_2103; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE10_7_0 + uint8_t prop_2104; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE9_7_0 + uint8_t prop_2105; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE8_7_0 + uint8_t prop_2106; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE7_7_0 + uint8_t prop_2107; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE6_7_0 + uint8_t prop_2108; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE5_7_0 + uint8_t prop_2109; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE4_7_0 + uint8_t prop_210a; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE3_7_0 + uint8_t prop_210b; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE2_7_0 + uint8_t prop_210c; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE1_7_0 + uint8_t prop_210d; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COE0_7_0 + uint8_t prop_210e; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM0 + uint8_t prop_210f; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM1 + uint8_t prop_2110; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM2 + uint8_t prop_2111; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX1_CHFLT_COEM3 + uint8_t prop_2112; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE13_7_0 + uint8_t prop_2113; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE12_7_0 + uint8_t prop_2114; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE11_7_0 + uint8_t prop_2115; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE10_7_0 + uint8_t prop_2116; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE9_7_0 + uint8_t prop_2117; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE8_7_0 + uint8_t prop_2118; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE7_7_0 + uint8_t prop_2119; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE6_7_0 + uint8_t prop_211a; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE5_7_0 + uint8_t prop_211b; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE4_7_0 + uint8_t prop_211c; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE3_7_0 + uint8_t prop_211d; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE2_7_0 + uint8_t prop_211e; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE1_7_0 + uint8_t prop_211f; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COE0_7_0 + uint8_t prop_2120; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM0 + uint8_t prop_2121; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM1 + uint8_t prop_2122; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM2 + uint8_t prop_2123; ///< Value for property RH_RF24_PROPERTY_MODEM_CHFLT_RX2_CHFLT_COEM3 + uint8_t prop_2203; ///< Value for property RH_RF24_PROPERTY_PA_TC + uint8_t prop_2300; ///< Value for property RH_RF24_PROPERTY_SYNTH_PFDCP_CPFF + uint8_t prop_2301; ///< Value for property RH_RF24_PROPERTY_SYNTH_PFDCP_CPINT + uint8_t prop_2303; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT3 + uint8_t prop_2304; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT2 + uint8_t prop_2305; ///< Value for property RH_RF24_PROPERTY_SYNTH_LPFILT1 + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common + /// modulation types, and data rates. If you need another configuration, + /// use the register calculator. and call setModemRegisters() with your + /// desired settings. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that values will be + /// changed in later versions (though we will try to avoid it). + /// Contributions of new complete and tested ModemConfigs ready to add to this list will be readily accepted. + /// OBSOLETE: no need ever to use this now + typedef enum + { + FSK_Rb0_5Fd1 = 0, ///< FSK Rb = 0.5kbs, Fd = 1kHz + FSK_Rb5Fd10, ///< FSK Rb = 5kbs, Fd = 10kHz + FSK_Rb50Fd100, ///< FSK Rb = 50kbs, Fd = 100kHz + FSK_Rb150Fd300, ///< FSK Rb = 50kbs, Fd = 100kHz + + GFSK_Rb0_5Fd1, ///< GFSK Rb = 0.5kbs, Fd = 1kHz + GFSK_Rb5Fd10, ///< GFSK Rb = 5kbs, Fd = 10kHz + GFSK_Rb50Fd100, ///< GFSK Rb = 50kbs, Fd = 100kHz + GFSK_Rb150Fd300, ///< GFSK Rb = 150kbs, Fd = 300kHz + + // We were unable to get any other OOKs to work + OOK_Rb5Bw30, ///< OOK Rb = 5kbs, Bw = 30kHz + OOK_Rb10Bw40, ///< OOK Rb = 10kbs, Bw = 40kHz + + // We were unable to get any 4FSK or 4GFSK schemes to work + + } ModemConfigChoice; + + /// \brief Defines the available choices for CRC + /// Types of permitted CRC polynomials, to be passed to setCRCPolynomial() + /// They deliberately have the same numeric values as the CRC_POLYNOMIAL field of PKT_CRC_CONFIG + typedef enum + { + CRC_NONE = 0, + CRC_ITU_T, + CRC_IEC_16, + CRC_Biacheva, + CRC_16_IBM, + CRC_CCITT, + CRC_Koopman, + CRC_IEEE_802_3, + CRC_Castagnoli, + } CRCPolynomial; + + /// \brief Defines the commands we can interrogate in printRegisters + typedef struct + { + uint8_t cmd; ///< The command number + uint8_t replyLen; ///< Number of bytes in the reply stream (after the CTS) + } CommandInfo; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RF24 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the RF24 DIO0 interrupt line. + /// Defaults to pin 2. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param [in] sdnPin The pin number connected to SDN on the radio. Defaults to pin 9. + /// Connecting SDN directly to ground does not aloways provide reliable radio startup. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_RF24(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, uint8_t sdnPin = 9, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken: + /// - Initialise the slave select and shutdown pins and the SPI interface library + /// - Checks the connected RF24 module can be communicated + /// - Attaches an interrupt handler + /// - Configures the RF24 module + /// - Sets the frequency to 434.0 MHz + /// - Sets the modem data rate to GFSK_Rb5Fd10 + /// - Sets the tranmitter power level to 16 (about 2.4dBm on RFM4) + /// \return true if everything was successful + bool init(); + + /// Sets the chip mode that will be used when the RH_RF24 driver is idle (ie not transmitting or receiving) + /// You can use this to control the power level consumed while idle, at the cost of slower + /// transition to tranmit or receive states + /// \param[in] idleMode The chip state to use when idle. Sensible choices might be RH_RF24_DEVICE_STATE_SLEEP or RH_RF24_DEVICE_STATE_READY + void setIdleMode(uint8_t idleMode); + + /// Sets the transmitter and receiver + /// centre frequency. + /// Valid frequency ranges for RFM24/Si4460, Si4461, RFM25/Si4463 are: + /// 142MHz to 175Mhz, 284MHz to 350MHz, 425MHz to 525MHz, 850MHz to 1050MHz. + /// Valid frequency ranges for RFM26/Si4464 are: + /// 119MHz to 960MHz. + /// Caution: RFM modules are designed with antenna coupling components to suit a limited band + /// of frequencies (marked underneath the module). It is possible to set frequencies in other bands, + /// but you may only get little or no power radiated. + /// Caution, you can only use this function to change frequency within the frequency band configured by + /// the radio configuration header file. To use a frequency in a different band, you must recompile with + /// the appropriate radio configuration header file. Setting a frequency in anotehr band will + /// have unpredicatble results. + /// \param[in] centre Frequency in MHz. + /// \param[in] afcPullInRange Not used + /// \return true if the selected frequency is within a valid range for the connected radio and if + /// setting the new frequency succeeded. + bool setFrequency(float centre, float afcPullInRange = 0.05); + + /// OBSOLETE, do not use. + /// To get different modulation schemes, you must generate a new radio config file + /// as described in this documentation. + /// Sets all the properties required to configure the data modem in the RF24, including the data rate, + /// bandwidths etc. You can use this to configure the modem with custom configurations if none of the + /// canned configurations in ModemConfigChoice suit you. + /// \param[in] config A ModemConfig structure containing values for the modem configuration registers. + void setModemRegisters(const ModemConfig* config); + + /// OBSOLETE, do not use. + /// To get different modulation schemes, you must generate a new radio config file + /// as described in this documentation. + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. The default after init() is RH_RF24::GFSK_Rb5Fd10. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Starts the receiver and checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + bool send(const uint8_t* data, uint8_t len); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Sets the length of the preamble + /// in bytes. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 4. + /// \param[in] bytes Preamble length in bytes. + void setPreambleLength(uint16_t bytes); + + /// Sets the sync words for transmit and receive + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0x2d, 0xd4 }. + /// \param[in] syncWords Array of sync words, 1 to 4 octets long. NULL if no sync words to be used. + /// \param[in] len Number of sync words to set, 1 to 4. 0 if no sync words to be used. + void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0); + + /// Sets the CRC polynomial to be used to generate the CRC for both receive and transmit + /// otherwise the default of CRC_16_IBM will be used. + /// \param[in] polynomial One of RH_RF24::CRCPolynomial choices CRC_* + /// \return true if polynomial is a valid option for this radio. + bool setCRCPolynomial(CRCPolynomial polynomial); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the RF24. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the RF24. + void setModeTx(); + + /// Sets the transmitter power output level register PA_PWR_LVL + /// The power argument to this function has a non-linear correlation with the actual RF power output. + /// See the transmitter power table above for some examples. + /// Also the Si446x Data Sheet section 5.4.2 may be helpful. + /// Be a good neighbour and set the lowest power level you need. + /// Caution: legal power limits may apply in certain countries. + /// After init(), the power will be set to 0x10. + /// \param[in] power Transmitter power level. For RFM24/Si4460, valid values are 0x00 to 0x4f. For others, 0x00 to 0x7f + void setTxPower(uint8_t power); + + /// Dump the values of available command replies and properties + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// Not all commands have valid replies, therefore they are not all printed. + /// Caution: the list is very long + bool printRegisters(); + + /// Send a string of command bytes to the chip and get a string of reply bytes + /// Different RFM24 commands take different numbers of command bytes and send back different numbers + /// of reply bytes. See the Si446x documentaiton for more details. + /// Both command bytes and reply bytes are optional + /// \param[in] cmd The command number. One of RH_RF24_CMD_* + /// \param[in] write_buf Pointer to write_len bytes of command input bytes to send. If there are none, set to NULL. + /// \param[in] write_len The number of bytes to send from write_buf. If there are none, set to 0 + /// \param[out] read_buf Pointer to read_len bytes of storage where the reply stream from the comand will be written. + /// If none are required, set to NULL + /// \param[in] read_len The number of bytes to read from the reply stream. If none required, set to 0. + /// \return true if the command succeeeded. + bool command(uint8_t cmd, const uint8_t* write_buf = 0, uint8_t write_len = 0, uint8_t* read_buf = 0, uint8_t read_len = 0); + + /// Set one or more chip properties using the RH_RF24_CMD_SET_PROPERTY + /// command. See the Si446x API Description AN625 for details on what properties are available. + /// param[in] firstProperty The property number of the first property to set. The first value in the values array + /// will be used to set this property, and any subsequent values will be used to set the following properties. + /// One of RH_RF24_PROPERTY_* + /// param[in] values Array of 0 or more values to write the firstProperty and subsequent proerties + /// param[in] count The number of values in the values array + /// \return true if the command succeeeded. + bool set_properties(uint16_t firstProperty, const uint8_t* values, uint8_t count); + + /// Get one or more chip properties using the RH_RF24_CMD_GET_PROPERTY + /// command. See the Si446x API Description AN625 for details on what properties are available. + /// param[in] firstProperty The property number of the first property to get. The first value in the values array + /// will be set with this property, and any subsequent values will be set from the following properties. + /// One of RH_RF24_PROPERTY_* + /// param[out] values Array of 0 or more values to receive the firstProperty and subsequent proerties + /// param[in] count The number of values in the values array + /// \return true if the command succeeeded. + bool get_properties(uint16_t firstProperty, uint8_t* values, uint8_t count); + + /// Measures and returns the current + /// Chip temperature. + /// \return The current chip temperature in degrees Centigrade + float get_temperature(); + + /// Measures and returns the current + /// Chip Vcc supply voltage. + /// \return The current chip Vcc supply voltage in Volts. + float get_battery_voltage(); + + /// Measures and returns the current + /// voltage applied to a GPIO pin (which has previously been configured as a voltage input) + /// \param[in] gpio The GPIO pin to read. 0 to 3. + /// \return The current pin voltage in Volts. + float get_gpio_voltage(uint8_t gpio); + + /// Read one of the Fast Read Response registers. + /// The Fast Read Response register must be previously configured with the matching + /// RH_RF24_PROPERTY_FRR_CTL_?_MODE property to select what chip property will be available in that register. + /// \param[in] reg The index of the FRR register to read. 0 means FRR A, 1 means B etc. + /// \return the value read from the specified Fast Read Response register. + uint8_t frr_read(uint8_t reg); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finte time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + + /// Return the integer value of the device type + /// as read from the device in from RH_RF24_CMD_PART_INFO. + /// One of 0x4460, 0x4461, 0x4462 or 0x4463, depending on the type of device actually + /// connected. + /// \return The integer device type + uint16_t deviceType() {return _deviceType;}; + +protected: + /// This is a low level function to handle the interrupts for one instance of RF24. + /// Called automatically by isr*() + /// Should not need to be called by user code. + void handleInterrupt(); + + /// Clears the chips RX FIFO + /// \return true if successful + bool clearRxFifo(); + + /// Clears RH_RF24's internal TX and RX buffers and counters + void clearBuffer(); + + /// Loads the next part of the currently transmitting message + /// into the chips TX buffer + void sendNextFragment(); + + /// Copies the next part of the currenrtly received message from the chips RX FIFO to the + /// receive buffer + void readNextFragment(); + + /// Loads data into the chips TX FIFO + /// \param[in] data Array of data bytes to be loaded + /// \param[in] len Number of bytes in data to be loaded + /// \return true if successful + bool writeTxFifo(uint8_t *data, uint8_t len); + + /// Checks the contents of the RX buffer. + /// If it contans a valid message adressed to this node + /// sets _rxBufValid. + void validateRxBuf(); + + /// Cycles the Shutdown pin to force the cradio chip to reset + void power_on_reset(); + + /// Sets registers, commands and properties + /// in the ratio according to the data in the commands array + /// \param[in] commands Array of data containing radio commands in the format provided by radio_config_Si4460.h + /// \return true if successful + bool configure(const uint8_t* commands); + + /// Clears all pending interrutps in the radio chip. + bool cmd_clear_all_interrupts(); + +private: + + /// Low level interrupt service routine for RF24 connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for RF24 connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for RF24 connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_RF24* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// The configured pin connected to the SDN pin of the radio + uint8_t _sdnPin; + + /// The radio OP mode to use when mode is RHModeIdle + uint8_t _idleMode; + + /// The reported PART device type + uint16_t _deviceType; + + /// The selected output power in dBm + int8_t _power; + + /// The message length in _buf + volatile uint8_t _bufLen; + + /// Array of octets of the last received message or the next to transmit message + uint8_t _buf[RH_RF24_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the Rx buffer + volatile bool _rxBufValid; + + /// Index into TX buffer of the next to send chunk + volatile uint8_t _txBufSentIndex; + + /// Time in millis since the last preamble was received (and the last time the RSSI was measured) + uint32_t _lastPreambleTime; + +}; + +/// @example rf24_client.pde +/// @example rf24_server.pde +/// @example rf24_lowpower_client.pde +/// @example rf24_reliable_datagram_client.pde +/// @example rf24_reliable_datagram_server.pde + +#endif diff --git a/RH_RF24_property_data/convert.pl b/RH_RF24_property_data/convert.pl new file mode 100755 index 0000000..502bb79 --- /dev/null +++ b/RH_RF24_property_data/convert.pl @@ -0,0 +1,132 @@ +#!/usr/bin/perl +# +# Convert a RH_RFM24 dump of a desired modulation made with printRegisters into an entry suitable for +# inclusion in RH_RF24::ModemConfig MODEM_CONFIG_TABLE + +use strict; + +# List of the properties that are relevant to modulation schemes and speeds +my @wanted_properties = ( + 0x2000, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200a, + 0x200b, + 0x200c, + 0x2018, + 0x201e, + 0x201f, + 0x2022, + 0x2023, + 0x2024, + 0x2025, + 0x2026, + 0x2027, + 0x2028, + 0x2029, + 0x202d, + 0x202e, + 0x202f, + 0x2030, + 0x2031, + 0x2035, + 0x2038, + 0x2039, + 0x203a, + 0x203b, + 0x203c, + 0x203d, + 0x203e, + 0x203f, + 0x2040, + 0x2043, + 0x2045, + 0x2046, + 0x2047, + 0x204e, + 0x2100, + 0x2101, + 0x2102, + 0x2103, + 0x2104, + 0x2105, + 0x2106, + 0x2107, + 0x2108, + 0x2109, + 0x210a, + 0x210b, + 0x210c, + 0x210d, + 0x210e, + 0x210f, + 0x2110, + 0x2111, + 0x2112, + 0x2113, + 0x2114, + 0x2115, + 0x2116, + 0x2117, + 0x2118, + 0x2119, + 0x211a, + 0x211b, + 0x211c, + 0x211d, + 0x211e, + 0x211f, + 0x2120, + 0x2121, + 0x2122, + 0x2123, + 0x2203, + 0x2300, + 0x2301, + 0x2303, + 0x2304, + 0x2305, + ); + +my %properties; + +while (<>) +{ + if (/prop: (\S+): (\S+)/) + { + my $prop_num = hex($1); + my $prop_value = hex($2); + $properties{$prop_num} = $prop_value; + } +} + +# now have all the properties in %properties +# dump the ones we are interested in + +my $prop_num; + +print " { "; +foreach $prop_num (@wanted_properties) +{ + if (exists($properties{$prop_num})) + { + printf "0x%02x, ", $properties{$prop_num}; + } + else + { + printf "not present: 0x%04x\n", $prop_num; + } +} +print "},\n"; + +print "\nPut these lines in RH_RF24::setModemRegisters\n\n"; +# Generate lines for RH_RF24::setModemRegisters +foreach $prop_num (@wanted_properties) +{ + printf " set_properties(0x%04x, &config->prop_%04x, 1);\n", $prop_num, $prop_num; +} diff --git a/RH_RF69.cpp b/RH_RF69.cpp new file mode 100644 index 0000000..d9ab8de --- /dev/null +++ b/RH_RF69.cpp @@ -0,0 +1,569 @@ +// RH_RF69.cpp +// +// Copyright (C) 2011 Mike McCauley +// $Id: RH_RF69.cpp,v 1.31 2019/09/02 05:21:52 mikem Exp $ + +#include + +// 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 +RH_RF69* RH_RF69::_deviceForInterrupt[RH_RF69_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_RF69::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// 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 + + // 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) +{ + _interruptPin = interruptPin; + _idleMode = RH_RF69_OPMODE_MODE_STDBY; + _myInterruptIndex = 0xff; // Not allocated yet +} + +void RH_RF69::setIdleMode(uint8_t idleMode) +{ + _idleMode = idleMode; +} + +bool RH_RF69::init() +{ + if (!RHSPIDriver::init()) + return false; + + // 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 + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + + // 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; + + // Add by Adrien van den Bossche 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 + + 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. +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"); + } +} + +// 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.beginTransaction(); + _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); + _spi.endTransaction(); + 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 +void RH_INTERRUPT_ATTR RH_RF69::isr0() +{ + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF69::isr1() +{ + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF69::isr2() +{ + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +} + +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 + (void)afcPullInRange; + 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, bool ishighpowermodule) +{ + _power = power; + uint8_t palevel; + + if (ishighpowermodule) + { + if (_power < -2) + _power = -2; //RFM69HW only works down to -2. + if (_power <= 13) + { + // -2dBm to +13dBm + //Need PA1 exclusivelly on RFM69HW + palevel = RH_RF69_PALEVEL_PA1ON | ((_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); + } + } + else + { + if (_power < -18) _power = -18; + if (_power > 13) _power = 13; //limit for RFM69W + palevel = RH_RF69_PALEVEL_PA0ON + | ((_power + 18) & 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; +} + +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() +{ + 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; +} + +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; +} diff --git a/RH_RF69.h b/RH_RF69.h new file mode 100644 index 0000000..1c92f89 --- /dev/null +++ b/RH_RF69.h @@ -0,0 +1,1052 @@ +// RH_RF69.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_RF69.h,v 1.38 2020/04/09 23:40:34 mikem Exp $ +// +/// + + +#ifndef RH_RF69_h +#define RH_RF69_h + +#include +#include + +// The crystal oscillator frequency of the RF69 module +#define RH_RF69_FXOSC 32000000.0 + +// The Frequency Synthesizer step = RH_RF69_FXOSC / 2^^19 +#define RH_RF69_FSTEP (RH_RF69_FXOSC / 524288) + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_RF69_NUM_INTERRUPTS 3 + +// This is the bit in the SPI address that marks it as a write +#define RH_RF69_SPI_WRITE_MASK 0x80 + +// Max number of octets the RH_RF69 Rx and Tx FIFOs can hold +#define RH_RF69_FIFO_SIZE 66 + +// Maximum encryptable payload length the RF69 can support +#define RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN 64 + +// The length of the headers we add. +// The headers are inside the RF69's payload and are therefore encrypted if encryption is enabled +#define RH_RF69_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this driver. Limited by +// the size of the FIFO, since we are unable to support on-the-fly filling and emptying +// of the FIFO. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 4 bytes of address and header and payload to be included in the 64 byte encryption limit. +// the one byte payload length is not encrpyted +#ifndef RH_RF69_MAX_MESSAGE_LEN +#define RH_RF69_MAX_MESSAGE_LEN (RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN - RH_RF69_HEADER_LEN) +#endif + +// Keep track of the mode the RF69 is in +#define RH_RF69_MODE_IDLE 0 +#define RH_RF69_MODE_RX 1 +#define RH_RF69_MODE_TX 2 + +// This is the default node address, +#define RH_RF69_DEFAULT_NODE_ADDRESS 0 + +// You can define the following macro (either by editing here or by passing it as a compiler definition +// to change the default value of the ishighpowermodule argument to setTxPower to true +// +// #define RFM69_HW +#ifdef RFM69_HW +#define RH_RF69_DEFAULT_HIGHPOWER true +#else +#define RH_RF69_DEFAULT_HIGHPOWER false +#endif + +// Register names +#define RH_RF69_REG_00_FIFO 0x00 +#define RH_RF69_REG_01_OPMODE 0x01 +#define RH_RF69_REG_02_DATAMODUL 0x02 +#define RH_RF69_REG_03_BITRATEMSB 0x03 +#define RH_RF69_REG_04_BITRATELSB 0x04 +#define RH_RF69_REG_05_FDEVMSB 0x05 +#define RH_RF69_REG_06_FDEVLSB 0x06 +#define RH_RF69_REG_07_FRFMSB 0x07 +#define RH_RF69_REG_08_FRFMID 0x08 +#define RH_RF69_REG_09_FRFLSB 0x09 +#define RH_RF69_REG_0A_OSC1 0x0a +#define RH_RF69_REG_0B_AFCCTRL 0x0b +#define RH_RF69_REG_0C_RESERVED 0x0c +#define RH_RF69_REG_0D_LISTEN1 0x0d +#define RH_RF69_REG_0E_LISTEN2 0x0e +#define RH_RF69_REG_0F_LISTEN3 0x0f +#define RH_RF69_REG_10_VERSION 0x10 +#define RH_RF69_REG_11_PALEVEL 0x11 +#define RH_RF69_REG_12_PARAMP 0x12 +#define RH_RF69_REG_13_OCP 0x13 +#define RH_RF69_REG_14_RESERVED 0x14 +#define RH_RF69_REG_15_RESERVED 0x15 +#define RH_RF69_REG_16_RESERVED 0x16 +#define RH_RF69_REG_17_RESERVED 0x17 +#define RH_RF69_REG_18_LNA 0x18 +#define RH_RF69_REG_19_RXBW 0x19 +#define RH_RF69_REG_1A_AFCBW 0x1a +#define RH_RF69_REG_1B_OOKPEAK 0x1b +#define RH_RF69_REG_1C_OOKAVG 0x1c +#define RH_RF69_REG_1D_OOKFIX 0x1d +#define RH_RF69_REG_1E_AFCFEI 0x1e +#define RH_RF69_REG_1F_AFCMSB 0x1f +#define RH_RF69_REG_20_AFCLSB 0x20 +#define RH_RF69_REG_21_FEIMSB 0x21 +#define RH_RF69_REG_22_FEILSB 0x22 +#define RH_RF69_REG_23_RSSICONFIG 0x23 +#define RH_RF69_REG_24_RSSIVALUE 0x24 +#define RH_RF69_REG_25_DIOMAPPING1 0x25 +#define RH_RF69_REG_26_DIOMAPPING2 0x26 +#define RH_RF69_REG_27_IRQFLAGS1 0x27 +#define RH_RF69_REG_28_IRQFLAGS2 0x28 +#define RH_RF69_REG_29_RSSITHRESH 0x29 +#define RH_RF69_REG_2A_RXTIMEOUT1 0x2a +#define RH_RF69_REG_2B_RXTIMEOUT2 0x2b +#define RH_RF69_REG_2C_PREAMBLEMSB 0x2c +#define RH_RF69_REG_2D_PREAMBLELSB 0x2d +#define RH_RF69_REG_2E_SYNCCONFIG 0x2e +#define RH_RF69_REG_2F_SYNCVALUE1 0x2f +// another 7 sync word bytes follow, 30 through 36 inclusive +#define RH_RF69_REG_37_PACKETCONFIG1 0x37 +#define RH_RF69_REG_38_PAYLOADLENGTH 0x38 +#define RH_RF69_REG_39_NODEADRS 0x39 +#define RH_RF69_REG_3A_BROADCASTADRS 0x3a +#define RH_RF69_REG_3B_AUTOMODES 0x3b +#define RH_RF69_REG_3C_FIFOTHRESH 0x3c +#define RH_RF69_REG_3D_PACKETCONFIG2 0x3d +#define RH_RF69_REG_3E_AESKEY1 0x3e +// Another 15 AES key bytes follow +#define RH_RF69_REG_4E_TEMP1 0x4e +#define RH_RF69_REG_4F_TEMP2 0x4f +#define RH_RF69_REG_58_TESTLNA 0x58 +#define RH_RF69_REG_5A_TESTPA1 0x5a +#define RH_RF69_REG_5C_TESTPA2 0x5c +#define RH_RF69_REG_6F_TESTDAGC 0x6f +#define RH_RF69_REG_71_TESTAFC 0x71 + +// These register masks etc are named wherever possible +// corresponding to the bit and field names in the RFM69 Manual + +// RH_RF69_REG_01_OPMODE +#define RH_RF69_OPMODE_SEQUENCEROFF 0x80 +#define RH_RF69_OPMODE_LISTENON 0x40 +#define RH_RF69_OPMODE_LISTENABORT 0x20 +#define RH_RF69_OPMODE_MODE 0x1c +#define RH_RF69_OPMODE_MODE_SLEEP 0x00 +#define RH_RF69_OPMODE_MODE_STDBY 0x04 +#define RH_RF69_OPMODE_MODE_FS 0x08 +#define RH_RF69_OPMODE_MODE_TX 0x0c +#define RH_RF69_OPMODE_MODE_RX 0x10 + +// RH_RF69_REG_02_DATAMODUL +#define RH_RF69_DATAMODUL_DATAMODE 0x60 +#define RH_RF69_DATAMODUL_DATAMODE_PACKET 0x00 +#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITH_SYNC 0x40 +#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITHOUT_SYNC 0x60 +#define RH_RF69_DATAMODUL_MODULATIONTYPE 0x18 +#define RH_RF69_DATAMODUL_MODULATIONTYPE_FSK 0x00 +#define RH_RF69_DATAMODUL_MODULATIONTYPE_OOK 0x08 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING 0x03 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_NONE 0x00 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT1_0 0x01 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_5 0x02 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_3 0x03 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_NONE 0x00 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_BR 0x01 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_2BR 0x02 + +// RH_RF69_REG_11_PALEVEL +#define RH_RF69_PALEVEL_PA0ON 0x80 +#define RH_RF69_PALEVEL_PA1ON 0x40 +#define RH_RF69_PALEVEL_PA2ON 0x20 +#define RH_RF69_PALEVEL_OUTPUTPOWER 0x1f + +// RH_RF69_REG_23_RSSICONFIG +#define RH_RF69_RSSICONFIG_RSSIDONE 0x02 +#define RH_RF69_RSSICONFIG_RSSISTART 0x01 + +// RH_RF69_REG_25_DIOMAPPING1 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING 0xc0 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_01 0x40 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_10 0x80 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_11 0xc0 + +#define RH_RF69_DIOMAPPING1_DIO1MAPPING 0x30 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_01 0x10 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_10 0x20 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_11 0x30 + +#define RH_RF69_DIOMAPPING1_DIO2MAPPING 0x0c +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_01 0x04 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_10 0x08 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_11 0x0c + +#define RH_RF69_DIOMAPPING1_DIO3MAPPING 0x03 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_01 0x01 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_10 0x02 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_11 0x03 + +// RH_RF69_REG_26_DIOMAPPING2 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING 0xc0 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_01 0x40 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_10 0x80 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_11 0xc0 + +#define RH_RF69_DIOMAPPING2_DIO5MAPPING 0x30 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_01 0x10 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_10 0x20 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_11 0x30 + +#define RH_RF69_DIOMAPPING2_CLKOUT 0x07 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_ 0x00 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_2 0x01 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_4 0x02 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_8 0x03 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_16 0x04 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_32 0x05 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_RC 0x06 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_OFF 0x07 + +// RH_RF69_REG_27_IRQFLAGS1 +#define RH_RF69_IRQFLAGS1_MODEREADY 0x80 +#define RH_RF69_IRQFLAGS1_RXREADY 0x40 +#define RH_RF69_IRQFLAGS1_TXREADY 0x20 +#define RH_RF69_IRQFLAGS1_PLLLOCK 0x10 +#define RH_RF69_IRQFLAGS1_RSSI 0x08 +#define RH_RF69_IRQFLAGS1_TIMEOUT 0x04 +#define RH_RF69_IRQFLAGS1_AUTOMODE 0x02 +#define RH_RF69_IRQFLAGS1_SYNADDRESSMATCH 0x01 + +// RH_RF69_REG_28_IRQFLAGS2 +#define RH_RF69_IRQFLAGS2_FIFOFULL 0x80 +#define RH_RF69_IRQFLAGS2_FIFONOTEMPTY 0x40 +#define RH_RF69_IRQFLAGS2_FIFOLEVEL 0x20 +#define RH_RF69_IRQFLAGS2_FIFOOVERRUN 0x10 +#define RH_RF69_IRQFLAGS2_PACKETSENT 0x08 +#define RH_RF69_IRQFLAGS2_PAYLOADREADY 0x04 +#define RH_RF69_IRQFLAGS2_CRCOK 0x02 + +// RH_RF69_REG_2E_SYNCCONFIG +#define RH_RF69_SYNCCONFIG_SYNCON 0x80 +#define RH_RF69_SYNCCONFIG_FIFOFILLCONDITION_MANUAL 0x40 +#define RH_RF69_SYNCCONFIG_SYNCSIZE 0x38 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_1 0x00 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_2 0x08 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_3 0x10 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_4 0x18 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_5 0x20 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_6 0x28 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_7 0x30 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_8 0x38 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_SYNCTOL 0x07 + +// RH_RF69_REG_37_PACKETCONFIG1 +#define RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80 +#define RH_RF69_PACKETCONFIG1_DCFREE 0x60 +#define RH_RF69_PACKETCONFIG1_DCFREE_NONE 0x00 +#define RH_RF69_PACKETCONFIG1_DCFREE_MANCHESTER 0x20 +#define RH_RF69_PACKETCONFIG1_DCFREE_WHITENING 0x40 +#define RH_RF69_PACKETCONFIG1_DCFREE_RESERVED 0x60 +#define RH_RF69_PACKETCONFIG1_CRC_ON 0x10 +#define RH_RF69_PACKETCONFIG1_CRCAUTOCLEAROFF 0x08 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING 0x06 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE 0x00 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE 0x02 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE_BC 0x04 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_RESERVED 0x06 + +// RH_RF69_REG_3B_AUTOMODES +#define RH_RF69_AUTOMODE_ENTER_COND_NONE 0x00 +#define RH_RF69_AUTOMODE_ENTER_COND_FIFO_NOT_EMPTY 0x20 +#define RH_RF69_AUTOMODE_ENTER_COND_FIFO_LEVEL 0x40 +#define RH_RF69_AUTOMODE_ENTER_COND_CRC_OK 0x60 +#define RH_RF69_AUTOMODE_ENTER_COND_PAYLOAD_READY 0x80 +#define RH_RF69_AUTOMODE_ENTER_COND_SYNC_ADDRESS 0xa0 +#define RH_RF69_AUTOMODE_ENTER_COND_PACKET_SENT 0xc0 +#define RH_RF69_AUTOMODE_ENTER_COND_FIFO_EMPTY 0xe0 + +#define RH_RF69_AUTOMODE_EXIT_COND_NONE 0x00 +#define RH_RF69_AUTOMODE_EXIT_COND_FIFO_EMPTY 0x04 +#define RH_RF69_AUTOMODE_EXIT_COND_FIFO_LEVEL 0x08 +#define RH_RF69_AUTOMODE_EXIT_COND_CRC_OK 0x0c +#define RH_RF69_AUTOMODE_EXIT_COND_PAYLOAD_READY 0x10 +#define RH_RF69_AUTOMODE_EXIT_COND_SYNC_ADDRESS 0x14 +#define RH_RF69_AUTOMODE_EXIT_COND_PACKET_SENT 0x18 +#define RH_RF69_AUTOMODE_EXIT_COND_TIMEOUT 0x1c + +#define RH_RF69_AUTOMODE_INTERMEDIATE_MODE_SLEEP 0x00 +#define RH_RF69_AUTOMODE_INTERMEDIATE_MODE_STDBY 0x01 +#define RH_RF69_AUTOMODE_INTERMEDIATE_MODE_RX 0x02 +#define RH_RF69_AUTOMODE_INTERMEDIATE_MODE_TX 0x03 + +// RH_RF69_REG_3C_FIFOTHRESH +#define RH_RF69_FIFOTHRESH_TXSTARTCONDITION_NOTEMPTY 0x80 +#define RH_RF69_FIFOTHRESH_FIFOTHRESHOLD 0x7f + +// RH_RF69_REG_3D_PACKETCONFIG2 +#define RH_RF69_PACKETCONFIG2_INTERPACKETRXDELAY 0xf0 +#define RH_RF69_PACKETCONFIG2_RESTARTRX 0x04 +#define RH_RF69_PACKETCONFIG2_AUTORXRESTARTON 0x02 +#define RH_RF69_PACKETCONFIG2_AESON 0x01 + +// RH_RF69_REG_4E_TEMP1 +#define RH_RF69_TEMP1_TEMPMEASSTART 0x08 +#define RH_RF69_TEMP1_TEMPMEASRUNNING 0x04 + +// RH_RF69_REG_5A_TESTPA1 +#define RH_RF69_TESTPA1_NORMAL 0x55 +#define RH_RF69_TESTPA1_BOOST 0x5d + +// RH_RF69_REG_5C_TESTPA2 +#define RH_RF69_TESTPA2_NORMAL 0x70 +#define RH_RF69_TESTPA2_BOOST 0x7c + +// RH_RF69_REG_6F_TESTDAGC +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_NORMAL 0x00 +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAON 0x20 +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAOFF 0x30 + +// Define this to include Serial printing in diagnostic routines +#define RH_RF69_HAVE_SERIAL + + +///////////////////////////////////////////////////////////////////// +/// \class RH_RF69 RH_RF69.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF69 and compatible radio transceiver. +/// +/// Works with +/// - the excellent Moteino and Moteino-USB +/// boards from LowPowerLab http://lowpowerlab.com/moteino/ +/// - compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H), +/// - RFM69 modules from http://www.hoperfusa.com such as http://www.hoperfusa.com/details.jsp?pid=145 +/// - Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including +/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range) +/// - the excellent Rocket Scream Mini Ultra Pro with the RFM69HCW +/// http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/ +/// - The excellent Talk2 Whisper Node boards +/// (https://talk2.wisen.com.au/ and https://bitbucket.org/talk2/whisper-node-avr), +/// an Arduino compatible board, which include an on-board RF69 radio, external antenna, +/// run on 2xAAA batteries and support low power operations. RF69 examples work without modification. +/// Use Arduino Board Manager to install the Talk2 code support as described in +/// https://bitbucket.org/talk2/whisper-node-avr. Upeload the code with an FTDI adapter set to 3.3V. +/// - The excellent Adafruit Feather. These are excellent boards that are available with a variety of radios. +/// We tested with the +/// Feather 32u4 with RFM69HCW radio, with Arduino IDE 1.6.8 and the Adafruit AVR Boards board manager version 1.6.10. +/// https://www.adafruit.com/products/3076 +/// +/// \par Overview +/// +/// This class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 64 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF +/// RF69B and compatible radio modules, such as the RFM69 module. +/// +/// The Hope-RF (http://www.hoperf.com) RF69 is a low-cost ISM transceiver +/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and +/// programmable data rates. It also suports AES encryption of up to 64 octets +/// of payload It is available prepackaged on modules such as the RFM69W. And +/// such modules can be prepacked on processor boards such as the Moteino from +/// LowPowerLabs (which is what we used to develop the RH_RF69 driver) +/// +/// This Driver provides functions for sending and receiving messages of up +/// to 60 octets on any frequency supported by the RF69, in a range of +/// predefined data rates and frequency deviations. Frequency can be set with +/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited +/// range of frequencies due to antenna tuning. +/// +/// Up to 2 RF69B modules can be connected to an Arduino (3 on a Mega), +/// permitting the construction of translators and frequency changers, etc. +/// +/// The following modulation types are suppported with a range of modem configurations for +/// common data rates and frequency deviations: +/// - GFSK Gaussian Frequency Shift Keying +/// - FSK Frequency Shift Keying +/// +/// Support for other RF69 features such as on-chip temperature measurement, +/// transmitter power control etc is also provided. +/// +/// Tested on USB-Moteino with arduino-1.0.5 +/// on OpenSuSE 13.1 +/// +/// \par Packet Format +/// +/// All messages sent and received by this RH_RF69 Driver conform to this packet format: +/// +/// - 4 octets PREAMBLE +/// - 2 octets SYNC 0x2d, 0xd4 (configurable, so you can use this as a network filter) +/// - 1 octet RH_RF69 payload length +/// - 4 octets HEADER: (TO, FROM, ID, FLAGS) +/// - 0 to 60 octets DATA +/// - 2 octets CRC computed with CRC16(IBM), computed on HEADER and DATA +/// +/// For technical reasons, the message format is not protocol compatible with the +/// 'HopeRF Radio Transceiver Message Library for Arduino' +/// http://www.airspayce.com/mikem/arduino/HopeRF from the same author. Nor is +/// it compatible with messages sent by 'Virtual Wire' +/// http://www.airspayce.com/mikem/arduino/VirtualWire.pdf also from the same +/// author. Nor is it compatible with messages sent by 'RF22' +/// http://www.airspayce.com/mikem/arduino/RF22 also from the same author. +/// +/// \par Connecting RFM-69 to Arduino +/// +/// We tested with Moteino, which is an Arduino Uno compatible with the RFM69W +/// module on-board. Therefore it needs no connections other than the USB +/// programming connection and an antenna to make it work. +/// +/// If you have a bare RFM69W that you want to connect to an Arduino, you +/// might use these connections: CAUTION: you must use a 3.3V type +/// Arduino, otherwise you will also need voltage level shifters between the +/// Arduino and the RFM69. CAUTION, you must also ensure you connect an +/// antenna +/// +/// \code +/// Arduino RFM69W +/// GND----------GND (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin D2-----------DIO0 (interrupt request out) +/// SS pin D10----------NSS (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------MOSI (SPI Data in) +/// MISO pin D12----------MISO (SPI Data out) +/// \endcode +/// +/// For Arduino Due, use these connections: +/// \code +/// Arduino RFM69W +/// GND----------GND (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin D2-----------DIO0 (interrupt request out) +/// SS pin D10----------NSS (chip select in) +/// SCK SPI pin 3----------SCK (SPI clock in) +/// MOSI SPI pin 4----------MOSI (SPI Data in) +/// MISO SPI pin 1----------MISO (SPI Data out) +/// \endcode +/// +/// With these connections, you can then use the default constructor RH_RF69(). +/// You can override the default settings for the SS pin and the interrupt in +/// the RH_RF69 constructor if you wish to connect the slave select SS to other +/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53 +/// for Mega) or the interrupt request to other than pin D2 (Caution, +/// different processors have different constraints as to the pins available +/// for interrupts). +/// +/// If you have a Teensy 3.1 and a compatible RFM69 breakout board, you will need to +/// construct the RH_RF69 instance like this: +/// \code +/// RH_RF69 driver(15, 16); +/// \endcode +/// +/// If you have a MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +/// with RFM69 on board, you dont need to make any wiring connections +/// (the RFM69 module is soldered onto the MotienoMEGA), but you must initialise the RH_RF69 +/// constructor like this: +/// \code +/// RH_RF69 driver(4, 2); +/// \endcode +/// Make sure you have the MoteinoMEGA core installed in your Arduino hardware folder as described in the +/// documentation for the MoteinoMEGA. +/// +/// If you have an Arduino M0 Pro from arduino.org, +/// you should note that you cannot use Pin 2 for the interrupt line +/// (Pin 2 is for the NMI only). The same comments apply to Pin 4 on Arduino Zero from arduino.cc. +/// Instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this: +/// \code +/// // Slave Select is pin 10, interrupt is Pin 3 +/// RH_RF69 driver(10, 3); +/// \endcode +/// +/// If you have a Rocket Scream Mini Ultra Pro with the RFM69HCW +/// - Ensure you have Arduino SAMD board support 1.6.5 or later in Arduino IDE 1.6.8 or later. +/// - The radio SS is hardwired to pin D5 and the DIO0 interrupt to pin D2, +/// so you need to initialise the radio like this: +/// \code +/// RH_RF69 driver(5, 2); +/// \endcode +/// - The name of the serial port on that board is 'SerialUSB', not 'Serial', so this may be helpful at the top of our +/// sample sketches: +/// \code +/// #define Serial SerialUSB +/// \endcode +/// - You also need this in setup before radio initialisation +/// \code +/// // Ensure serial flash is not interfering with radio communication on SPI bus +/// pinMode(4, OUTPUT); +/// digitalWrite(4, HIGH); +/// \endcode +/// - and if you have a 915MHz part, you need this after driver/manager intitalisation: +/// \code +/// rf69.setFrequency(915.0); +/// rf69.setTxPower(20); +/// \endcode +/// which adds up to modifying sample sketches something like: +/// \code +/// #include +/// #include +/// RH_RF69 rf69(5, 2); // Rocket Scream Mini Ultra Pro with the RFM69HCW +/// #define Serial SerialUSB +/// +/// void setup() +/// { +/// // Ensure serial flash is not interfering with radio communication on SPI bus +/// pinMode(4, OUTPUT); +/// digitalWrite(4, HIGH); +/// +/// Serial.begin(9600); +/// while (!Serial) ; // Wait for serial port to be available +/// if (!rf69.init()) +/// Serial.println("init failed"); +/// rf69.setFrequency(915.0); +/// rf69.setTxPower(20); +/// } +/// ... +/// \endcode +/// +/// If you have a talk2 Whisper Node board with on-board RF69 radio, +/// the example rf69_* sketches work without modifications. Initialise the radio like +/// with the default constructor: +/// \code +/// RH_RF69 driver; +/// \endcode +/// +/// If you have a Feather 32u4 with RFM69HCW you need to initialise the driver like: +/// \code +/// RH_RF69 driver(8, 7); +/// \endcode +/// and since the radio is the high power HCW model, you must set the Tx power in the +/// range 14 to 20 like this: +/// \code +/// driver.setTxPower(14); +/// \endcode +/// +/// If you are connecting an RF69 to a ESP8266 board breakout board that exposes pins +/// 12, 13, 14, 15 (ie NOT an ESP-01) you can connect like this: +/// \code +/// ESP8266 RFM69W +/// GND-----------GND (ground in) +/// VIN-----------3.3V (3.3V in) +/// interrupt D0 pin GPIO0-----------DIO0 (interrupt request out) +/// SS pin GPIO15----------NSS (chip select in) +/// SCK SPI pin GPIO14----------SCK (SPI clock in) +/// MOSI SPI pin GPIO13----------MOSI (SPI Data in) +/// MISO SPI pin GPIO12----------MISO (SPI Data out) +/// \endcode +/// and initialise with +/// \code +/// RH_RF69 driver(15, 0); +/// \endcode +/// If you are connecting an RF69 to a Sparkfun nRF52832 Breakout board +/// with Arduino 1.8.9 with board: +/// "SparkFun nRF52 Boards by Sparkfun Electronics version 0.2.3", +/// you can connect like this: +/// \code +/// nRF52832 RFM69W +/// GND----------GND (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin 02-----------DIO0 (interrupt request out) +/// SS pin 08-----------NSS (chip select in) +/// SCK SPI pin 13-----------SCK (SPI clock in) +/// MOSI SPI pin 11-----------MOSI (SPI Data in) +/// MISO SPI pin 12-----------MISO (SPI Data out) +/// \endcode +/// and initialise with +/// \code +/// RHSoftwareSPI softwarespi; +/// RH_RF69 driver(8, 2, softwarespi); +/// and inside your setup() function: +/// softwarespi.setPins(12, 11, 13); +/// \endcode +/// +/// It is possible to have 2 or more radios connected to one Arduino, provided +/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common +/// to all radios) +/// +/// Caution: on some Arduinos such as the Mega 2560, if you set the slave +/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may +/// need to set the usual SS pin to be an output to force the Arduino into SPI +/// master mode. +/// +/// Caution: Power supply requirements of the RF69 module may be relevant in some circumstances: +/// RF69 modules are capable of pulling 45mA+ at full power, where Arduino's 3.3V line can +/// give 50mA. You may need to make provision for alternate power supply for +/// the RF69, especially if you wish to use full transmit power, and/or you have +/// other shields demanding power. Inadequate power for the RF69 is likely to cause symptoms such as: +/// -reset's/bootups terminate with "init failed" messages +/// -random termination of communication after 5-30 packets sent/received +/// -"fake ok" state, where initialization passes fluently, but communication doesn't happen +/// -shields hang Arduino boards, especially during the flashing +/// +/// \par Encryption +/// +/// This driver support the on-chip AES encryption provided by the RF69. +/// You can enable encryption by calling setEncryptionKey() after init() has been called. +/// If both transmitter and receiver have been configured with the same AES key, +/// then the receiver will recover the unencrypted message sent by the receiver. +/// However, you should note that there is no way for RF69 nor for the RadioHead +/// drivers to know whether the AES +/// key for a message is 'correct' or not. This is because the RF69 CRC covers the +/// _encrypted_ payload not the plaintext. +/// +/// In RadioHead managers that support addressing, +/// the RF69 AES encryption includes the RadioHead payload and the TO and FROM addresses, so +/// occasionally (average one in 256 messages), a message encrypted with the +/// 'wrong' key will have the 'correct' destination address, and will therefore be +/// accepted by RadioHead as a 'random' message content from a 'random' sender. +/// Its up to your code to figure out whether the message makes sense or not. +/// +/// \par Interrupts +/// +/// The RH_RF69 driver uses interrupts to react to events in the RF69 module, +/// such as the reception of a new packet, or the completion of transmission +/// of a packet. The RH_RF69 driver interrupt service routine reads status from +/// and writes data to the the RF69 module via the SPI interface. It is very +/// important therefore, that if you are using the RH_RF69 driver with another +/// SPI based deviced, that you disable interrupts while you transfer data to +/// and from that other device. Use cli() to disable interrupts and sei() to +/// reenable them. +/// +/// \par Memory +/// +/// The RH_RF69 driver requires non-trivial amounts of memory. The sample +/// programs above all compile to about 8kbytes each, which will fit in the +/// flash proram memory of most Arduinos. However, the RAM requirements are +/// more critical. Therefore, you should be vary sparing with RAM use in +/// programs that use the RH_RF69 driver. +/// +/// It is often hard to accurately identify when you are hitting RAM limits on Arduino. +/// The symptoms can include: +/// - Mysterious crashes and restarts +/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements) +/// - Hanging +/// - Output from Serial.print() not appearing +/// +/// \par Automatic Frequency Control (AFC) +/// +/// The RF69 module is configured by the RH_RF69 driver to always use AFC. +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power on the RF69 transceiver +/// with the RH_RF69::setTxPower() function. The argument can be any of +/// -18 to +13 (for RF69W) or -14 to 20 (for RF69HW) +/// The default is 13. Eg: +/// \code +/// driver.setTxPower(-5); +/// \endcode +/// +/// We have made some actual power measurements against +/// programmed power for Moteino (with RF69W) +/// - Moteino (with RF69W), USB power +/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND +/// - bnc connecteor +/// - 12dB attenuator +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// -18 -17 +/// -16 -16 +/// -14 -14 +/// -12 -12 +/// -10 -9 +/// -8 -7 +/// -6 -4 +/// -4 -3 +/// -2 -2 +/// 0 0.2 +/// 2 3 +/// 4 5 +/// 6 7 +/// 8 10 +/// 10 13 +/// 12 14 +/// 13 15 +/// 14 -51 +/// 20 -51 +/// \endcode +/// We have also made some actual power measurements against +/// programmed power for Anarduino MiniWireless with RFM69-HW +/// Anarduino MiniWireless (with RFM69-HW), USB power +/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND +/// - bnc connecteor +/// - 2x12dB attenuators +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// -18 no measurable output +/// 0 no measurable output +/// 13 no measurable output +/// 14 11 +/// 15 12 +/// 16 12.4 +/// 17 14 +/// 18 15 +/// 19 15.8 +/// 20 17 +/// \endcode +/// (Caution: we dont claim laboratory accuracy for these measurements) +/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna. +/// Caution: although the RFM69 appears to have a PC antenna on board, you will get much better power and range even +/// with just a 1/4 wave wire antenna. +/// +/// \par Performance +/// +/// Some simple speed performance tests have been conducted. +/// In general packet transmission rate will be limited by the modulation scheme. +/// Also, if your code does any slow operations like Serial printing it will also limit performance. +/// We disabled any printing in the tests below. +/// We tested with RH_RF69::GFSK_Rb250Fd250, which is probably the fastest scheme available. +/// We tested with a 13 octet message length, over a very short distance of 10cm. +/// +/// Transmission (no reply) tests with modulation RH_RF69::GFSK_Rb250Fd250 and a +/// 13 octet message show about 152 messages per second transmitted and received. +/// +/// Transmit-and-wait-for-a-reply tests with modulation RH_RF69::GFSK_Rb250Fd250 and a +/// 13 octet message (send and receive) show about 68 round trips per second. +/// +class RH_RF69 : public RHSPIDriver +{ +public: + + /// \brief Defines register values for a set of modem configuration registers + /// + /// Defines register values for a set of modem configuration registers + /// that can be passed to setModemRegisters() if none of the choices in + /// ModemConfigChoice suit your need setModemRegisters() writes the + /// register values from this structure to the appropriate RF69 registers + /// to set the desired modulation type, data rate and deviation/bandwidth. + typedef struct + { + uint8_t reg_02; ///< Value for register RH_RF69_REG_02_DATAMODUL + uint8_t reg_03; ///< Value for register RH_RF69_REG_03_BITRATEMSB + uint8_t reg_04; ///< Value for register RH_RF69_REG_04_BITRATELSB + uint8_t reg_05; ///< Value for register RH_RF69_REG_05_FDEVMSB + uint8_t reg_06; ///< Value for register RH_RF69_REG_06_FDEVLSB + uint8_t reg_19; ///< Value for register RH_RF69_REG_19_RXBW + uint8_t reg_1a; ///< Value for register RH_RF69_REG_1A_AFCBW + uint8_t reg_37; ///< Value for register RH_RF69_REG_37_PACKETCONFIG1 + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common + /// modulation types, and data rates. If you need another configuration, + /// use the register calculator. and call setModemRegisters() with your + /// desired settings. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + /// CAUTION: some of these configurations do not work corectly and are marked as such. + typedef enum + { + FSK_Rb2Fd5 = 0, ///< FSK, Whitening, Rb = 2kbs, Fd = 5kHz + FSK_Rb2_4Fd4_8, ///< FSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz + FSK_Rb4_8Fd9_6, ///< FSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz + FSK_Rb9_6Fd19_2, ///< FSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz + FSK_Rb19_2Fd38_4, ///< FSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz + FSK_Rb38_4Fd76_8, ///< FSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz + FSK_Rb57_6Fd120, ///< FSK, Whitening, Rb = 57.6kbs, Fd = 120kHz + FSK_Rb125Fd125, ///< FSK, Whitening, Rb = 125kbs, Fd = 125kHz + FSK_Rb250Fd250, ///< FSK, Whitening, Rb = 250kbs, Fd = 250kHz + FSK_Rb55555Fd50, ///< FSK, Whitening, Rb = 55555kbs,Fd = 50kHz for RFM69 lib compatibility + + GFSK_Rb2Fd5, ///< GFSK, Whitening, Rb = 2kbs, Fd = 5kHz + GFSK_Rb2_4Fd4_8, ///< GFSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz + GFSK_Rb4_8Fd9_6, ///< GFSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz + GFSK_Rb9_6Fd19_2, ///< GFSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz + GFSK_Rb19_2Fd38_4, ///< GFSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz + GFSK_Rb38_4Fd76_8, ///< GFSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz + GFSK_Rb57_6Fd120, ///< GFSK, Whitening, Rb = 57.6kbs, Fd = 120kHz + GFSK_Rb125Fd125, ///< GFSK, Whitening, Rb = 125kbs, Fd = 125kHz + GFSK_Rb250Fd250, ///< GFSK, Whitening, Rb = 250kbs, Fd = 250kHz + GFSK_Rb55555Fd50, ///< GFSK, Whitening, Rb = 55555kbs,Fd = 50kHz + + OOK_Rb1Bw1, ///< OOK, Whitening, Rb = 1kbs, Rx Bandwidth = 1kHz. + OOK_Rb1_2Bw75, ///< OOK, Whitening, Rb = 1.2kbs, Rx Bandwidth = 75kHz. + OOK_Rb2_4Bw4_8, ///< OOK, Whitening, Rb = 2.4kbs, Rx Bandwidth = 4.8kHz. + OOK_Rb4_8Bw9_6, ///< OOK, Whitening, Rb = 4.8kbs, Rx Bandwidth = 9.6kHz. + OOK_Rb9_6Bw19_2, ///< OOK, Whitening, Rb = 9.6kbs, Rx Bandwidth = 19.2kHz. + OOK_Rb19_2Bw38_4, ///< OOK, Whitening, Rb = 19.2kbs, Rx Bandwidth = 38.4kHz. + OOK_Rb32Bw64, ///< OOK, Whitening, Rb = 32kbs, Rx Bandwidth = 64kHz. + +// Test, + } ModemConfigChoice; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RF69 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the RF69 DIO0 interrupt line. + /// Defaults to pin 2. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On Arduino Zero from arduino.cc, any digital pin other than 4. + /// On Arduino M0 Pro from arduino.org, any digital pin other than 2. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_RF69(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken: + /// - Initialise the slave select pin and the SPI interface library + /// - Checks the connected RF69 module can be communicated + /// - Attaches an interrupt handler + /// - Configures the RF69 module + /// - Sets the frequency to 434.0 MHz + /// - Sets the modem data rate to FSK_Rb2Fd5 + /// \return true if everything was successful + bool init(); + + /// Reads the on-chip temperature sensor. + /// The RF69 must be in Idle mode (= RF69 Standby) to measure temperature. + /// The measurement is uncalibrated and without calibration, you can expect it to be far from + /// correct. + /// \return The measured temperature, in degrees C from -40 to 85 (uncalibrated) + int8_t temperatureRead(); + + /// Sets the transmitter and receiver + /// centre frequency + /// \param[in] centre Frequency in MHz. 240.0 to 960.0. Caution, RF69 comes in several + /// different frequency ranges, and setting a frequency outside that range of your radio will probably not work + /// \param[in] afcPullInRange Not used + /// \return true if the selected frquency centre is within range + bool setFrequency(float centre, float afcPullInRange = 0.05); + + /// Reads and returns the current RSSI value. + /// Causes the current signal strength to be measured and returned + /// If you want to find the RSSI + /// of the last received message, use lastRssi() instead. + /// \return The current RSSI value on units of 0.5dB. + int8_t rssiRead(); + + /// Sets the parameters for the RF69 OPMODE. + /// This is a low level device access function, and should not normally ned to be used by user code. + /// Instead can use stModeRx(), setModeTx(), setModeIdle() + /// \param[in] mode RF69 OPMODE to set, one of RH_RF69_OPMODE_MODE_*. + void setOpMode(uint8_t mode); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the RF69. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the RF69. + void setModeTx(); + + /// Sets the transmitter power output level. + /// Be a good neighbour and set the lowest power level you need. + /// Caution: legal power limits may apply in certain countries. + /// After init(), the power will be set to 13dBm for a low power module. + /// If you are using a high p[ower modfule such as an RFM69HW, you MUST set the power level + /// with the ishighpowermodule flag set to true. Else you wil get no measurable power output. + /// Simlarly if you are not using a high power module, you must NOT set the ishighpowermodule + /// (which is the default) + /// \param[in] power Transmitter power level in dBm. For RF69W (ishighpowermodule = false), + /// valid values are from -18 to +13.; Values outside this range are trimmed. + /// For RF69HW (ishighpowermodule = true), valid values are from -2 to +20. + /// Caution: at +20dBm, duty cycle is limited to 1% and a + /// maximum VSWR of 3:1 at the antenna port. + /// \param ishighpowermodule Set to true if the connected module is a high power module RFM69HW + void setTxPower(int8_t power, bool ishighpowermodule = RH_RF69_DEFAULT_HIGHPOWER); + + /// Sets all the registers required to configure the data modem in the RF69, including the data rate, + /// bandwidths etc. You can use this to configure the modem with custom configurations if none of the + /// canned configurations in ModemConfigChoice suit you. + /// \param[in] config A ModemConfig structure containing values for the modem configuration registers. + void setModemRegisters(const ModemConfig* config); + + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. The default after init() is RH_RF69::GFSK_Rb250Fd250. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Starts the receiver and checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + bool send(const uint8_t* data, uint8_t len); + + /// Sets the length of the preamble + /// in bytes. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 4. + /// Sets the message preamble length in REG_0?_PREAMBLE?SB + /// \param[in] bytes Preamble length in bytes. + void setPreambleLength(uint16_t bytes); + + /// Sets the sync words for transmit and receive + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0x2d, 0xd4 }. + /// Caution: tests here show that with a single sync word (ie where len == 1), + /// RFM69 reception can be unreliable. + /// To disable sync word generation and detection, call with the defaults: setSyncWords(); + /// \param[in] syncWords Array of sync words, 1 to 4 octets long. NULL if no sync words to be used. + /// \param[in] len Number of sync words to set, 1 to 4. 0 if no sync words to be used. + void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0); + + /// Enables AES encryption and sets the AES encryption key, used + /// to encrypt and decrypt all messages. The default is disabled. + /// \param[in] key The key to use. Must be 16 bytes long. The same key must be installed + /// in other instances of RF69, otherwise communications will not work correctly. If key is NULL, + /// encryption is disabled, which is the default. + void setEncryptionKey(uint8_t* key = NULL); + + /// Returns the time in millis since the most recent preamble was received, and when the most recent + /// RSSI measurement was made. + uint32_t getLastPreambleTime(); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Prints the value of a single register + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging/testing only + /// \return true if successful + bool printRegister(uint8_t reg); + + /// Prints the value of all the RF69 registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging/testing only + /// \return true if successful + bool printRegisters(); + + /// Sets the radio operating mode for the case when the driver is idle (ie not + /// transmitting or receiving), allowing you to control the idle mode power requirements + /// at the expense of slower transitions to transmit and receive modes. + /// By default, the idle mode is RH_RF69_OPMODE_MODE_STDBY, + /// but eg setIdleMode(RH_RF69_OPMODE_MODE_SLEEP) will provide a much lower + /// idle current but slower transitions. Call this function after init(). + /// \param[in] idleMode The chip operating mode to use when the driver is idle. One of RH_RF69_OPMODE_* + void setIdleMode(uint8_t idleMode); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + + /// Return the integer value of the device type + /// as read from the device in from RH_RF69_REG_10_VERSION. + /// Expect 0x24, depending on the type of device actually + /// connected. + /// \return The integer device type + uint16_t deviceType() {return _deviceType;}; + +protected: + /// This is a low level function to handle the interrupts for one instance of RF69. + /// Called automatically by isr*() + /// Should not need to be called by user code. + void handleInterrupt(); + + /// Low level function to read the FIFO and put the received data into the receive buffer + /// Should not need to be called by user code. + void readFifo(); + +protected: + /// Low level interrupt service routine for RF69 connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for RF69 connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for RF69 connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_RF69* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// The radio OP mode to use when mode is RHModeIdle + uint8_t _idleMode; + + /// The reported device type + uint8_t _deviceType; + + /// The selected output power in dBm + int8_t _power; + + /// The message length in _buf + volatile uint8_t _bufLen; + + /// Array of octets of teh last received message or the next to transmit message + uint8_t _buf[RH_RF69_MAX_MESSAGE_LEN]; + + /// True when there is a valid message in the Rx buffer + volatile bool _rxBufValid; + + /// Time in millis since the last preamble was received (and the last time the RSSI was measured) + uint32_t _lastPreambleTime; +}; + +/// @example rf69_client.pde +/// @example rf69_server.pde +/// @example rf69_reliable_datagram_client.pde +/// @example rf69_reliable_datagram_server.pde + + +#endif diff --git a/RH_RF95.cpp b/RH_RF95.cpp new file mode 100644 index 0000000..f999d87 --- /dev/null +++ b/RH_RF95.cpp @@ -0,0 +1,679 @@ +// RH_RF95.cpp +// +// Copyright (C) 2011 Mike McCauley +// $Id: RH_RF95.cpp,v 1.27 2020/07/05 08:52:21 mikem Exp mikem $ + +#include + +// Interrupt vectors for the 3 Arduino interrupt pins +// Each interrupt can be handled by a different instance of RH_RF95, allowing you to have +// 2 or more LORAs per Arduino +RH_RF95* RH_RF95::_deviceForInterrupt[RH_RF95_NUM_INTERRUPTS] = {0, 0, 0}; +uint8_t RH_RF95::_interruptCount = 0; // Index into _deviceForInterrupt for next device + +// These are indexed by the values of ModemConfigChoice +// Stored in flash (program) memory to save SRAM +PROGMEM static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] = +{ + // 1d, 1e, 26 + { 0x72, 0x74, 0x04}, // Bw125Cr45Sf128 (the chip default), AGC enabled + { 0x92, 0x74, 0x04}, // Bw500Cr45Sf128, AGC enabled + { 0x48, 0x94, 0x04}, // Bw31_25Cr48Sf512, AGC enabled + { 0x78, 0xc4, 0x0c}, // Bw125Cr48Sf4096, AGC enabled + { 0x72, 0xb4, 0x04}, // Bw125Cr45Sf2048, AGC enabled + +}; + +RH_RF95::RH_RF95(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi) + : + RHSPIDriver(slaveSelectPin, spi), + _rxBufValid(0) +{ + _interruptPin = interruptPin; + _myInterruptIndex = 0xff; // Not allocated yet + _enableCRC = true; + _useRFO = false; +} + +bool RH_RF95::init() +{ + if (!RHSPIDriver::init()) + return false; + + // For some subclasses (eg RH_ABZ) we dont want to set up interrupt + int interruptNumber = NOT_AN_INTERRUPT; + if (_interruptPin != RH_INVALID_PIN) + { + // Determine the interrupt number that corresponds to the interruptPin + interruptNumber = digitalPinToInterrupt(_interruptPin); + if (interruptNumber == NOT_AN_INTERRUPT) + return false; +#ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER + interruptNumber = _interruptPin; +#endif + + // Tell the low level SPI interface we will use SPI within this interrupt + spiUsingInterrupt(interruptNumber); + } + + // No way to check the device type :-( + + // Set sleep mode, so we can also set LORA mode: + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE); + delay(10); // Wait for sleep mode to take over from say, CAD + // Check we are in sleep mode, with LORA set + if (spiRead(RH_RF95_REG_01_OP_MODE) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE)) + { +// Serial.println(spiRead(RH_RF95_REG_01_OP_MODE), HEX); + return false; // No device present? + } + + + if (_interruptPin != RH_INVALID_PIN) + { + // Add by Adrien van den Bossche 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 actually 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_RF95_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 + } + + // Set up FIFO + // We configure so that we can use the entire 256 byte FIFO for either receive + // or transmit, but not both at the same time + spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0); + spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0); + + // Packet format is preamble + explicit-header + payload + crc + // Explicit Header Mode + // payload is TO + FROM + ID + FLAGS + message data + // RX mode is implmented with RXCONTINUOUS + // max message data length is 255 - 4 = 251 octets + + setModeIdle(); + + // Set up default configuration + // No Sync Words in LORA mode. + setModemConfig(Bw125Cr45Sf128); // Radio default +// setModemConfig(Bw125Cr48Sf4096); // slow and reliable? + setPreambleLength(8); // Default is 8 + // An innocuous ISM frequency, same as RF22's + setFrequency(434.0); + // Lowish power + setTxPower(13); + + return true; +} + +// C++ level interrupt handler for this instance +// LORA is unusual in that it has several interrupt lines, and not a single, combined one. +// On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly +// connnected to the processor. +// We use this to get RxDone and TxDone interrupts +void RH_RF95::handleInterrupt() +{ + // we need the RF95 IRQ to be level triggered, or we ……have slim chance of missing events + // https://github.com/geeksville/Meshtastic-esp32/commit/78470ed3f59f5c84fbd1325bcff1fd95b2b20183 + + // Read the interrupt register + uint8_t irq_flags = spiRead(RH_RF95_REG_12_IRQ_FLAGS); + // Read the RegHopChannel register to check if CRC presence is signalled + // in the header. If not it might be a stray (noise) packet.* + uint8_t hop_channel = spiRead(RH_RF95_REG_1C_HOP_CHANNEL); +// Serial.println(irq_flags, HEX); +// Serial.println(_mode, HEX); +// Serial.println(hop_channel, HEX); +// Serial.println(_enableCRC, HEX); + + // ack all interrupts, + // Sigh: on some processors, for some unknown reason, doing this only once does not actually + // clear the radio's interrupt flag. So we do it twice. Why? (kevinh - I think the root cause we want level + // triggered interrupts here - not edge. Because edge allows us to miss handling secondard interrupts that occurred + // while this ISR was running. Better to instead, configure the interrupts as level triggered and clear pending + // at the _beginning_ of the ISR. If any interrupts occur while handling the ISR, the signal will remain asserted and + // our ISR will be reinvoked to handle that case) + // kevinh: turn this off until root cause is known, because it can cause missed interrupts! + // spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags + spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags + + // error if: + // timeout + // bad CRC + // CRC is required but it is not present + if (_mode == RHModeRx + && ( (irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR)) + || (_enableCRC && !(hop_channel & RH_RF95_RX_PAYLOAD_CRC_IS_ON)) )) +// if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR)) + { +// Serial.println("E"); + _rxBad++; + clearRxBuf(); + } + // It is possible to get RX_DONE and CRC_ERROR and VALID_HEADER all at once + // so this must be an else + else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE) + { + // Packet received, no CRC error +// Serial.println("R"); + // Have received a packet + uint8_t len = spiRead(RH_RF95_REG_13_RX_NB_BYTES); + + // Reset the fifo read ptr to the beginning of the packet + spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)); + spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len); + _bufLen = len; + + // Remember the last signal to noise ratio, LORA mode + // Per page 111, SX1276/77/78/79 datasheet + _lastSNR = (int8_t)spiRead(RH_RF95_REG_19_PKT_SNR_VALUE) / 4; + + // Remember the RSSI of this packet, LORA mode + // this is according to the doc, but is it really correct? + // weakest receiveable signals are reported RSSI at about -66 + _lastRssi = spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE); + // Adjust the RSSI, datasheet page 87 + if (_lastSNR < 0) + _lastRssi = _lastRssi + _lastSNR; + else + _lastRssi = (int)_lastRssi * 16 / 15; + if (_usingHFport) + _lastRssi -= 157; + else + _lastRssi -= 164; + + // We have received a message. + validateRxBuf(); + if (_rxBufValid) + setModeIdle(); // Got one + } + else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE) + { +// Serial.println("T"); + _txGood++; + setModeIdle(); + } + else if (_mode == RHModeCad && irq_flags & RH_RF95_CAD_DONE) + { +// Serial.println("C"); + _cad = irq_flags & RH_RF95_CAD_DETECTED; + setModeIdle(); + } + else + { +// Serial.println("?"); + } + + // Sigh: on some processors, for some unknown reason, doing this only once does not actually + // clear the radio's interrupt flag. So we do it twice. Why? +// spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags +// spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff); // Clear all IRQ flags +} + +// These are low level functions that call the interrupt handler for the correct +// instance of RH_RF95. +// 3 interrupts allows us to have 3 different devices +void RH_INTERRUPT_ATTR RH_RF95::isr0() +{ + if (_deviceForInterrupt[0]) + _deviceForInterrupt[0]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF95::isr1() +{ + if (_deviceForInterrupt[1]) + _deviceForInterrupt[1]->handleInterrupt(); +} +void RH_INTERRUPT_ATTR RH_RF95::isr2() +{ + if (_deviceForInterrupt[2]) + _deviceForInterrupt[2]->handleInterrupt(); +} + +// Check whether the latest received message is complete and uncorrupted +void RH_RF95::validateRxBuf() +{ + if (_bufLen < 4) + return; // Too short to be a real message + // Extract the 4 headers + _rxHeaderTo = _buf[0]; + _rxHeaderFrom = _buf[1]; + _rxHeaderId = _buf[2]; + _rxHeaderFlags = _buf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +bool RH_RF95::available() +{ + if (_mode == RHModeTx) + return false; + setModeRx(); + return _rxBufValid; // Will be set by the interrupt handler when a good message is received +} + +void RH_RF95::clearRxBuf() +{ + ATOMIC_BLOCK_START; + _rxBufValid = false; + _bufLen = 0; + ATOMIC_BLOCK_END; +} + +bool RH_RF95::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + ATOMIC_BLOCK_START; + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _bufLen-RH_RF95_HEADER_LEN) + *len = _bufLen-RH_RF95_HEADER_LEN; + memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len); + ATOMIC_BLOCK_END; + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +bool RH_RF95::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_RF95_MAX_MESSAGE_LEN) + return false; + + waitPacketSent(); // Make sure we dont interrupt an outgoing message + setModeIdle(); + + if (!waitCAD()) + return false; // Check channel activity + + // Position at the beginning of the FIFO + spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0); + // The headers + spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo); + spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom); + spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId); + spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags); + // The message data + spiBurstWrite(RH_RF95_REG_00_FIFO, data, len); + spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN); + + setModeTx(); // Start the transmitter + // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY + return true; +} + +bool RH_RF95::printRegisters() +{ +#ifdef RH_HAVE_SERIAL + uint8_t registers[] = { 0x01, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x4b}; + + uint8_t i; + for (i = 0; i < sizeof(registers); i++) + { + Serial.print(registers[i], HEX); + Serial.print(": "); + Serial.println(spiRead(registers[i]), HEX); + } +#endif + return true; +} + +uint8_t RH_RF95::maxMessageLength() +{ + return RH_RF95_MAX_MESSAGE_LEN; +} + +bool RH_RF95::setFrequency(float centre) +{ + // Frf = FRF / FSTEP + uint32_t frf = (centre * 1000000.0) / RH_RF95_FSTEP; + spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff); + spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff); + spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff); + _usingHFport = (centre >= 779.0); + + return true; +} + +void RH_RF95::setModeIdle() +{ + if (_mode != RHModeIdle) + { + modeWillChange(RHModeIdle); + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY); + _mode = RHModeIdle; + } +} + +bool RH_RF95::sleep() +{ + if (_mode != RHModeSleep) + { + modeWillChange(RHModeSleep); + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP); + _mode = RHModeSleep; + } + return true; +} + +void RH_RF95::setModeRx() +{ + if (_mode != RHModeRx) + { + modeWillChange(RHModeRx); + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS); + spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone + _mode = RHModeRx; + } +} + +void RH_RF95::setModeTx() +{ + if (_mode != RHModeTx) + { + modeWillChange(RHModeTx); + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX); + spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone + _mode = RHModeTx; + } +} + +void RH_RF95::setTxPower(int8_t power, bool useRFO) +{ + _useRFO = useRFO; + + // Sigh, different behaviours depending on whether the module use PA_BOOST or the RFO pin + // for the transmitter output + if (useRFO) + { + if (power > 15) + power = 15; + if (power < 0) + power = 0; + // Set the MaxPower register to 0x7 => MaxPower = 10.8 + 0.6 * 7 = 15dBm + // So Pout = Pmax - (15 - power) = 15 - 15 + power + spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_MAX_POWER | power); + spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE); + } + else + { + if (power > 20) + power = 20; + if (power < 2) + power = 2; + + // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf' + // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will use it + // for 8, 19 and 20dBm + if (power > 17) + { + spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE); + power -= 3; + } + else + { + spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE); + } + + // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST + // pin is connected, so must use PA_BOOST + // Pout = 2 + OutputPower (+3dBm if DAC enabled) + spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-2)); + } +} + +// Sets registers from a canned modem configuration structure +void RH_RF95::setModemRegisters(const ModemConfig* config) +{ + spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, config->reg_1d); + spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, config->reg_1e); + spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, config->reg_26); +} + +// Set one of the canned FSK Modem configs +// Returns true if its a valid choice +bool RH_RF95::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_RF95::ModemConfig)); + setModemRegisters(&cfg); + + return true; +} + +void RH_RF95::setPreambleLength(uint16_t bytes) +{ + spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8); + spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff); +} + +bool RH_RF95::isChannelActive() +{ + // Set mode RHModeCad + if (_mode != RHModeCad) + { + modeWillChange(RHModeCad); + spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_CAD); + spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x80); // Interrupt on CadDone + _mode = RHModeCad; + } + + while (_mode == RHModeCad) + YIELD; + + return _cad; +} + +void RH_RF95::enableTCXO(bool on) +{ + if (on) + { + while ((spiRead(RH_RF95_REG_4B_TCXO) & RH_RF95_TCXO_TCXO_INPUT_ON) != RH_RF95_TCXO_TCXO_INPUT_ON) + { + sleep(); + spiWrite(RH_RF95_REG_4B_TCXO, (spiRead(RH_RF95_REG_4B_TCXO) | RH_RF95_TCXO_TCXO_INPUT_ON)); + } + } + else + { + while ((spiRead(RH_RF95_REG_4B_TCXO) & RH_RF95_TCXO_TCXO_INPUT_ON)) + { + sleep(); + spiWrite(RH_RF95_REG_4B_TCXO, (spiRead(RH_RF95_REG_4B_TCXO) & ~RH_RF95_TCXO_TCXO_INPUT_ON)); + } + } +} + +// From section 4.1.5 of SX1276/77/78/79 +// Ferror = FreqError * 2**24 * BW / Fxtal / 500 +int RH_RF95::frequencyError() +{ + int32_t freqerror = 0; + + // Convert 2.5 bytes (5 nibbles, 20 bits) to 32 bit signed int + // Caution: some C compilers make errors with eg: + // freqerror = spiRead(RH_RF95_REG_28_FEI_MSB) << 16 + // so we go more carefully. + freqerror = spiRead(RH_RF95_REG_28_FEI_MSB); + freqerror <<= 8; + freqerror |= spiRead(RH_RF95_REG_29_FEI_MID); + freqerror <<= 8; + freqerror |= spiRead(RH_RF95_REG_2A_FEI_LSB); + // Sign extension into top 3 nibbles + if (freqerror & 0x80000) + freqerror |= 0xfff00000; + + int error = 0; // In hertz + float bw_tab[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250, 500}; + uint8_t bwindex = spiRead(RH_RF95_REG_1D_MODEM_CONFIG1) >> 4; + if (bwindex < (sizeof(bw_tab) / sizeof(float))) + error = (float)freqerror * bw_tab[bwindex] * ((float)(1L << 24) / (float)RH_RF95_FXOSC / 500.0); + // else not defined + + return error; +} + +int RH_RF95::lastSNR() +{ + return _lastSNR; +} + + /////////////////////////////////////////////////// + // + // additions below by Brian Norman 9th Nov 2018 + // brian.n.norman@gmail.com + // + // Routines intended to make changing BW, SF and CR + // a bit more intuitive + // + /////////////////////////////////////////////////// + + void RH_RF95::setSpreadingFactor(uint8_t sf) + { + if (sf <= 6) + sf = RH_RF95_SPREADING_FACTOR_64CPS; + else if (sf == 7) + sf = RH_RF95_SPREADING_FACTOR_128CPS; + else if (sf == 8) + sf = RH_RF95_SPREADING_FACTOR_256CPS; + else if (sf == 9) + sf = RH_RF95_SPREADING_FACTOR_512CPS; + else if (sf == 10) + sf = RH_RF95_SPREADING_FACTOR_1024CPS; + else if (sf == 11) + sf = RH_RF95_SPREADING_FACTOR_2048CPS; + else if (sf >= 12) + sf = RH_RF95_SPREADING_FACTOR_4096CPS; + + // set the new spreading factor + spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, (spiRead(RH_RF95_REG_1E_MODEM_CONFIG2) & ~RH_RF95_SPREADING_FACTOR) | sf); + // check if Low data Rate bit should be set or cleared + setLowDatarate(); + } + +void RH_RF95::setSignalBandwidth(long sbw) +{ + uint8_t bw; //register bit pattern + + if (sbw <= 7800) + bw = RH_RF95_BW_7_8KHZ; + else if (sbw <= 10400) + bw = RH_RF95_BW_10_4KHZ; + else if (sbw <= 15600) + bw = RH_RF95_BW_15_6KHZ ; + else if (sbw <= 20800) + bw = RH_RF95_BW_20_8KHZ; + else if (sbw <= 31250) + bw = RH_RF95_BW_31_25KHZ; + else if (sbw <= 41700) + bw = RH_RF95_BW_41_7KHZ; + else if (sbw <= 62500) + bw = RH_RF95_BW_62_5KHZ; + else if (sbw <= 125000) + bw = RH_RF95_BW_125KHZ; + else if (sbw <= 250000) + bw = RH_RF95_BW_250KHZ; + else + bw = RH_RF95_BW_500KHZ; + + // top 4 bits of reg 1D control bandwidth + spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, (spiRead(RH_RF95_REG_1D_MODEM_CONFIG1) & ~RH_RF95_BW) | bw); + // check if low data rate bit should be set or cleared + setLowDatarate(); +} + +void RH_RF95::setCodingRate4(uint8_t denominator) +{ + int cr = RH_RF95_CODING_RATE_4_5; + +// if (denominator <= 5) +// cr = RH_RF95_CODING_RATE_4_5; + if (denominator == 6) + cr = RH_RF95_CODING_RATE_4_6; + else if (denominator == 7) + cr = RH_RF95_CODING_RATE_4_7; + else if (denominator >= 8) + cr = RH_RF95_CODING_RATE_4_8; + + // CR is bits 3..1 of RH_RF95_REG_1D_MODEM_CONFIG1 + spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1, (spiRead(RH_RF95_REG_1D_MODEM_CONFIG1) & ~RH_RF95_CODING_RATE) | cr); +} + +void RH_RF95::setLowDatarate() +{ + // called after changing bandwidth and/or spreading factor + // Semtech modem design guide AN1200.13 says + // "To avoid issues surrounding drift of the crystal reference oscillator due to either temperature change + // or motion,the low data rate optimization bit is used. Specifically for 125 kHz bandwidth and SF = 11 and 12, + // this adds a small overhead to increase robustness to reference frequency variations over the timescale of the LoRa packet." + + // read current value for BW and SF + uint8_t BW = spiRead(RH_RF95_REG_1D_MODEM_CONFIG1) >> 4; // bw is in bits 7..4 + uint8_t SF = spiRead(RH_RF95_REG_1E_MODEM_CONFIG2) >> 4; // sf is in bits 7..4 + + // calculate symbol time (see Semtech AN1200.22 section 4) + float bw_tab[] = {7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000, 500000}; + + float bandwidth = bw_tab[BW]; + + float symbolTime = 1000.0 * pow(2, SF) / bandwidth; // ms + + // the symbolTime for SF 11 BW 125 is 16.384ms. + // and, according to this :- + // https://www.thethingsnetwork.org/forum/t/a-point-to-note-lora-low-data-rate-optimisation-flag/12007 + // the LDR bit should be set if the Symbol Time is > 16ms + // So the threshold used here is 16.0ms + + // the LDR is bit 3 of RH_RF95_REG_26_MODEM_CONFIG3 + uint8_t current = spiRead(RH_RF95_REG_26_MODEM_CONFIG3) & ~RH_RF95_LOW_DATA_RATE_OPTIMIZE; // mask off the LDR bit + if (symbolTime > 16.0) + spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, current | RH_RF95_LOW_DATA_RATE_OPTIMIZE); + else + spiWrite(RH_RF95_REG_26_MODEM_CONFIG3, current); + +} + +void RH_RF95::setPayloadCRC(bool on) +{ + // Payload CRC is bit 2 of register 1E + uint8_t current = spiRead(RH_RF95_REG_1E_MODEM_CONFIG2) & ~RH_RF95_PAYLOAD_CRC_ON; // mask off the CRC + + if (on) + spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, current | RH_RF95_PAYLOAD_CRC_ON); + else + spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2, current); + _enableCRC = on; +} + diff --git a/RH_RF95.h b/RH_RF95.h new file mode 100644 index 0000000..2e15a5c --- /dev/null +++ b/RH_RF95.h @@ -0,0 +1,917 @@ +// RH_RF95.h +// +// Definitions for HopeRF LoRa radios per: +// http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf +// http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf +// +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_RF95.h,v 1.26 2020/06/15 23:39:39 mikem Exp $ +// + +#ifndef RH_RF95_h +#define RH_RF95_h + +#include + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_RF95_NUM_INTERRUPTS 3 + +// Max number of octets the LORA Rx/Tx FIFO can hold +#define RH_RF95_FIFO_SIZE 255 + +// This is the maximum number of bytes that can be carried by the LORA. +// We use some for headers, keeping fewer for RadioHead messages +#define RH_RF95_MAX_PAYLOAD_LEN RH_RF95_FIFO_SIZE + +// The length of the headers we add. +// The headers are inside the LORA's payload +#define RH_RF95_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this driver. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 1 byte message length, 4 bytes headers, user data and 2 bytes of FCS +#ifndef RH_RF95_MAX_MESSAGE_LEN + #define RH_RF95_MAX_MESSAGE_LEN (RH_RF95_MAX_PAYLOAD_LEN - RH_RF95_HEADER_LEN) +#endif + +// The crystal oscillator frequency of the module +#define RH_RF95_FXOSC 32000000.0 + +// The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19 +#define RH_RF95_FSTEP (RH_RF95_FXOSC / 524288) + + +// Register names (LoRa Mode, from table 85) +#define RH_RF95_REG_00_FIFO 0x00 +#define RH_RF95_REG_01_OP_MODE 0x01 +#define RH_RF95_REG_02_RESERVED 0x02 +#define RH_RF95_REG_03_RESERVED 0x03 +#define RH_RF95_REG_04_RESERVED 0x04 +#define RH_RF95_REG_05_RESERVED 0x05 +#define RH_RF95_REG_06_FRF_MSB 0x06 +#define RH_RF95_REG_07_FRF_MID 0x07 +#define RH_RF95_REG_08_FRF_LSB 0x08 +#define RH_RF95_REG_09_PA_CONFIG 0x09 +#define RH_RF95_REG_0A_PA_RAMP 0x0a +#define RH_RF95_REG_0B_OCP 0x0b +#define RH_RF95_REG_0C_LNA 0x0c +#define RH_RF95_REG_0D_FIFO_ADDR_PTR 0x0d +#define RH_RF95_REG_0E_FIFO_TX_BASE_ADDR 0x0e +#define RH_RF95_REG_0F_FIFO_RX_BASE_ADDR 0x0f +#define RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR 0x10 +#define RH_RF95_REG_11_IRQ_FLAGS_MASK 0x11 +#define RH_RF95_REG_12_IRQ_FLAGS 0x12 +#define RH_RF95_REG_13_RX_NB_BYTES 0x13 +#define RH_RF95_REG_14_RX_HEADER_CNT_VALUE_MSB 0x14 +#define RH_RF95_REG_15_RX_HEADER_CNT_VALUE_LSB 0x15 +#define RH_RF95_REG_16_RX_PACKET_CNT_VALUE_MSB 0x16 +#define RH_RF95_REG_17_RX_PACKET_CNT_VALUE_LSB 0x17 +#define RH_RF95_REG_18_MODEM_STAT 0x18 +#define RH_RF95_REG_19_PKT_SNR_VALUE 0x19 +#define RH_RF95_REG_1A_PKT_RSSI_VALUE 0x1a +#define RH_RF95_REG_1B_RSSI_VALUE 0x1b +#define RH_RF95_REG_1C_HOP_CHANNEL 0x1c +#define RH_RF95_REG_1D_MODEM_CONFIG1 0x1d +#define RH_RF95_REG_1E_MODEM_CONFIG2 0x1e +#define RH_RF95_REG_1F_SYMB_TIMEOUT_LSB 0x1f +#define RH_RF95_REG_20_PREAMBLE_MSB 0x20 +#define RH_RF95_REG_21_PREAMBLE_LSB 0x21 +#define RH_RF95_REG_22_PAYLOAD_LENGTH 0x22 +#define RH_RF95_REG_23_MAX_PAYLOAD_LENGTH 0x23 +#define RH_RF95_REG_24_HOP_PERIOD 0x24 +#define RH_RF95_REG_25_FIFO_RX_BYTE_ADDR 0x25 +#define RH_RF95_REG_26_MODEM_CONFIG3 0x26 + +#define RH_RF95_REG_27_PPM_CORRECTION 0x27 +#define RH_RF95_REG_28_FEI_MSB 0x28 +#define RH_RF95_REG_29_FEI_MID 0x29 +#define RH_RF95_REG_2A_FEI_LSB 0x2a +#define RH_RF95_REG_2C_RSSI_WIDEBAND 0x2c +#define RH_RF95_REG_31_DETECT_OPTIMIZE 0x31 +#define RH_RF95_REG_33_INVERT_IQ 0x33 +#define RH_RF95_REG_37_DETECTION_THRESHOLD 0x37 +#define RH_RF95_REG_39_SYNC_WORD 0x39 + +#define RH_RF95_REG_40_DIO_MAPPING1 0x40 +#define RH_RF95_REG_41_DIO_MAPPING2 0x41 +#define RH_RF95_REG_42_VERSION 0x42 + +#define RH_RF95_REG_4B_TCXO 0x4b +#define RH_RF95_REG_4D_PA_DAC 0x4d +#define RH_RF95_REG_5B_FORMER_TEMP 0x5b +#define RH_RF95_REG_61_AGC_REF 0x61 +#define RH_RF95_REG_62_AGC_THRESH1 0x62 +#define RH_RF95_REG_63_AGC_THRESH2 0x63 +#define RH_RF95_REG_64_AGC_THRESH3 0x64 + +// RH_RF95_REG_01_OP_MODE 0x01 +#define RH_RF95_LONG_RANGE_MODE 0x80 +#define RH_RF95_ACCESS_SHARED_REG 0x40 +#define RH_RF95_LOW_FREQUENCY_MODE 0x08 +#define RH_RF95_MODE 0x07 +#define RH_RF95_MODE_SLEEP 0x00 +#define RH_RF95_MODE_STDBY 0x01 +#define RH_RF95_MODE_FSTX 0x02 +#define RH_RF95_MODE_TX 0x03 +#define RH_RF95_MODE_FSRX 0x04 +#define RH_RF95_MODE_RXCONTINUOUS 0x05 +#define RH_RF95_MODE_RXSINGLE 0x06 +#define RH_RF95_MODE_CAD 0x07 + +// RH_RF95_REG_09_PA_CONFIG 0x09 +#define RH_RF95_PA_SELECT 0x80 +#define RH_RF95_MAX_POWER 0x70 +#define RH_RF95_OUTPUT_POWER 0x0f + +// RH_RF95_REG_0A_PA_RAMP 0x0a +#define RH_RF95_LOW_PN_TX_PLL_OFF 0x10 +#define RH_RF95_PA_RAMP 0x0f +#define RH_RF95_PA_RAMP_3_4MS 0x00 +#define RH_RF95_PA_RAMP_2MS 0x01 +#define RH_RF95_PA_RAMP_1MS 0x02 +#define RH_RF95_PA_RAMP_500US 0x03 +#define RH_RF95_PA_RAMP_250US 0x04 +#define RH_RF95_PA_RAMP_125US 0x05 +#define RH_RF95_PA_RAMP_100US 0x06 +#define RH_RF95_PA_RAMP_62US 0x07 +#define RH_RF95_PA_RAMP_50US 0x08 +#define RH_RF95_PA_RAMP_40US 0x09 +#define RH_RF95_PA_RAMP_31US 0x0a +#define RH_RF95_PA_RAMP_25US 0x0b +#define RH_RF95_PA_RAMP_20US 0x0c +#define RH_RF95_PA_RAMP_15US 0x0d +#define RH_RF95_PA_RAMP_12US 0x0e +#define RH_RF95_PA_RAMP_10US 0x0f + +// RH_RF95_REG_0B_OCP 0x0b +#define RH_RF95_OCP_ON 0x20 +#define RH_RF95_OCP_TRIM 0x1f + +// RH_RF95_REG_0C_LNA 0x0c +#define RH_RF95_LNA_GAIN 0xe0 +#define RH_RF95_LNA_GAIN_G1 0x20 +#define RH_RF95_LNA_GAIN_G2 0x40 +#define RH_RF95_LNA_GAIN_G3 0x60 +#define RH_RF95_LNA_GAIN_G4 0x80 +#define RH_RF95_LNA_GAIN_G5 0xa0 +#define RH_RF95_LNA_GAIN_G6 0xc0 +#define RH_RF95_LNA_BOOST_LF 0x18 +#define RH_RF95_LNA_BOOST_LF_DEFAULT 0x00 +#define RH_RF95_LNA_BOOST_HF 0x03 +#define RH_RF95_LNA_BOOST_HF_DEFAULT 0x00 +#define RH_RF95_LNA_BOOST_HF_150PC 0x03 + +// RH_RF95_REG_11_IRQ_FLAGS_MASK 0x11 +#define RH_RF95_RX_TIMEOUT_MASK 0x80 +#define RH_RF95_RX_DONE_MASK 0x40 +#define RH_RF95_PAYLOAD_CRC_ERROR_MASK 0x20 +#define RH_RF95_VALID_HEADER_MASK 0x10 +#define RH_RF95_TX_DONE_MASK 0x08 +#define RH_RF95_CAD_DONE_MASK 0x04 +#define RH_RF95_FHSS_CHANGE_CHANNEL_MASK 0x02 +#define RH_RF95_CAD_DETECTED_MASK 0x01 + +// RH_RF95_REG_12_IRQ_FLAGS 0x12 +#define RH_RF95_RX_TIMEOUT 0x80 +#define RH_RF95_RX_DONE 0x40 +#define RH_RF95_PAYLOAD_CRC_ERROR 0x20 +#define RH_RF95_VALID_HEADER 0x10 +#define RH_RF95_TX_DONE 0x08 +#define RH_RF95_CAD_DONE 0x04 +#define RH_RF95_FHSS_CHANGE_CHANNEL 0x02 +#define RH_RF95_CAD_DETECTED 0x01 + +// RH_RF95_REG_18_MODEM_STAT 0x18 +#define RH_RF95_RX_CODING_RATE 0xe0 +#define RH_RF95_MODEM_STATUS_CLEAR 0x10 +#define RH_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08 +#define RH_RF95_MODEM_STATUS_RX_ONGOING 0x04 +#define RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED 0x02 +#define RH_RF95_MODEM_STATUS_SIGNAL_DETECTED 0x01 + +// RH_RF95_REG_1C_HOP_CHANNEL 0x1c +#define RH_RF95_PLL_TIMEOUT 0x80 +#define RH_RF95_RX_PAYLOAD_CRC_IS_ON 0x40 +#define RH_RF95_FHSS_PRESENT_CHANNEL 0x3f + +// RH_RF95_REG_1D_MODEM_CONFIG1 0x1d +#define RH_RF95_BW 0xf0 + +#define RH_RF95_BW_7_8KHZ 0x00 +#define RH_RF95_BW_10_4KHZ 0x10 +#define RH_RF95_BW_15_6KHZ 0x20 +#define RH_RF95_BW_20_8KHZ 0x30 +#define RH_RF95_BW_31_25KHZ 0x40 +#define RH_RF95_BW_41_7KHZ 0x50 +#define RH_RF95_BW_62_5KHZ 0x60 +#define RH_RF95_BW_125KHZ 0x70 +#define RH_RF95_BW_250KHZ 0x80 +#define RH_RF95_BW_500KHZ 0x90 +#define RH_RF95_CODING_RATE 0x0e +#define RH_RF95_CODING_RATE_4_5 0x02 +#define RH_RF95_CODING_RATE_4_6 0x04 +#define RH_RF95_CODING_RATE_4_7 0x06 +#define RH_RF95_CODING_RATE_4_8 0x08 +#define RH_RF95_IMPLICIT_HEADER_MODE_ON 0x01 + +// RH_RF95_REG_1E_MODEM_CONFIG2 0x1e +#define RH_RF95_SPREADING_FACTOR 0xf0 +#define RH_RF95_SPREADING_FACTOR_64CPS 0x60 +#define RH_RF95_SPREADING_FACTOR_128CPS 0x70 +#define RH_RF95_SPREADING_FACTOR_256CPS 0x80 +#define RH_RF95_SPREADING_FACTOR_512CPS 0x90 +#define RH_RF95_SPREADING_FACTOR_1024CPS 0xa0 +#define RH_RF95_SPREADING_FACTOR_2048CPS 0xb0 +#define RH_RF95_SPREADING_FACTOR_4096CPS 0xc0 +#define RH_RF95_TX_CONTINUOUS_MODE 0x08 + +#define RH_RF95_PAYLOAD_CRC_ON 0x04 +#define RH_RF95_SYM_TIMEOUT_MSB 0x03 + +// RH_RF95_REG_26_MODEM_CONFIG3 +#define RH_RF95_MOBILE_NODE 0x08 // HopeRF term +#define RH_RF95_LOW_DATA_RATE_OPTIMIZE 0x08 // Semtechs term +#define RH_RF95_AGC_AUTO_ON 0x04 + +// RH_RF95_REG_4B_TCXO 0x4b +#define RH_RF95_TCXO_TCXO_INPUT_ON 0x10 + +// RH_RF95_REG_4D_PA_DAC 0x4d +#define RH_RF95_PA_DAC_DISABLE 0x04 +#define RH_RF95_PA_DAC_ENABLE 0x07 + + +///////////////////////////////////////////////////////////////////// +/// \class RH_RF95 RH_RF95.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via a LoRa +/// capable radio transceiver. +/// +/// For an excellent discussion of LoRa range and modulations, see +/// https://medium.com/home-wireless/testing-lora-radios-with-the-limesdr-mini-part-2-37fa481217ff +/// +/// For Semtech SX1276/77/78/79 and HopeRF RF95/96/97/98 and other similar LoRa capable radios. +/// Based on http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf +/// and http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf +/// and http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf +/// and http://www.semtech.com/images/datasheet/sx1276.pdf +/// and http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf +/// FSK/GFSK/OOK modes are not (yet) supported. +/// +/// Works with +/// - the excellent MiniWirelessLoRa from Anarduino http://www.anarduino.com/miniwireless +/// - The excellent Modtronix inAir4 http://modtronix.com/inair4.html +/// and inAir9 modules http://modtronix.com/inair9.html. +/// - the excellent Rocket Scream Mini Ultra Pro with the RFM95W +/// http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/ +/// - Lora1276 module from NiceRF http://www.nicerf.com/product_view.aspx?id=99 +/// - Adafruit Feather M0 with RFM95 +/// - The very fine Talk2 Whisper Node LoRa boards https://wisen.com.au/store/products/whisper-node-lora +/// an Arduino compatible board, which include an on-board RFM95/96 LoRa Radio (Semtech SX1276), external antenna, +/// run on 2xAAA batteries and support low power operations. RF95 examples work without modification. +/// Use Arduino Board Manager to install the Talk2 code support. Upload the code with an FTDI adapter set to 5V. +/// - heltec / TTGO ESP32 LoRa OLED https://www.aliexpress.com/item/Internet-Development-Board-SX1278-ESP32-WIFI-chip-0-96-inch-OLED-Bluetooth-WIFI-Lora-Kit-32/32824535649.html +/// +/// \par Overview +/// +/// This class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 251 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF +/// RFM95/96/97/98(W), Semtech SX1276/77/78/79 and compatible radio modules in LoRa mode. +/// +/// The Hope-RF (http://www.hoperf.com) RFM95/96/97/98(W) and Semtech SX1276/77/78/79 is a low-cost ISM transceiver +/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and +/// programmable data rates, and it also supports the proprietary LoRA (Long Range) mode, which +/// is the only mode supported in this RadioHead driver. +/// +/// This Driver provides functions for sending and receiving messages of up +/// to 251 octets on any frequency supported by the radio, in a range of +/// predefined Bandwidths, Spreading Factors and Coding Rates. Frequency can be set with +/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited +/// range of frequencies due to antenna tuning. +/// +/// Up to 2 modules can be connected to an Arduino (3 on a Mega), +/// permitting the construction of translators and frequency changers, etc. +/// +/// Support for other features such as transmitter power control etc is +/// also provided. +/// +/// Tested on MinWirelessLoRa with arduino-1.0.5 +/// on OpenSuSE 13.1. +/// Also tested with Teensy3.1, Modtronix inAir4 and Arduino 1.6.5 on OpenSuSE 13.1 +/// +/// \par Packet Format +/// +/// All messages sent and received by this RH_RF95 Driver conform to this packet format: +/// +/// - LoRa mode: +/// - 8 symbol PREAMBLE +/// - Explicit header with header CRC (default CCITT, handled internally by the radio) +/// - 4 octets HEADER: (TO, FROM, ID, FLAGS) +/// - 0 to 251 octets DATA +/// - CRC (default CCITT, handled internally by the radio) +/// +/// \par Connecting RFM95/96/97/98 and Semtech SX1276/77/78/79 to Arduino +/// +/// We tested with Anarduino MiniWirelessLoRA, which is an Arduino Duemilanove compatible with a RFM96W +/// module on-board. Therefore it needs no connections other than the USB +/// programming connection and an antenna to make it work. +/// +/// If you have a bare RFM95/96/97/98 that you want to connect to an Arduino, you +/// might use these connections (untested): CAUTION: you must use a 3.3V type +/// Arduino, otherwise you will also need voltage level shifters between the +/// Arduino and the RFM95. CAUTION, you must also ensure you connect an +/// antenna. +/// +/// \code +/// Arduino RFM95/96/97/98 +/// GND----------GND (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin D2-----------DIO0 (interrupt request out) +/// SS pin D10----------NSS (CS chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------MOSI (SPI Data in) +/// MISO pin D12----------MISO (SPI Data out) +/// \endcode +/// With these connections, you can then use the default constructor RH_RF95(). +/// You can override the default settings for the SS pin and the interrupt in +/// the RH_RF95 constructor if you wish to connect the slave select SS to other +/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53 +/// for Mega) or the interrupt request to other than pin D2 (Caution, +/// different processors have different constraints as to the pins available +/// for interrupts). +/// +/// You can connect a Modtronix inAir4 or inAir9 directly to a 3.3V part such as a Teensy 3.1 like +/// this (tested). +/// \code +/// Teensy inAir4 inAir9 +/// GND----------0V (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin D2-----------D0 (interrupt request out) +/// SS pin D10----------CS (CS chip select in) +/// SCK pin D13----------CK (SPI clock in) +/// MOSI pin D11----------SI (SPI Data in) +/// MISO pin D12----------SO (SPI Data out) +/// \endcode +/// With these connections, you can then use the default constructor RH_RF95(). +/// you must also set the transmitter power with useRFO: +/// driver.setTxPower(13, true); +/// +/// Note that if you are using Modtronix inAir4 or inAir9,or any other module which uses the +/// transmitter RFO pins and not the PA_BOOST pins +/// that you must configure the power transmitter power for -1 to 14 dBm and with useRFO true. +/// Failure to do that will result in extremely low transmit powers. +/// +/// If you have an Arduino M0 Pro from arduino.org, +/// you should note that you cannot use Pin 2 for the interrupt line +/// (Pin 2 is for the NMI only). The same comments apply to Pin 4 on Arduino Zero from arduino.cc. +/// Instead you can use any other pin (we use Pin 3) and initialise RH_RF69 like this: +/// \code +/// // Slave Select is pin 10, interrupt is Pin 3 +/// RH_RF95 driver(10, 3); +/// \endcode +/// +/// If you have a Rocket Scream Mini Ultra Pro with the RFM95W: +/// - Ensure you have Arduino SAMD board support 1.6.5 or later in Arduino IDE 1.6.8 or later. +/// - The radio SS is hardwired to pin D5 and the DIO0 interrupt to pin D2, +/// so you need to initialise the radio like this: +/// \code +/// RH_RF95 driver(5, 2); +/// \endcode +/// - The name of the serial port on that board is 'SerialUSB', not 'Serial', so this may be helpful at the top of our +/// sample sketches: +/// \code +/// #define Serial SerialUSB +/// \endcode +/// - You also need this in setup before radio initialisation +/// \code +/// // Ensure serial flash is not interfering with radio communication on SPI bus +/// pinMode(4, OUTPUT); +/// digitalWrite(4, HIGH); +/// \endcode +/// - and if you have a 915MHz part, you need this after driver/manager intitalisation: +/// \code +/// rf95.setFrequency(915.0); +/// \endcode +/// which adds up to modifying sample sketches something like: +/// \code +/// #include +/// #include +/// RH_RF95 rf95(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W +/// #define Serial SerialUSB +/// +/// void setup() +/// { +/// // Ensure serial flash is not interfering with radio communication on SPI bus +/// pinMode(4, OUTPUT); +/// digitalWrite(4, HIGH); +/// +/// Serial.begin(9600); +/// while (!Serial) ; // Wait for serial port to be available +/// if (!rf95.init()) +/// Serial.println("init failed"); +/// rf95.setFrequency(915.0); +/// } +/// ... +/// \endcode +/// +/// For Adafruit Feather M0 with RFM95, construct the driver like this: +/// \code +/// RH_RF95 rf95(8, 3); +/// \endcode +/// +/// If you have a talk2 Whisper Node LoRa board with on-board RF95 radio, +/// the example rf95_* sketches work without modification. Initialise the radio like +/// with the default constructor: +/// \code +/// RH_RF95 driver; +/// \endcode +/// +/// It is possible to have 2 or more radios connected to one Arduino, provided +/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common +/// to all radios) +/// +/// Caution: on some Arduinos such as the Mega 2560, if you set the slave +/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may +/// need to set the usual SS pin to be an output to force the Arduino into SPI +/// master mode. +/// +/// Caution: Power supply requirements of the RFM module may be relevant in some circumstances: +/// RFM95/96/97/98 modules are capable of pulling 120mA+ at full power, where Arduino's 3.3V line can +/// give 50mA. You may need to make provision for alternate power supply for +/// the RFM module, especially if you wish to use full transmit power, and/or you have +/// other shields demanding power. Inadequate power for the RFM is likely to cause symptoms such as: +/// - reset's/bootups terminate with "init failed" messages +/// - random termination of communication after 5-30 packets sent/received +/// - "fake ok" state, where initialization passes fluently, but communication doesn't happen +/// - shields hang Arduino boards, especially during the flashing +/// +/// \par Interrupts +/// +/// The RH_RF95 driver uses interrupts to react to events in the RFM module, +/// such as the reception of a new packet, or the completion of transmission +/// of a packet. The driver configures the radio so the required interrupt is generated by the radio's DIO0 pin. +/// The RH_RF95 driver interrupt service routine reads status from +/// and writes data to the the RFM module via the SPI interface. It is very +/// important therefore, that if you are using the RH_RF95 driver with another +/// SPI based deviced, that you disable interrupts while you transfer data to +/// and from that other device. Use cli() to disable interrupts and sei() to +/// reenable them. +/// +/// \par Memory +/// +/// The RH_RF95 driver requires non-trivial amounts of memory. The sample +/// programs all compile to about 8kbytes each, which will fit in the +/// flash proram memory of most Arduinos. However, the RAM requirements are +/// more critical. Therefore, you should be vary sparing with RAM use in +/// programs that use the RH_RF95 driver. +/// +/// It is often hard to accurately identify when you are hitting RAM limits on Arduino. +/// The symptoms can include: +/// - Mysterious crashes and restarts +/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements) +/// - Hanging +/// - Output from Serial.print() not appearing +/// +/// \par Range +/// +/// We have made some simple range tests under the following conditions: +/// - rf95_client base station connected to a VHF discone antenna at 8m height above ground +/// - rf95_server mobile connected to 17.3cm 1/4 wavelength antenna at 1m height, no ground plane. +/// - Both configured for 13dBm, 434MHz, Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, CRC on. Slow+long range +/// - Minimum reported RSSI seen for successful comms was about -91 +/// - Range over flat ground through heavy trees and vegetation approx 2km. +/// - At 20dBm (100mW) otherwise identical conditions approx 3km. +/// - At 20dBm, along salt water flat sandy beach, 3.2km. +/// +/// It should be noted that at this data rate, a 12 octet message takes 2 seconds to transmit. +/// +/// At 20dBm (100mW) with Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. +/// (Default medium range) in the conditions described above. +/// - Range over flat ground through heavy trees and vegetation approx 2km. +/// +/// Caution: the performance of this radio, especially with narrow bandwidths is strongly dependent on the +/// accuracy and stability of the chip clock. HopeRF and Semtech do not appear to +/// recommend bandwidths of less than 62.5 kHz +/// unless you have the optional Temperature Compensated Crystal Oscillator (TCXO) installed and +/// enabled on your radio module. See the refernece manual for more data. +/// Also https://lowpowerlab.com/forum/rf-range-antennas-rfm69-library/lora-library-experiences-range/15/ +/// and http://www.semtech.com/images/datasheet/an120014-xo-guidance-lora-modulation.pdf +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power on the RF transceiver +/// with the RH_RF95::setTxPower() function. The argument can be any of +/// +2 to +20 (for modules that use PA_BOOST) +/// 0 to +15 (for modules that use RFO transmitter pin) +/// The default is 13. Eg: +/// \code +/// driver.setTxPower(10); // use PA_BOOST transmitter pin +/// driver.setTxPower(10, true); // use PA_RFO pin transmitter pin instead of PA_BOOST +/// \endcode +/// +/// We have made some actual power measurements against +/// programmed power for Anarduino MiniWirelessLoRa (which has RFM96W-433Mhz installed, and which includes an RF power +/// amp for addition 3dBm of power +/// - MiniWirelessLoRa RFM96W-433Mhz, USB power +/// - 30cm RG316 soldered direct to RFM96W module ANT and GND +/// - SMA connector +/// - 12db attenuator +/// - SMA connector +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// 2 5 +/// 4 7 +/// 6 8 +/// 8 11 +/// 10 13 +/// 12 15 +/// 14 16 +/// 16 18 +/// 17 20 +/// 18 21 +/// 19 22 +/// 20 23 +/// \endcode +/// +/// We have also measured the actual power output from a Modtronix inAir4 http://modtronix.com/inair4.html +/// connected to a Teensy 3.1: +/// Teensy 3.1 this is a 3.3V part, connected directly to: +/// Modtronix inAir4 with SMA antenna connector, connected as above: +/// 10cm SMA-SMA cable +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// 0 0 +/// 2 2 +/// 3 4 +/// 6 7 +/// 8 10 +/// 10 13 +/// 12 14.2 +/// 14 15 +/// 15 16 +/// \endcode +/// (Caution: we dont claim laboratory accuracy for these power measurements) +/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna. +class RH_RF95 : public RHSPIDriver +{ +public: + /// \brief Defines register values for a set of modem configuration registers + /// + /// Defines register values for a set of modem configuration registers + /// that can be passed to setModemRegisters() if none of the choices in + /// ModemConfigChoice suit your need setModemRegisters() writes the + /// register values from this structure to the appropriate registers + /// to set the desired spreading factor, coding rate and bandwidth + typedef struct + { + uint8_t reg_1d; ///< Value for register RH_RF95_REG_1D_MODEM_CONFIG1 + uint8_t reg_1e; ///< Value for register RH_RF95_REG_1E_MODEM_CONFIG2 + uint8_t reg_26; ///< Value for register RH_RF95_REG_26_MODEM_CONFIG3 + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common + /// data rates. If you need another configuration, + /// determine the necessary settings and call setModemRegisters() with your + /// desired settings. It might be helpful to use the LoRa calculator mentioned in + /// http://www.semtech.com/images/datasheet/LoraDesignGuide_STD.pdf + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + /// Caution: if you are using slow packet rates and long packets with RHReliableDatagram or subclasses + /// you may need to change the RHReliableDatagram timeout for reliable operations. + /// Caution: for some slow rates nad with ReliableDatagrams you may need to increase the reply timeout + /// with manager.setTimeout() to + /// deal with the long transmission times. + /// Caution: SX1276 family errata suggests alternate settings for some LoRa registers when 500kHz bandwidth + /// is in use. See the Semtech SX1276/77/78 Errata Note. These are not implemented by RH_RF95. + typedef enum + { + Bw125Cr45Sf128 = 0, ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range + Bw500Cr45Sf128, ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range + Bw31_25Cr48Sf512, ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range + Bw125Cr48Sf4096, ///< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, low data rate, CRC on. Slow+long range + Bw125Cr45Sf2048, ///< Bw = 125 kHz, Cr = 4/5, Sf = 2048chips/symbol, CRC on. Slow+long range + } ModemConfigChoice; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RH_RF22 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the RFM DIO0 interrupt line. + /// Defaults to pin 2, as required by Anarduino MinWirelessLoRa module. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On Arduino Zero from arduino.cc, any digital pin other than 4. + /// On Arduino M0 Pro from arduino.org, any digital pin other than 2. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_RF95(uint8_t slaveSelectPin = SS, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi); + + /// Initialise the Driver transport hardware and software. + /// Leaves the radio in idle mode, + /// with default configuration of: 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Prints the value of all chip registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging purposes only. + /// \return true on success + bool printRegisters(); + + /// Sets all the registers required to configure the data modem in the radio, including the bandwidth, + /// spreading factor etc. You can use this to configure the modem with custom configurations if none of the + /// canned configurations in ModemConfigChoice suit you. + /// \param[in] config A ModemConfig structure containing values for the modem configuration registers. + void setModemRegisters(const ModemConfig* config); + + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. + /// Caution: the slowest protocols may require a radio module with TCXO temperature controlled oscillator + /// for reliable operation. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Tests whether a new message is available from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it will be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then optionally waits for Channel Activity Detection (CAD) + /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send + /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting. + /// \return true if the message length was valid and it was correctly queued for transmit. Return false + /// if CAD was requested and the CAD timeout timed out before clear channel was detected. + virtual bool send(const uint8_t* data, uint8_t len); + + /// Sets the length of the preamble + /// in bytes. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 8. + /// Sets the message preamble length in RH_RF95_REG_??_PREAMBLE_?SB + /// \param[in] bytes Preamble length in bytes. + void setPreambleLength(uint16_t bytes); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// Sets the transmitter and receiver + /// centre frequency. + /// \param[in] centre Frequency in MHz. 137.0 to 1020.0. Caution: RFM95/96/97/98 comes in several + /// different frequency ranges, and setting a frequency outside that range of your radio will probably not work + /// \return true if the selected frquency centre is within range + bool setFrequency(float centre); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the RF95/96/97/98. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the RF95/96/97/98. + void setModeTx(); + + /// Sets the transmitter power output level, and configures the transmitter pin. + /// Be a good neighbour and set the lowest power level you need. + /// Some SX1276/77/78/79 and compatible modules (such as RFM95/96/97/98) + /// use the PA_BOOST transmitter pin for high power output (and optionally the PA_DAC) + /// while some (such as the Modtronix inAir4 and inAir9) + /// use the RFO transmitter pin for lower power but higher efficiency. + /// You must set the appropriate power level and useRFO argument for your module. + /// Check with your module manufacturer which transmtter pin is used on your module + /// to ensure you are setting useRFO correctly. + /// Failure to do so will result in very low + /// transmitter power output. + /// Caution: legal power limits may apply in certain countries. + /// After init(), the power will be set to 13dBm, with useRFO false (ie PA_BOOST enabled). + /// \param[in] power Transmitter power level in dBm. For RFM95/96/97/98 LORA with useRFO false, + /// valid values are from +2 to +20. For 18, 19 and 20, PA_DAC is enabled, + /// For Modtronix inAir4 and inAir9 with useRFO true (ie RFO pins in use), + /// valid values are from 0 to 15. + /// \param[in] useRFO If true, enables the use of the RFO transmitter pins instead of + /// the PA_BOOST pin (false). Choose the correct setting for your module. + void setTxPower(int8_t power, bool useRFO = false); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + + // Bent G Christensen (bentor@gmail.com), 08/15/2016 + /// Use the radio's Channel Activity Detect (CAD) function to detect channel activity. + /// Sets the RF95 radio into CAD mode and waits until CAD detection is complete. + /// To be used in a listen-before-talk mechanism (Collision Avoidance) + /// with a reasonable time backoff algorithm. + /// This is called automatically by waitCAD(). + /// \return true if channel is in use. + virtual bool isChannelActive(); + + /// Enable TCXO mode + /// Call this immediately after init(), to force your radio to use an external + /// frequency source, such as a Temperature Compensated Crystal Oscillator (TCXO), if available. + /// See the comments in the main documentation about the sensitivity of this radio to + /// clock frequency especially when using narrow bandwidths. + /// Leaves the module in sleep mode. + /// Caution: the TCXO model radios are not low power when in sleep (consuming + /// about ~600 uA, reported by Phang Moh Lim.
+ /// Caution: if you enable TCXO and there is no exernal TCXO signal connected to the radio + /// or if the exerrnal TCXO is not + /// powered up, the radio will not work<\b> + /// \param[in] on If true (the default) enables the radio to use the external TCXO. + void enableTCXO(bool on = true); + + /// Returns the last measured frequency error. + /// The LoRa receiver estimates the frequency offset between the receiver centre frequency + /// and that of the received LoRa signal. This function returns the estimates offset (in Hz) + /// of the last received message. Caution: this measurement is not absolute, but is measured + /// relative to the local receiver's oscillator. + /// Apparent errors may be due to the transmitter, the receiver or both. + /// \return The estimated centre frequency offset in Hz of the last received message. + /// If the modem bandwidth selector in + /// register RH_RF95_REG_1D_MODEM_CONFIG1 is invalid, returns 0. + int frequencyError(); + + /// Returns the Signal-to-noise ratio (SNR) of the last received message, as measured + /// by the receiver. + /// \return SNR of the last received message in dB + int lastSNR(); + + /// brian.n.norman@gmail.com 9th Nov 2018 + /// Sets the radio spreading factor. + /// valid values are 6 through 12. + /// Out of range values below 6 are clamped to 6 + /// Out of range values above 12 are clamped to 12 + /// See Semtech DS SX1276/77/78/79 page 27 regarding SF6 configuration. + /// + /// \param[in] uint8_t sf (spreading factor 6..12) + /// \return nothing + void setSpreadingFactor(uint8_t sf); + + /// brian.n.norman@gmail.com 9th Nov 2018 + /// Sets the radio signal bandwidth + /// sbw ranges and resultant settings are as follows:- + /// sbw range actual bw (kHz) + /// 0-7800 7.8 + /// 7801-10400 10.4 + /// 10401-15600 15.6 + /// 15601-20800 20.8 + /// 20801-31250 31.25 + /// 31251-41700 41.7 + /// 41701-62500 62.5 + /// 62501-12500 125.0 + /// 12501-250000 250.0 + /// >250000 500.0 + /// NOTE caution Earlier - Semtech do not recommend BW below 62.5 although, in testing + /// I managed 31.25 with two devices in close proximity. + /// \param[in] sbw long, signal bandwidth e.g. 125000 + void setSignalBandwidth(long sbw); + + /// brian.n.norman@gmail.com 9th Nov 2018 + /// Sets the coding rate to 4/5, 4/6, 4/7 or 4/8. + /// Valid denominator values are 5, 6, 7 or 8. A value of 5 sets the coding rate to 4/5 etc. + /// Values below 5 are clamped at 5 + /// values above 8 are clamped at 8. + /// Default for all standard modem config options is 4/5. + /// \param[in] denominator uint8_t range 5..8 + void setCodingRate4(uint8_t denominator); + + /// brian.n.norman@gmail.com 9th Nov 2018 + /// sets the low data rate flag if symbol time exceeds 16ms + /// ref: https://www.thethingsnetwork.org/forum/t/a-point-to-note-lora-low-data-rate-optimisation-flag/12007 + /// called by setBandwidth() and setSpreadingfactor() since these affect the symbol time. + void setLowDatarate(); + + /// brian.n.norman@gmail.com 9th Nov 2018 + /// Allows the CRC to be turned on/off. Default is true (enabled) + /// When true, RH_RF95 sends a CRC in outgoing packets and requires a valid CRC to be + /// present and correct on incoming packets. + /// When false, does not send CRC in outgoing packets and does not require a CRC to be + /// present on incoming packets. However if a CRC is present, it must be correct. + /// Normally this should be left on (the default) + /// so that packets with a bad CRC are rejected. If turned off you wil be much more likely to receive + /// false noise packets. + /// \param[in] on bool, true enables CRCs in incoming and outgoing packets, false disables them + void setPayloadCRC(bool on); + +protected: + /// This is a low level function to handle the interrupts for one instance of RH_RF95. + /// Called automatically by isr*() + /// Should not need to be called by user code. + void handleInterrupt(); + + /// Examine the revceive buffer to determine whether the message is for this node + void validateRxBuf(); + + /// Clear our local receive buffer + void clearRxBuf(); + + /// Called by RH_RF95 when the radio mode is about to change to a new setting. + /// Can be used by subclasses to implement antenna switching etc. + /// \param[in] mode RHMode the new mode about to take effect + /// \return true if the subclasses changes successful + virtual bool modeWillChange(RHMode) {return true;} + + /// False if the PA_BOOST transmitter output pin is to be used. + /// True if the RFO transmitter output pin is to be used. + bool _useRFO; + +private: + /// Low level interrupt service routine for device connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for device connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_RF95* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// Number of octets in the buffer + volatile uint8_t _bufLen; + + /// The receiver/transmitter buffer + uint8_t _buf[RH_RF95_MAX_PAYLOAD_LEN]; + + /// True when there is a valid message in the buffer + volatile bool _rxBufValid; + + /// True if we are using the HF port (779.0 MHz and above) + bool _usingHFport; + + /// Last measured SNR, dB + int8_t _lastSNR; + + /// If true, sends CRCs in every packet and requires a valid CRC in every received packet + bool _enableCRC; + +}; + +/// @example rf95_client.pde +/// @example rf95_server.pde +/// @example rf95_encrypted_client.pde +/// @example rf95_encrypted_server.pde +/// @example rf95_reliable_datagram_client.pde +/// @example rf95_reliable_datagram_server.pde + +#endif + diff --git a/RH_Serial.cpp b/RH_Serial.cpp new file mode 100644 index 0000000..fa1975b --- /dev/null +++ b/RH_Serial.cpp @@ -0,0 +1,249 @@ +// RH_Serial.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RH_Serial.cpp,v 1.17 2020/01/07 23:35:02 mikem Exp $ + +#include +#if (RH_PLATFORM == RH_PLATFORM_STM32F2) +#elif defined (ARDUINO_ARCH_STM32F4) + #include +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA) + #include +#else + #include +#endif +#include + +#ifdef RH_HAVE_SERIAL +RH_Serial::RH_Serial(HardwareSerial& serial) + : + _serial(serial), + _rxState(RxStateInitialising) +{ +} + +HardwareSerial& RH_Serial::serial() +{ + return _serial; +} + +bool RH_Serial::init() +{ + if (!RHGenericDriver::init()) + return false; + _rxState = RxStateIdle; + return true; +} + +// Call this often +bool RH_Serial::available() +{ + while (!_rxBufValid &&_serial.available()) + handleRx(_serial.read()); + return _rxBufValid; +} + +void RH_Serial::waitAvailable() +{ +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + // Unix version driver in RHutil/HardwareSerial knows how to wait without polling + while (!available()) + _serial.waitAvailable(); +#else + RHGenericDriver::waitAvailable(); +#endif +} + +bool RH_Serial::waitAvailableTimeout(uint16_t timeout) +{ +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + // Unix version driver in RHutil/HardwareSerial knows how to wait without polling + unsigned long starttime = millis(); + while ((millis() - starttime) < timeout) + { + _serial.waitAvailableTimeout(timeout - (millis() - starttime)); + if (available()) + return true; + YIELD; + } + return false; +#else + return RHGenericDriver::waitAvailableTimeout(timeout); +#endif +} + +void RH_Serial::handleRx(uint8_t ch) +{ + // State machine for receiving chars + switch(_rxState) + { + case RxStateIdle: + { + if (ch == DLE) + _rxState = RxStateDLE; + } + break; + + case RxStateDLE: + { + if (ch == STX) + { + clearRxBuf(); + _rxState = RxStateData; + } + else + _rxState = RxStateIdle; + } + break; + + case RxStateData: + { + if (ch == DLE) + _rxState = RxStateEscape; + else + appendRxBuf(ch); + } + break; + + case RxStateEscape: + { + if (ch == ETX) + { + // add fcs for DLE, ETX + _rxFcs = RHcrc_ccitt_update(_rxFcs, DLE); + _rxFcs = RHcrc_ccitt_update(_rxFcs, ETX); + _rxState = RxStateWaitFCS1; // End frame + } + else if (ch == DLE) + { + appendRxBuf(ch); + _rxState = RxStateData; + } + else + _rxState = RxStateIdle; // Unexpected + } + break; + + case RxStateWaitFCS1: + { + _rxRecdFcs = ch << 8; + _rxState = RxStateWaitFCS2; + } + break; + + case RxStateWaitFCS2: + { + _rxRecdFcs |= ch; + _rxState = RxStateIdle; + validateRxBuf(); + } + break; + + default: // Else some compilers complain + break; + } +} + +void RH_Serial::clearRxBuf() +{ + _rxBufValid = false; + _rxFcs = 0xffff; + _rxBufLen = 0; +} + +void RH_Serial::appendRxBuf(uint8_t ch) +{ + if (_rxBufLen < RH_SERIAL_MAX_PAYLOAD_LEN) + { + // Normal data, save and add to FCS + _rxBuf[_rxBufLen++] = ch; + _rxFcs = RHcrc_ccitt_update(_rxFcs, ch); + } + // If the buffer overflows, we dont record the trailing data, and the FCS will be wrong, + // causing the message to be dropped when the FCS is received +} + +// Check whether the latest received message is complete and uncorrupted +void RH_Serial::validateRxBuf() +{ + if (_rxRecdFcs != _rxFcs) + { + _rxBad++; + return; + } + + // Extract the 4 headers + _rxHeaderTo = _rxBuf[0]; + _rxHeaderFrom = _rxBuf[1]; + _rxHeaderId = _rxBuf[2]; + _rxHeaderFlags = _rxBuf[3]; + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +bool RH_Serial::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + if (buf && len) + { + // Skip the 4 headers that are at the beginning of the rxBuf + if (*len > _rxBufLen-RH_SERIAL_HEADER_LEN) + *len = _rxBufLen-RH_SERIAL_HEADER_LEN; + memcpy(buf, _rxBuf+RH_SERIAL_HEADER_LEN, *len); + } + clearRxBuf(); // This message accepted and cleared + return true; +} + +// Caution: this may block +bool RH_Serial::send(const uint8_t* data, uint8_t len) +{ + if (len > RH_SERIAL_MAX_MESSAGE_LEN) + return false; + + if (!waitCAD()) + return false; // Check channel activity + + _txFcs = 0xffff; // Initial value + _serial.write(DLE); // Not in FCS + _serial.write(STX); // Not in FCS + // First the 4 headers + txData(_txHeaderTo); + txData(_txHeaderFrom); + txData(_txHeaderId); + txData(_txHeaderFlags); + // Now the payload + while (len--) + txData(*data++); + // End of message + _serial.write(DLE); + _txFcs = RHcrc_ccitt_update(_txFcs, DLE); + _serial.write(ETX); + _txFcs = RHcrc_ccitt_update(_txFcs, ETX); + + // Now send the calculated FCS for this message + _serial.write((_txFcs >> 8) & 0xff); + _serial.write(_txFcs & 0xff); + return true; +} + +void RH_Serial::txData(uint8_t ch) +{ + if (ch == DLE) // DLE stuffing required? + _serial.write(DLE); // Not in FCS + _serial.write(ch); + _txFcs = RHcrc_ccitt_update(_txFcs, ch); +} + +uint8_t RH_Serial::maxMessageLength() +{ + return RH_SERIAL_MAX_MESSAGE_LEN; +} + +#endif // HAVE_SERIAL diff --git a/RH_Serial.h b/RH_Serial.h new file mode 100644 index 0000000..c3b3591 --- /dev/null +++ b/RH_Serial.h @@ -0,0 +1,267 @@ +// RH_Serial.h +// +// Copyright (C) 2014 Mike McCauley +// $Id: RH_Serial.h,v 1.14 2020/01/07 23:35:02 mikem Exp $ + +// Works with any serial port. Tested with Arduino Mega connected to Serial1 +// Also works with 3DR Radio V1.3 Telemetry kit (serial at 57600baud) + +#ifndef RH_Serial_h +#define RH_Serial_h + +#include + +// Special characters +#define STX 0x02 +#define ETX 0x03 +#define DLE 0x10 +#define SYN 0x16 + +// Maximum message length (including the headers) we are willing to support +#define RH_SERIAL_MAX_PAYLOAD_LEN 64 + +// The length of the headers we add. +// The headers are inside the payload and are therefore protected by the FCS +#define RH_SERIAL_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this library. +// It is an arbitrary limit. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 4 bytes of address and header and payload to be included in the 64 byte encryption limit. +// the one byte payload length is not encrpyted +#ifndef RH_SERIAL_MAX_MESSAGE_LEN +#define RH_SERIAL_MAX_MESSAGE_LEN (RH_SERIAL_MAX_PAYLOAD_LEN - RH_SERIAL_HEADER_LEN) +#endif + +#if (RH_PLATFORM == RH_PLATFORM_STM32F2) + #define HardwareSerial USARTSerial +#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(ARDUINO_attinyxy6) +// AT Tiny Mega 3216 etc + #define HardwareSerial UartClass +#endif + +class HardwareSerial; + +///////////////////////////////////////////////////////////////////// +/// \class RH_Serial RH_Serial.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via a serial connection +/// +/// This class sends and received packetized messages over a serial connection. +/// It can be used for point-to-point or multidrop, RS232, RS488 or other serial connections as +/// supported by your controller hardware. +/// It can also be used to communicate via radios with serial interfaces such as: +/// - APC220 Radio Data Module http://www.dfrobot.com/image/data/TEL0005/APC220_Datasheet.pdf +/// http://www.dfrobot.com/image/data/TEL0005/APC220_Datasheet.pdf +/// - 3DR Telemetry Radio https://store.3drobotics.com/products/3dr-radio +/// - HopeRF HM-TR module http://www.hoperf.com/upload/rf_app/HM-TRS.pdf +/// - Others +/// +/// Compiles and runs on Linux, OSX and all the microprocessers and MCUs suported by +/// radiohead. On Linux and OSX, a RadioHead specific version of HardwareSerial (in RHutil/HardwareSerial.*) +/// encapsulates access to any serial port (or suported USB-serial converter) +/// +/// The packetised messages include message encapsulation, headers, a message payload and a checksum. +/// It therefore can support robust binary message passing with error-detection and retransmission +/// when used with the appropriate manager. This allows reliable serial communicaitons even over very long +/// lines where noise might otherwise affect reliablity of the communications. +/// +/// \par Packet Format +/// +/// All messages sent and received by this RH_Serial Driver conform to this packet format: +/// \code +/// DLE +/// STX +/// TO Header (1 octet) +/// FROM Header (1 octet) +/// ID Header (1 octet) +/// FLAGS Header (1 octet) +/// Message payload (0 to 60 octets) +/// DLE +/// ETX +/// Frame Check Sequence FCS CCITT CRC-16 (2 octets) +/// \endcode +/// +/// If any of octets from TO header through to the end of the payload are a DLE, +/// then they are preceded by a DLE (ie DLE stuffing). +/// The FCS covers everything from the TO header to the ETX inclusive, but not any stuffed DLEs +/// +/// \par Physical connection +/// +/// The physical connection to your serial port will depend on the type of platform you are on. +/// +/// For example, many arduinos only support a single Serial port on pins 0 and 1, +/// which is shared with the USB host connections. On such Arduinos, it is not possible to use both +/// RH_Serial on the Serial port as well as using the Serial port for debugand other printing or communications. +/// +/// On Arduino Mega and Due, there are 4 serial ports: +/// - Serial: this is the serial port connected to the USB interface and the programming host. +/// - Serial1: on pins 18 (Tx) and 19 (Rx) +/// - Serial2: on pins 16 (Tx) and 17 (Rx) +/// - Serial3: on pins 14 (Tx) and 15 (Rx) +/// +/// On Uno32, there are 2 serial ports: +/// - SerialUSB: this is the port for the USB host connection. +/// - Serial1: on pins 39 (Rx) and 40 (Tx) +/// +/// On Maple and Flymaple, there are 4 serial ports: +/// - SerialUSB: this is the port for the USB host connection. +/// - Serial1: on pins 7 (Tx) and 8 (Rx) +/// - Serial2: on pins 0 (Rx) and 1 (Tx) +/// - Serial3: on pins 29 (Tx) and 30 (Rx) +/// +/// On Linux and OSX there can be any number of serial ports. +/// - On Linux, names like /dev/ttyUSB0 (for a FTDO USB-serial converter) +/// - On OSX, names like /dev/tty.usbserial-A501YSWL (for a FTDO USB-serial converter) +/// +/// On STM32 F4 Discovery with Arduino and Arduino_STM32, there are 4 serial ports. We had success with port 2 +/// (TX on pin PA2 and RX on pin PA3) and initialising the driver like this: +/// RH_Serial driver(Serial2); +/// +/// Note that it is necessary for you to select which Serial port your RF_Serial will use and pass it to the +/// contructor. On Linux you must pass an instance of HardwareSerial. +/// +/// \par Testing +/// +/// You can test this class and the RHReliableDatagram manager +/// on Unix and OSX with back-to-back connected FTDI USB-serial adapters. +/// Back-to-back means the TX of one is connected to the RX of the other and vice-versa. +/// You should also join the ground pins. +/// +/// Assume the 2 USB-serial adapters are connected by USB +/// and have been assigned device names: +/// /dev/ttyUSB0 and /dev/ttyUSB1. +/// Build the example RHReliableDatagram client and server programs: +/// \code +/// tools/simBuild examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde +/// tools/simBuild examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde +/// \endcode +/// In one window run the server, specifying the device to use as an environment variable: +/// \code +/// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB1 ./serial_reliable_datagram_server +/// \endcode +/// And in another window run the client, specifying the other device to use as an environment variable: +/// \code +/// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB0 ./serial_reliable_datagram_client +/// \endcode +/// You should see the 2 programs passing messages to each other. +/// +class RH_Serial : public RHGenericDriver +{ +public: + /// Constructor + /// \param[in] serial Reference to the HardwareSerial port which will be used by this instance. + /// On Unix and OSX, this is an instance of RHutil/HardwareSerial. On + /// Arduino and other, it is an instance of the built in HardwareSerial class. + RH_Serial(HardwareSerial& serial); + + /// Return the HardwareSerial port in use by this instance + /// \return The current HardwareSerial as a reference + HardwareSerial& serial(); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Tests whether a new message is available + /// This can be called multiple times in a timeout loop. + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Wait until a new message is available from the driver. + /// Blocks until a complete message is received as reported by available() + virtual void waitAvailable(); + + /// Wait until a new message is available from the driver or the timeout expires. + /// Blocks until a complete message is received as reported by available() or the timeout expires. + /// \param[in] timeout The maximum time to wait in milliseconds + /// \return true if a message is available as reported by available(), false on timeout. + virtual bool waitAvailableTimeout(uint16_t timeout); + + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + +protected: + /// \brief Defines different receiver states in teh receiver state machine + typedef enum + { + RxStateInitialising = 0, ///< Before init() is called + RxStateIdle, ///< Waiting for an STX + RxStateDLE, ///< Waiting for the DLE after STX + RxStateData, ///< Receiving data + RxStateEscape, ///< Got a DLE while receiving data. + RxStateWaitFCS1, ///< Got DLE ETX, waiting for first FCS octet + RxStateWaitFCS2 ///< Waiting for second FCS octet + } RxState; + + /// HAndle a character received from the serial port. IMplements + /// the receiver state machine + void handleRx(uint8_t ch); + + /// Empties the Rx buffer + void clearRxBuf(); + + /// Adds a charater to the Rx buffer + void appendRxBuf(uint8_t ch); + + /// Checks whether the Rx buffer contains valid data that is complete and uncorrupted + /// Check the FCS, the TO address, and extracts the headers + void validateRxBuf(); + + /// Sends a single data octet to the serial port. + /// Implements DLE stuffing and keeps track of the senders FCS + void txData(uint8_t ch); + + /// Reference to the HardwareSerial port we will use + HardwareSerial& _serial; + + /// The current state of the Rx state machine + RxState _rxState; + + /// Progressive FCS calc (CCITT CRC-16 covering all received data (but not stuffed DLEs), plus trailing DLE, ETX) + uint16_t _rxFcs; + + /// The received FCS at the end of the current message + uint16_t _rxRecdFcs; + + /// The Rx buffer + uint8_t _rxBuf[RH_SERIAL_MAX_PAYLOAD_LEN]; + + /// Current length of data in the Rx buffer + uint8_t _rxBufLen; + + /// True if the data in the Rx buffer is value and uncorrupted and complete message is available for collection + bool _rxBufValid; + + /// FCS for transmitted data + uint16_t _txFcs; +}; + +/// @example serial_reliable_datagram_client.pde +/// @example serial_reliable_datagram_server.pde +/// @example serial_gateway.pde +/// @example serial_encrypted_reliable_datagram_client.pde +/// @example serial_encrypted_reliable_datagram_server.pde + +#endif diff --git a/RH_TCP.cpp b/RH_TCP.cpp new file mode 100644 index 0000000..317aad0 --- /dev/null +++ b/RH_TCP.cpp @@ -0,0 +1,304 @@ +// RH_TCP.cpp +// +// Copyright (C) 2014 Mike McCauley +// $Id: RH_TCP.cpp,v 1.6 2017/01/12 23:58:00 mikem Exp $ + +#include + +// This can only build on Linux and compatible systems +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RH_TCP::RH_TCP(const char* server) + : _server(server), + _rxBufLen(0), + _rxBufValid(false), + _socket(-1) +{ +} + +bool RH_TCP::init() +{ + if (!connectToServer()) + return false; + return sendThisAddress(_thisAddress); +} + +bool RH_TCP::connectToServer() +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; // Stream socket + hints.ai_flags = AI_PASSIVE; // For wildcard IP address + hints.ai_protocol = 0; // Any protocol + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + std::string server(_server); + std::string port("4000"); + size_t indexOfSeparator = server.find_first_of(':'); + if (indexOfSeparator != std::string::npos) + { + port = server.substr(indexOfSeparator+1); + server.erase(indexOfSeparator); + } + + s = getaddrinfo(server.c_str(), port.c_str(), &hints, &result); + if (s != 0) + { + fprintf(stderr, "RH_TCP::connect getaddrinfo failed: %s\n", gai_strerror(s)); + return false; + } + + // getaddrinfo() returns a list of address structures. + // Try each address until we successfully connect(2). + // If socket(2) (or connect(2)) fails, we (close the socket + // and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) + { + _socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (_socket == -1) + continue; + + if (connect(_socket, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(_socket); + } + + if (rp == NULL) + { /* No address succeeded */ + fprintf(stderr, "RH_TCP::connect could not connect to %s\n", _server); + return false; + } + + freeaddrinfo(result); /* No longer needed */ + + // Now make the socket non-blocking + int on = 1; + int rc = ioctl(_socket, FIONBIO, (char *)&on); + if (rc < 0) + { + fprintf(stderr,"RH_TCP::init failed to set socket non-blocking: %s\n", strerror(errno)); + close(_socket); + _socket = -1; + return false; + } + return true; +} + +void RH_TCP::clearRxBuf() +{ + _rxBufValid = false; + _rxBufLen = 0; +} + +void RH_TCP::checkForEvents() +{ + #define RH_TCP_SOCKETBUF_LEN 500 + static uint8_t socketBuf[RH_TCP_SOCKETBUF_LEN]; // Room for several messages + static uint16_t socketBufLen = 0; + + // Read at most the amount of space we have left in the buffer + ssize_t count = read(_socket, socketBuf + socketBufLen, sizeof(socketBuf) - socketBufLen); + if (count < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr,"RH_TCP::checkForEvents read error: %s\n", strerror(errno)); + exit(1); + } + } + else if (count == 0) + { + // End of file + fprintf(stderr,"RH_TCP::checkForEvents unexpected end of file on read\n"); + exit(1); + } + else + { + socketBufLen += count; + while (socketBufLen >= 5) + { + RHTcpTypeMessage* message = ((RHTcpTypeMessage*)socketBuf); + uint32_t len = ntohl(message->length); + uint32_t messageLen = len + sizeof(message->length); + if (len > sizeof(socketBuf) - sizeof(message->length)) + { + // Bogus length + fprintf(stderr, "RH_TCP::checkForEvents read ridiculous length: %d. Corrupt message stream? Aborting\n", len); + exit(1); + } + if (socketBufLen >= len + sizeof(message->length)) + { + // Got at least all of this message + if (message->type == RH_TCP_MESSAGE_TYPE_PACKET && len >= 5) + { + // REVISIT: need to check if we are actually receiving? + // Its a new packet, extract the headers and payload + RHTcpPacket* packet = ((RHTcpPacket*)socketBuf); + _rxHeaderTo = packet->to; + _rxHeaderFrom = packet->from; + _rxHeaderId = packet->id; + _rxHeaderFlags = packet->flags; + uint32_t payloadLen = len - 5; + if (payloadLen <= sizeof(_rxBuf)) + { + // Enough room in our receiver buffer + memcpy(_rxBuf, packet->payload, payloadLen); + _rxBufLen = payloadLen; + _rxBufFull = true; + } + } + // check for other message types here + // Now remove the used message by copying the trailing bytes (maybe start of a new message?) + // to the top of the buffer + memcpy(socketBuf, socketBuf + messageLen, sizeof(socketBuf) - messageLen); + socketBufLen -= messageLen; + } + } + } +} + +void RH_TCP::validateRxBuf() +{ + // The headers have already been extracted + if (_promiscuous || + _rxHeaderTo == _thisAddress || + _rxHeaderTo == RH_BROADCAST_ADDRESS) + { + _rxGood++; + _rxBufValid = true; + } +} + +bool RH_TCP::available() +{ + if (_socket < 0) + return false; + checkForEvents(); + if (_rxBufFull) + { + validateRxBuf(); + _rxBufFull= false; + } + return _rxBufValid; +} + +// Block until something is available +void RH_TCP::waitAvailable() +{ + waitAvailableTimeout(0); // 0 = Wait forever +} + +// Block until something is available or timeout expires +bool RH_TCP::waitAvailableTimeout(uint16_t timeout) +{ + int max_fd; + fd_set input; + int result; + + FD_ZERO(&input); + FD_SET(_socket, &input); + max_fd = _socket + 1; + + if (timeout) + { + struct timeval timer; + // Timeout is in milliseconds + timer.tv_sec = timeout / 1000; + timer.tv_usec = (timeout % 1000) * 1000; + result = select(max_fd, &input, NULL, NULL, &timer); + } + else + { + result = select(max_fd, &input, NULL, NULL, NULL); + } + if (result < 0) + fprintf(stderr, "RH_TCP::waitAvailableTimeout: select failed %s\n", strerror(errno)); + return result > 0; +} + +bool RH_TCP::recv(uint8_t* buf, uint8_t* len) +{ + if (!available()) + return false; + + if (buf && len) + { + if (*len > _rxBufLen) + *len = _rxBufLen; + memcpy(buf, _rxBuf, *len); + } + clearRxBuf(); + return true; +} + +bool RH_TCP::send(const uint8_t* data, uint8_t len) +{ + if (!waitCAD()) + return false; // Check channel activity (prob not possible for this driver?) + + bool ret = sendPacket(data, len); + delay(10); // Wait for transmit to succeed. REVISIT: depends on length and speed + return ret; +} + +uint8_t RH_TCP::maxMessageLength() +{ + return RH_TCP_MAX_MESSAGE_LEN; +} + +void RH_TCP::setThisAddress(uint8_t address) +{ + RHGenericDriver::setThisAddress(address); + sendThisAddress(_thisAddress); +} + +bool RH_TCP::sendThisAddress(uint8_t thisAddress) +{ + if (_socket < 0) + return false; + RHTcpThisAddress m; + m.length = htonl(2); + m.type = RH_TCP_MESSAGE_TYPE_THISADDRESS; + m.thisAddress = thisAddress; + ssize_t sent = write(_socket, &m, sizeof(m)); + return sent > 0; +} + +bool RH_TCP::sendPacket(const uint8_t* data, uint8_t len) +{ + if (_socket < 0) + return false; + RHTcpPacket m; + m.length = htonl(len + 4); + m.type = RH_TCP_MESSAGE_TYPE_PACKET; + m.to = _txHeaderTo; + m.from = _txHeaderFrom; + m.id = _txHeaderId; + m.flags = _txHeaderFlags; + memcpy(m.payload, data, len); + ssize_t sent = write(_socket, &m, len + 8); + return sent > 0; +} + +#endif diff --git a/RH_TCP.h b/RH_TCP.h new file mode 100644 index 0000000..d2cb0d2 --- /dev/null +++ b/RH_TCP.h @@ -0,0 +1,187 @@ +// RH_TCP.h +// Author: Mike McCauley (mikem@aierspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_TCP.h,v 1.4 2015/08/13 02:45:47 mikem Exp $ +#ifndef RH_TCP_h +#define RH_TCP_h + +#include +#include + +///////////////////////////////////////////////////////////////////// +/// \class RH_TCP RH_TCP.h +/// \brief Driver to send and receive unaddressed, unreliable datagrams via sockets on a Linux simulator +/// +/// \par Overview +/// +/// This class is intended to support the testing of RadioHead manager classes and simulated sketches +/// on a Linux host. +/// RH_TCP class sends messages to and from other simulator sketches via sockets to a 'Luminiferous Ether' +/// simulator server (provided). +/// Multiple instances of simulated clients and servers can run on a single Linux server, +/// passing messages to each other via the etherSimulator.pl server. +/// +/// Simple RadioHead sketches can be compiled and run on Linux using a build script and some support files. +/// +/// \par Running simulated sketches +/// +/// \code +/// cd whatever/RadioHead +/// # build the client for Linux: +/// tools/simBuild examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde +/// # build the server for Linux: +/// tools/simBuild examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde +/// # in one window, run the simulator server: +/// tools/etherSimulator.pl +/// # in another window, run the server +/// ./simulator_reliable_datagram_server +/// # in another window, run the client: +/// ./simulator_reliable_datagram_client +/// # see output: +/// Sending to simulator_reliable_datagram_server +/// got reply from : 0x02: And hello back to you +/// Sending to simulator_reliable_datagram_server +/// got reply from : 0x02: And hello back to you +/// Sending to simulator_reliable_datagram_server +/// got reply from : 0x02: And hello back to you +/// ... +/// \endcode +/// +/// You can change the listen port and the simulated baud rate with +/// command line arguments passed to etherSimulator.pl +/// +/// \par Implementation +/// +/// etherServer.pl is a conventional server written in Perl. +/// listens on a TCP socket (defaults to port 4000) for connections from sketch simulators +/// using RH_TCP as theur driver. +/// The simulated sketches send messages out to the 'ether' over the TCP connection to the etherServer. +/// etherServer manages the delivery of each message to any other RH_TCP sketches that are running. +/// +/// \par Prerequisites +/// +/// g++ compiler installed and in your $PATH +/// Perl +/// Perl POE library +/// +class RH_TCP : public RHGenericDriver +{ +public: + /// Constructor + /// \param[in] server Name and optionally the port number of the ether simulator server to contact. + /// Format is "name[:port]", where name can be any valid host name or address (IPV4 or IPV6). + /// The trailing :port is optional, and port can be any valid + /// port name or port number. + RH_TCP(const char* server = "localhost:4000"); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init(); + + /// Tests whether a new message is available + /// from the Driver. + /// On most drivers, this will also put the Driver into RHModeRx mode until + /// a message is actually received by the transport, when it will be returned to RHModeIdle. + /// This can be called multiple times in a timeout loop + /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() + virtual bool available(); + + /// Wait until a new message is available from the driver. + /// Blocks until a complete message is received as reported by available() + virtual void waitAvailable(); + + /// Wait until a new message is available from the driver + /// or the timeout expires + /// Blocks until a complete message is received as reported by available() + /// \param[in] timeout The maximum time to wait in milliseconds + /// \return true if a message is available as reported by available() + virtual bool waitAvailableTimeout(uint16_t timeout); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + virtual bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will + /// return false and will not send the message. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + virtual bool send(const uint8_t* data, uint8_t len); + + /// Returns the maximum message length + /// available in this Driver. + /// \return The maximum legal message length + virtual uint8_t maxMessageLength(); + + /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. + /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, + /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. + /// In promiscuous mode, all messages will be accepted regardless of the TO header. + /// In a conventional multinode system, all nodes will have a unique address + /// (which you could store in EEPROM). + /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, + /// allowing the possibilty of address spoofing). + /// \param[in] address The address of this node. + void setThisAddress(uint8_t address); + +protected: + +private: + /// Connect to the address and port specified by the server constructor argument. + /// Prepares the socket for use. + bool connectToServer(); + + /// Check for new messages from the ether simulator server + void checkForEvents(); + + /// Clear the receive buffer + void clearRxBuf(); + + /// Sends thisAddress to the ether simulator server + /// in a RHTcpThisAddress message. + /// \param[in] thisAddress The node address of this node + /// \return true if successful + bool sendThisAddress(uint8_t thisAddress); + + /// Sends a message to the ether simulator server for delivery to + /// other nodes + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if successful + bool sendPacket(const uint8_t* data, uint8_t len); + + /// Address and port of the server to which messages are sent + /// and received using the protocol RHTcpPRotocol + const char* _server; + + /// The TCP socket used to communicate with the message server + int _socket; + + /// Buffer to receive RHTcpProtocol messages + uint8_t _rxBuf[RH_TCP_MAX_PAYLOAD_LEN + 5]; + uint16_t _rxBufLen; + bool _rxBufValid; + + /// Check whether the latest received message is complete and uncorrupted + void validateRxBuf(); + + // Used in the interrupt handlers + /// Buf is filled but not validated + volatile bool _rxBufFull; + +}; + +/// @example simulator_reliable_datagram_client.pde +/// @example simulator_reliable_datagram_server.pde + +#endif diff --git a/RHutil/HardwareSerial.cpp b/RHutil/HardwareSerial.cpp new file mode 100644 index 0000000..786bd13 --- /dev/null +++ b/RHutil/HardwareSerial.cpp @@ -0,0 +1,246 @@ +// HardwareSerial.cpp +// +// Copyright (C) 2015 Mike McCauley +// $Id: HardwareSerial.cpp,v 1.3 2015/08/13 02:45:47 mikem Exp mikem $ + +#include +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + +#include + +#include +#include +#include +#include +#include +#include +#include + +HardwareSerial::HardwareSerial(const char* deviceName) + : _deviceName(deviceName), + _device(-1) +{ + // Override device name from environment + char* e = getenv("RH_HARDWARESERIAL_DEVICE_NAME"); + if (e) + _deviceName = e; +} + +void HardwareSerial::begin(int baud) +{ + if (openDevice()) + setBaud(baud); +} + +void HardwareSerial::end() +{ + closeDevice(); +} + +void HardwareSerial::flush() +{ + tcdrain(_device); +} + +int HardwareSerial::peek(void) +{ + printf("HardwareSerial::peek not implemented\n"); + return 0; +} + +int HardwareSerial::available() +{ + int bytes; + + if (ioctl(_device, FIONREAD, &bytes) != 0) + { + fprintf(stderr, "HardwareSerial::available ioctl failed: %s\n", strerror(errno)); + return 0; + } + return bytes; +} + +int HardwareSerial::read() +{ + uint8_t data; + ssize_t result = ::read(_device, &data, 1); + if (result != 1) + { + fprintf(stderr, "HardwareSerial::read read failed: %s\n", strerror(errno)); + return 0; + } +// printf("got: %02x\n", data); + return data; +} + +size_t HardwareSerial::write(uint8_t ch) +{ + size_t result = ::write(_device, &ch, 1); + if (result != 1) + { + fprintf(stderr, "HardwareSerial::write failed: %s\n", strerror(errno)); + return 0; + } +// printf("sent: %02x\n", ch); + return 1; // OK +} + +bool HardwareSerial::openDevice() +{ + if (_device == -1) + closeDevice(); + _device = open(_deviceName, O_RDWR | O_NOCTTY | O_NDELAY); + if (_device == -1) + { + // Could not open the port. + fprintf(stderr, "HardwareSerial::openDevice could not open %s: %s\n", _deviceName, strerror(errno)); + return false; + } + + // Device opened + fcntl(_device, F_SETFL, 0); + return true; +} + +bool HardwareSerial::closeDevice() +{ + if (_device != -1) + close(_device); + _device = -1; + return true; +} + +bool HardwareSerial::setBaud(int baud) +{ + speed_t speed; + + // This is kind of ugly, but its prob better than a case + if (baud == 50) + speed = B50; + else if (baud == 75) + speed = B75; + else if (baud == 110) + speed = B110; + else if (baud == 134) + speed = B134; + else if (baud == 150) + speed = B150; + else if (baud == 200) + speed = B200; + else if (baud == 300) + speed = B300; + else if (baud == 600) + speed = B600; + else if (baud == 1200) + speed = B1200; + else if (baud == 1800) + speed = B1800; + else if (baud == 2400) + speed = B2400; + else if (baud == 4800) + speed = B4800; + else if (baud == 9600) + speed = B9600; + else if (baud == 19200) + speed = B19200; + else if (baud == 38400) + speed = B38400; + else if (baud == 57600) + speed = B57600; +#ifdef B76800 + else if (baud == 76800) // Not available on Linux + speed = B76800; +#endif + else if (baud == 115200) + speed = B115200; + else if (baud == 230400) + speed = B230400; +#ifdef B460800 + else if (baud == 460800) // Not available on OSX + speed = B460800; +#endif +#ifdef B921600 + else if (baud == 921600) // Not available on OSX + speed = B921600; +#endif + else + { + fprintf(stderr, "HardwareSerial::setBaud: unsupported baud rate %d\n", baud); + return false; + } + + struct termios options; + // Get current options + if (tcgetattr(_device, &options) != 0) + { + fprintf(stderr, "HardwareSerial::setBaud: could not tcgetattr %s\n", strerror(errno)); + return false; + } + + // Set new speed options + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + // Enable the receiver and set local mode... + options.c_cflag |= (CLOCAL | CREAD); + + // Force mode to 8,N,1 + // to be compatible with Arduino HardwareSerial + // Should this be configurable? Prob not, must have 8 bits, dont need parity. + options.c_cflag &= ~(PARENB | CSTOPB | CSIZE); + options.c_cflag |= CS8; + + // Disable flow control and input character conversions + options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | INLCR); + + // Raw input: + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Raw output + options.c_oflag &= ~(OPOST | OCRNL | ONLCR); + + // Set the options in the port + if (tcsetattr(_device, TCSANOW, &options) != 0) + { + fprintf(stderr, "HardwareSerial::setBaud: could not tcsetattr %s\n", strerror(errno)); + return false; + } + + _baud = baud; + return true; +} + +// Block until something is available +void HardwareSerial::waitAvailable() +{ + waitAvailableTimeout(0); // 0 = Wait forever +} + +// Block until something is available or timeout expires +bool HardwareSerial::waitAvailableTimeout(uint16_t timeout) +{ + int max_fd; + fd_set input; + int result; + + FD_ZERO(&input); + FD_SET(_device, &input); + max_fd = _device + 1; + + if (timeout) + { + struct timeval timer; + // Timeout is in milliseconds + timer.tv_sec = timeout / 1000; + timer.tv_usec = (timeout % 1000) * 1000; + result = select(max_fd, &input, NULL, NULL, &timer); + } + else + { + result = select(max_fd, &input, NULL, NULL, NULL); + } + if (result < 0) + fprintf(stderr, "HardwareSerial::waitAvailableTimeout: select failed %s\n", strerror(errno)); + return result > 0; +} + +#endif diff --git a/RHutil/HardwareSerial.h b/RHutil/HardwareSerial.h new file mode 100644 index 0000000..e6ee111 --- /dev/null +++ b/RHutil/HardwareSerial.h @@ -0,0 +1,100 @@ +// HardwareSerial.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2015 Mike McCauley +// $Id: HardwareSerial.h,v 1.3 2015/08/13 02:45:47 mikem Exp mikem $ +#ifndef HardwareSerial_h +#define HardwareSerial_h + +#include + +///////////////////////////////////////////////////////////////////// +/// \class HardwareSerial HardwareSerial.h +/// \brief Encapsulates a Posix compliant serial port as a HarwareSerial +/// +/// This class provides access to a serial port on Unix and OSX. +/// It is equivalent to HardwareSerial in Arduino, and can be used by RH_Serial +/// We implement just enough to provide the services RadioHead needs. +/// Additional methods not present on Arduino are also provided for waiting for characters. +/// +/// The device port is configured for 8 bits, no parity, 1 stop bit and full raw transparency, so it can be used +/// to send and receive any 8 bit character. A limited range of baud rates is supported. +/// +/// \par Device Names +/// +/// Device naming conventions vary from OS to OS. ON linux, an FTDI serial port may have a name like +/// /dev/ttyUSB0. On OSX, it might be something like /dev/tty.usbserial-A501YSWL +/// \par errors +/// +/// A number of these methods print error messages to stderr in the event of an IO error. +class HardwareSerial +{ +public: + /// Constructor + // \param [in] deviceName Name of the derial port device to connect to + HardwareSerial(const char* deviceName); + + /// Open and configure the port. + /// The named port is opened, and the given baud rate is set. + /// The port is configure for raw input and output and 8,N,1 protocol + /// with no flow control. + /// This must be called before any other operations are attempted. + /// IO failures and unsupported baud rates will result in an error message on stderr. + /// \param[in] baud The desired baud rate. The only rates supported are: 50, 75, 110, 134, 150 + /// 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400. On some platform + /// such as Linux you may also use: 460800, 921600. + void begin(int baud); + + /// Close the port. + /// If begin() has previously been called successfully, the device port will be closed. + /// It may be reopened again with another call to begin(). + void end(); + + /// Flush remaining data. + /// Blocks until any data yet to be transmtted is sent. + void flush(); + + /// Peek at the nex available character without consuming it. + /// CAUTION: Not implemented. + int peek(void); + + /// Returns the number of bytes immediately available to be read from the + /// device. + /// \return 0 if none available else the number of characters available for immediate reading + int available(); + + /// Read and return the next available character. + /// If no character is available prints a message to stderr and returns 0; + /// \return The next available character + int read(); + + /// Transmit a single character oin the serial port. + /// Returns immediately. + /// IO errors are repored by printing aa message to stderr. + /// \param[in] ch The character to send. Anything in the range 0x00 to 0xff is permitted + /// \return 1 if successful else 0 + size_t write(uint8_t ch); + + // These are not usually in HardwareSerial but we + // need them in a Unix environment + + /// Wait until a character is available from the port. + void waitAvailable(); + + /// Wait until a a character is available from the port. + /// or the timeout expires + /// \param[in] timeout The maximum time to wait in milliseconds. 0 means wait forever. + /// \return true if a message is available as reported by available() + bool waitAvailableTimeout(uint16_t timeout); + +protected: + bool openDevice(); + bool closeDevice(); + bool setBaud(int baud); + +private: + const char* _deviceName; + int _device; // file desriptor + int _baud; +}; + +#endif diff --git a/RHutil/RasPi.cpp b/RHutil/RasPi.cpp new file mode 100644 index 0000000..f993980 --- /dev/null +++ b/RHutil/RasPi.cpp @@ -0,0 +1,176 @@ +// RasPi.cpp +// +// Routines for implementing RadioHead on Raspberry Pi +// using BCM2835 library for GPIO +// +// Contributed by Mike Poublon and used with permission + + +#include + +#if (RH_PLATFORM == RH_PLATFORM_RASPI) +#include +#include +#include "RasPi.h" + +//Initialize the values for sanity +timeval RHStartTime; + +void SPIClass::begin() +{ + //Set SPI Defaults + uint16_t divider = BCM2835_SPI_CLOCK_DIVIDER_256; + uint8_t bitorder = BCM2835_SPI_BIT_ORDER_MSBFIRST; + uint8_t datamode = BCM2835_SPI_MODE0; + + begin(divider, bitorder, datamode); +} + +void SPIClass::begin(uint16_t divider, uint8_t bitOrder, uint8_t dataMode) +{ + setClockDivider(divider); + setBitOrder(bitOrder); + setDataMode(dataMode); + + //Set CS pins polarity to low + bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, 0); + + bcm2835_spi_begin(); + + //Initialize a timestamp for millis calculation + gettimeofday(&RHStartTime, NULL); +} + +void SPIClass::end() +{ + //End the SPI + bcm2835_spi_end(); +} + +void SPIClass::setBitOrder(uint8_t bitOrder) +{ + //Set the SPI bit Order + bcm2835_spi_setBitOrder(bitOrder); +} + +void SPIClass::setDataMode(uint8_t mode) +{ + //Set SPI data mode + bcm2835_spi_setDataMode(mode); +} + +void SPIClass::setClockDivider(uint16_t rate) +{ + //Set SPI clock divider + bcm2835_spi_setClockDivider(rate); +} + +byte SPIClass::transfer(byte _data) +{ + //Set which CS pin to use for next transfers + bcm2835_spi_chipSelect(BCM2835_SPI_CS0); + //Transfer 1 byte + byte data; + data = bcm2835_spi_transfer((uint8_t)_data); + return data; +} + +void pinMode(unsigned char pin, unsigned char mode) +{ + if (mode == OUTPUT) + { + bcm2835_gpio_fsel(pin,BCM2835_GPIO_FSEL_OUTP); + } + else + { + bcm2835_gpio_fsel(pin,BCM2835_GPIO_FSEL_INPT); + } +} + +void digitalWrite(unsigned char pin, unsigned char value) +{ + bcm2835_gpio_write(pin,value); +} + +unsigned long millis() +{ + //Declare a variable to store current time + struct timeval RHCurrentTime; + //Get current time + gettimeofday(&RHCurrentTime,NULL); + //Calculate the difference between our start time and the end time + unsigned long difference = ((RHCurrentTime.tv_sec - RHStartTime.tv_sec) * 1000); + difference += ((RHCurrentTime.tv_usec - RHStartTime.tv_usec)/1000); + //Return the calculated value + return difference; +} + +void delay (unsigned long ms) +{ + //Implement Delay function + struct timespec ts; + ts.tv_sec=0; + ts.tv_nsec=(ms * 1000); + nanosleep(&ts,&ts); +} + +long random(long min, long max) +{ + long diff = max - min; + long ret = diff * rand() + min; + return ret; +} + +void SerialSimulator::begin(int baud) +{ + //No implementation neccesary - Serial emulation on Linux = standard console + // + //Initialize a timestamp for millis calculation - we do this here as well in case SPI + //isn't used for some reason + gettimeofday(&RHStartTime, NULL); +} + +size_t SerialSimulator::println(const char* s) +{ + print(s); + printf("\n"); +} + +size_t SerialSimulator::print(const char* s) +{ + printf(s); +} + +size_t SerialSimulator::print(unsigned int n, int base) +{ + if (base == DEC) + printf("%d", n); + else if (base == HEX) + printf("%02x", n); + else if (base == OCT) + printf("%o", n); + // TODO: BIN +} + +size_t SerialSimulator::print(char ch) +{ + printf("%c", ch); +} + +size_t SerialSimulator::println(char ch) +{ + printf("%c\n", ch); +} + +size_t SerialSimulator::print(unsigned char ch, int base) +{ + return print((unsigned int)ch, base); +} + +size_t SerialSimulator::println(unsigned char ch, int base) +{ + print((unsigned int)ch, base); + printf("\n"); +} + +#endif diff --git a/RHutil/RasPi.h b/RHutil/RasPi.h new file mode 100644 index 0000000..e32bc11 --- /dev/null +++ b/RHutil/RasPi.h @@ -0,0 +1,75 @@ +// RasPi.h +// +// Routines for implementing RadioHead on Raspberry Pi +// using BCM2835 library for GPIO +// Contributed by Mike Poublon and used with permission + +#ifndef RASPI_h +#define RASPI_h + +#include + +#include +#include +#include +#include + +typedef unsigned char byte; + +#ifndef NULL + #define NULL 0 +#endif + +#ifndef OUTPUT + #define OUTPUT BCM2835_GPIO_FSEL_OUTP +#endif + +class SPIClass +{ + public: + static byte transfer(byte _data); + // SPI Configuration methods + static void begin(); // Default + static void begin(uint16_t, uint8_t, uint8_t); + static void end(); + static void setBitOrder(uint8_t); + static void setDataMode(uint8_t); + static void setClockDivider(uint16_t); +}; + +extern SPIClass SPI; + +class SerialSimulator +{ + public: + #define DEC 10 + #define HEX 16 + #define OCT 8 + #define BIN 2 + + // TODO: move these from being inlined + static void begin(int baud); + static size_t println(const char* s); + static size_t print(const char* s); + static size_t print(unsigned int n, int base = DEC); + static size_t print(char ch); + static size_t println(char ch); + static size_t print(unsigned char ch, int base = DEC); + static size_t println(unsigned char ch, int base = DEC); +}; + +extern SerialSimulator Serial; + +void RasPiSetup(); + +void pinMode(unsigned char pin, unsigned char mode); + +void digitalWrite(unsigned char pin, unsigned char value); + +unsigned long millis(); + +void delay (unsigned long delay); + +long random(long min, long max); + +#endif diff --git a/RHutil/atomic.h b/RHutil/atomic.h new file mode 100644 index 0000000..0192198 --- /dev/null +++ b/RHutil/atomic.h @@ -0,0 +1,71 @@ +/* +* This is port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3 +* v1.0 +* Mark Pendrith, Nov 27, 2012. +* +* From Mark: +* >When I ported the macros I emailed Dean to ask what attribution would be +* >appropriate, and here is his response: +* > +* >>Mark, +* >>I think it's great that you've ported the macros; consider them +* >>public domain, to do with whatever you wish. I hope you find them >useful . +* >> +* >>Cheers! +* >>- Dean +*/ + +#ifdef __arm__ +#ifndef _CORTEX_M3_ATOMIC_H_ +#define _CORTEX_M3_ATOMIC_H_ + +static __inline__ uint32_t __get_primask(void) \ +{ uint32_t primask = 0; \ + __asm__ volatile ("MRS %[result], PRIMASK\n\t":[result]"=r"(primask)::); \ + return primask; } // returns 0 if interrupts enabled, 1 if disabled + +static __inline__ void __set_primask(uint32_t setval) \ +{ __asm__ volatile ("MSR PRIMASK, %[value]\n\t""dmb\n\t""dsb\n\t""isb\n\t"::[value]"r"(setval):); + __asm__ volatile ("" ::: "memory");} + +static __inline__ uint32_t __iSeiRetVal(void) \ +{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \ + __asm__ volatile ("" ::: "memory"); return 1; } + +static __inline__ uint32_t __iCliRetVal(void) \ +{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \ + __asm__ volatile ("" ::: "memory"); return 1; } + +static __inline__ void __iSeiParam(const uint32_t *__s) \ +{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \ + __asm__ volatile ("" ::: "memory"); (void)__s; } + +static __inline__ void __iCliParam(const uint32_t *__s) \ +{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \ + __asm__ volatile ("" ::: "memory"); (void)__s; } + +static __inline__ void __iRestore(const uint32_t *__s) \ +{ __set_primask(*__s); __asm__ volatile ("dmb\n\t""dsb\n\t""isb\n\t"); \ + __asm__ volatile ("" ::: "memory"); } + + +#define ATOMIC_BLOCK(type) \ +for ( type, __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0 ) + +#define ATOMIC_RESTORESTATE \ +uint32_t primask_save __attribute__((__cleanup__(__iRestore))) = __get_primask() + +#define ATOMIC_FORCEON \ +uint32_t primask_save __attribute__((__cleanup__(__iSeiParam))) = 0 + +#define NONATOMIC_BLOCK(type) \ +for ( type, __ToDo = __iSeiRetVal(); __ToDo ; __ToDo = 0 ) + +#define NONATOMIC_RESTORESTATE \ +uint32_t primask_save __attribute__((__cleanup__(__iRestore))) = __get_primask() + +#define NONATOMIC_FORCEOFF \ +uint32_t primask_save __attribute__((__cleanup__(__iCliParam))) = 0 + +#endif +#endif diff --git a/RHutil/simulator.h b/RHutil/simulator.h new file mode 100644 index 0000000..deced17 --- /dev/null +++ b/RHutil/simulator.h @@ -0,0 +1,84 @@ +// simulator.h +// Lets Arduino RadioHead sketches run within a simulator on Linux as a single process +// Copyright (C) 2014 Mike McCauley +// $Id: simulator.h,v 1.4 2015/08/13 02:45:47 mikem Exp mikem $ + +#ifndef simulator_h +#define simulator_h + +#include +#include +#include +#include + +// Equivalent types for common Arduino types like uint8_t are in stdint.h + +// Access to some globals +// Command line args passed to the process. +extern int _simulator_argc; +extern char** _simulator_argv; + +// Definitions for various Arduino functions +extern void delay(unsigned long ms); +extern unsigned long millis(); +extern long random(long to); +extern long random(long from, long to); + +// Equavalent to HardwareSerial in Arduino +// but outputs to stdout +class SerialSimulator +{ +public: +#define DEC 10 +#define HEX 16 +#define OCT 8 +#define BIN 2 + + // TODO: move these from being inlined + void begin(int baud) {} + + size_t println(const char* s) + { + print(s); + return printf("\n"); + } + size_t print(const char* s) + { + return printf("%s", s); // This style prevent warnings from [-Wformat-security] + } + size_t print(unsigned int n, int base = DEC) + { + if (base == DEC) + return printf("%d", n); + else if (base == HEX) + return printf("%02x", n); + else if (base == OCT) + return printf("%o", n); + // TODO: BIN + else + return 0; + } + size_t print(char ch) + { + return printf("%c", ch); + } + size_t println(char ch) + { + return printf("%c\n", ch); + } + size_t print(unsigned char ch, int base = DEC) + { + return print((unsigned int)ch, base); + } + size_t println(unsigned char ch, int base = DEC) + { + print((unsigned int)ch, base); + return printf("\n"); + } + +}; + +// Global instance of the Serial output +extern SerialSimulator Serial; + +#endif diff --git a/RHutil_pigpio/RasPi.cpp b/RHutil_pigpio/RasPi.cpp new file mode 100644 index 0000000..20f28e8 --- /dev/null +++ b/RHutil_pigpio/RasPi.cpp @@ -0,0 +1,264 @@ +// RasPi.cpp +//(9/22/2019) Contributed by Brody M. This file is based off RHutil\RasPi.cpp +// but modified for the pigpio library instead of BCM2835. Original +// code maintained where possible. Unused code commented out and +// left in place. + +// Routines for implementing RadioHead on Raspberry Pi +// using BCM2835 library for GPIO +// +// Contributed by Mike Poublon and used with permission + + +#include + +#if (RH_PLATFORM == RH_PLATFORM_RASPI) +#include +#include +#include "RasPi.h" +#include + +int spiHandle; + +//Initialize the values for sanity +timeval RHStartTime; + +void SPIClass::begin() +{ + //Set SPI Defaults + //Retaining BCM2835 macros for compatibility with RadioHead + uint16_t divider = BCM2835_SPI_CLOCK_DIVIDER_256; + uint8_t bitorder = BCM2835_SPI_BIT_ORDER_MSBFIRST; + uint8_t datamode = BCM2835_SPI_MODE0; + begin(divider, bitorder, datamode); +} + +//void SPIClass::begin(uint32_t spiChannel, uint32_t spiBaud, uint32_t spiFlags) +void SPIClass::begin(uint16_t divider, uint8_t bitOrder, uint8_t dataMode) +{ + + //Set CS pins polarity to low + //bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, 0); + + //pigpio SPI Defailts + //SPI Speed + //BCM2835 divider of 256 is approx 1MHz SCLK, depending on model + //uint32_t spiBaud = 1000000; + //Spi Flag Settings + //21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + //b b b b b b R T n n n n W A u2 u1 u0 p2 p1 p0 m m + //m m bits = mode + //Mode 0 = 0 0 + + uint32_t spiBaud = convertClockDivider(divider); + //datamode is 0 to 3 on BCM2835 + uint32_t spiFlags = 0; //Zero is a good default start. + //on pigpio, the least sig 2 bits set datamode, which will probably be zero. + spiFlags = 0x00000000 | (uint32_t) dataMode; + //According to documentation, bitOrder for SPI MAIN in pigpio is always MSBFIRST. So bitOrder ignored. + printf("\nSPI Settings:\nBaud rate=%d\nFlags=%d\n\n", spiBaud, spiFlags); + spiHandle = spiOpen(0, spiBaud, spiFlags); //spiChannel assumed to be zero. + + //Initialize a timestamp for millis calculation + gettimeofday(&RHStartTime, NULL); +} + +void SPIClass::end() +{ + //End the SPI + //bcm2835_spi_end(); + spiClose(spiHandle); +} + +uint32_t SPIClass::convertClockDivider(uint16_t rate) +{ + //Simple divide default RPi SPI clock by divider amount. + //Nominal clock at 250MHz for Zero. + return 250000000/rate; +} +/* +//Thes functions aren't necessary +void SPIClass::setBitOrder(uint8_t bitOrder) +{ + //Set the SPI bit Order + bcm2835_spi_setBitOrder(bitOrder); +} + +void SPIClass::setDataMode(uint8_t mode) +{ + //Set SPI data mode + bcm2835_spi_setDataMode(mode); +} + +void SPIClass::setClockDivider(uint16_t rate) +{ + //Set SPI clock divider + bcm2835_spi_setClockDivider(rate); +} +*/ + +byte SPIClass::transfer(byte _data) +{ + char txByte[1] = {(char)_data}; + char rxByte[1]; + //For RF Compatibility, just transfer 1 byte + spiXfer(spiHandle, txByte, rxByte, 1); + return (byte)rxByte[0]; +} + + +//void pinMode(unsigned char pin, unsigned char mode) +void pinMode(uint8_t pin, WiringPinMode mode) +{ + if (mode == OUTPUT) + { + gpioSetMode(pin, PI_OUTPUT); + //bcm2835_gpio_fsel(pin,BCM2835_GPIO_FSEL_OUTP); + } + else if (mode == INPUT) + { + gpioSetMode(pin, PI_INPUT); + //bcm2835_gpio_fsel(pin,BCM2835_GPIO_FSEL_INPT); + } + else if (mode == INPUT_PULLUP) + { + gpioSetMode(pin, PI_INPUT); + gpioSetPullUpDown(pin, PI_PUD_UP); + } + else if (mode == INPUT_PULLDOWN) + { + gpioSetMode(pin, PI_INPUT); + gpioSetPullUpDown(pin, PI_PUD_DOWN); + } + else + { + //For safety + gpioSetMode(pin, PI_INPUT); + } +} + +void digitalWrite(unsigned char pin, unsigned char value) +{ + //bcm2835_gpio_write(pin,value); + //Could have just written gpioWrite(pin, value) + if(value == HIGH) + { + gpioWrite(pin, PI_ON); + } + else + { + gpioWrite(pin, PI_OFF); + } +} + +unsigned long millis() +{ + //Declare a variable to store current time + struct timeval RHCurrentTime; + //Get current time + gettimeofday(&RHCurrentTime,NULL); + //Calculate the difference between our start time and the end time + unsigned long difference = ((RHCurrentTime.tv_sec - RHStartTime.tv_sec) * 1000); + difference += ((RHCurrentTime.tv_usec - RHStartTime.tv_usec)/1000); + //Return the calculated value + return difference; +} + +void delay (unsigned long ms) +{ + //Implement Delay function + struct timespec ts; + ts.tv_sec=0; + ts.tv_nsec=(ms * 1000); + nanosleep(&ts,&ts); +} + +long random(long min, long max) +{ + long diff = max - min; + long ret = diff * rand() + min; + return ret; +} + +//****************************** +//* Attach Interupt +//* Emulate Arduino Function +//****************************** + +void attachInterrupt(unsigned char pin, void (*handler)(void), int mode) +{ + switch(mode) + { + case CHANGE: + gpioSetISRFunc(pin, EITHER_EDGE, 0, (void (*)(int,int,unsigned int))handler); + break; + case RISING: + gpioSetISRFunc(pin, RISING_EDGE, 0, (void (*)(int,int,unsigned int))handler); + break; + case FALLING: + gpioSetISRFunc(pin, FALLING_EDGE, 0, (void (*)(int,int,unsigned int))handler); + break; + default: + break; + } +} + +void SerialSimulator::begin(int baud) +{ + //No implementation neccesary - Serial emulation on Linux = standard console + // + //Initialize a timestamp for millis calculation - we do this here as well in case SPI + //isn't used for some reason + gettimeofday(&RHStartTime, NULL); +} + +size_t SerialSimulator::println(const char* s) +{ + size_t charsPrinted = 0; + charsPrinted = print(s); + printf("\n"); + return charsPrinted + 1; +} + +size_t SerialSimulator::print(const char* s) +{ + return (size_t)printf(s); +} + +size_t SerialSimulator::print(unsigned int n, int base) +{ + if (base == DEC) + return (size_t)printf("%d", n); + else if (base == HEX) + return (size_t)printf("%02x", n); + else if (base == OCT) + return (size_t)printf("%o", n); + // TODO: BIN + else + return 0; +} + +size_t SerialSimulator::print(char ch) +{ + return (size_t)printf("%c", ch); +} + +size_t SerialSimulator::println(char ch) +{ + return (size_t)printf("%c\n", ch); +} + +size_t SerialSimulator::print(unsigned char ch, int base) +{ + return print((unsigned int)ch, base); +} + +size_t SerialSimulator::println(unsigned char ch, int base) +{ + size_t charsPrinted = 0; + charsPrinted = print((unsigned int)ch, base); + printf("\n"); + return charsPrinted + 1; +} + +#endif diff --git a/RHutil_pigpio/RasPi.h b/RHutil_pigpio/RasPi.h new file mode 100644 index 0000000..68062d9 --- /dev/null +++ b/RHutil_pigpio/RasPi.h @@ -0,0 +1,214 @@ +// RasPi.h +//(9/22/2019) Contributed by Brody M. This file is based off RHutil\RasPi.h +// but modified for the pigpio library instead of BCM2835. Original +// code maintained where possible. Unused code commented out and +// left in place. WiringPinMode enumeration declaration borrowed from +// STM32ArduinoCompat\wirish.h. Also some enumeration declarations +// "borrowed" from bcm2835.h to maintain original code and for simplicity. + +// Routines for implementing RadioHead on Raspberry Pi +// using BCM2835 library for GPIO +// Contributed by Mike Poublon and used with permission + +#ifndef RASPI_h +#define RASPI_h + +#include + +#include +#include +#include +#include +#include + +typedef unsigned char byte; + +#ifndef NULL + #define NULL 0 +#endif + +#define HIGH 0x1 +#define LOW 0x0 + +#define CHANGE 1 +#define FALLING 2 +#define RISING 3 + +#define memcpy_P memcpy + +//#ifndef OUTPUT +// #define OUTPUT BCM2835_GPIO_FSEL_OUTP +//#endif + +class SPIClass +{ + public: + //pigpio SPI ID + //We need to make sure this handle can be accessed by all SPI Functions + static byte transfer(byte _data); + // SPI Configuration methods + static void begin(); // Default + //static void begin(uint32_t,uint32_t,uint32_t); + static void begin(uint16_t, uint8_t, uint8_t); + static void end(); + //static void setBitOrder(uint8_t); + //static void setDataMode(uint8_t); + //static void setClockDivider(uint16_t); + static uint32_t convertClockDivider(uint16_t); +}; + +extern SPIClass SPI; + +class SerialSimulator +{ + public: + #define DEC 10 + #define HEX 16 + #define OCT 8 + #define BIN 2 + + // TODO: move these from being inlined + static void begin(int baud); + static size_t println(const char* s); + static size_t print(const char* s); + static size_t print(unsigned int n, int base = DEC); + static size_t print(char ch); + static size_t println(char ch); + static size_t print(unsigned char ch, int base = DEC); + static size_t println(unsigned char ch, int base = DEC); +}; + +extern SerialSimulator Serial; + +void RasPiSetup(); + +//The WiringPinMode enumeration declaration is borrowed from STM32ArduinoCompat\wirish.h + +typedef enum WiringPinMode { + OUTPUT, /**< Basic digital output: when the pin is HIGH, the + voltage is held at +3.3v (Vcc) and when it is LOW, it + is pulled down to ground. */ + + OUTPUT_OPEN_DRAIN, /**< In open drain mode, the pin indicates + "low" by accepting current flow to ground + and "high" by providing increased + impedance. An example use would be to + connect a pin to a bus line (which is pulled + up to a positive voltage by a separate + supply through a large resistor). When the + pin is high, not much current flows through + to ground and the line stays at positive + voltage; when the pin is low, the bus + "drains" to ground with a small amount of + current constantly flowing through the large + resistor from the external supply. In this + mode, no current is ever actually sourced + from the pin. */ + + INPUT, /**< Basic digital input. The pin voltage is sampled; when + it is closer to 3.3v (Vcc) the pin status is high, and + when it is closer to 0v (ground) it is low. If no + external circuit is pulling the pin voltage to high or + low, it will tend to randomly oscillate and be very + sensitive to noise (e.g., a breath of air across the pin + might cause the state to flip). */ + + INPUT_ANALOG, /**< This is a special mode for when the pin will be + used for analog (not digital) reads. Enables ADC + conversion to be performed on the voltage at the + pin. */ + + INPUT_PULLUP, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled up" towards +3.3v. This means + the state will be high unless an external device + is specifically pulling the pin down to ground, + in which case the "gentle" pull up will not + affect the state of the input. */ + + INPUT_PULLDOWN, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled down" towards 0v. This means + the state will be low unless an external device + is specifically pulling the pin up to 3.3v, in + which case the "gentle" pull down will not + affect the state of the input. */ + + INPUT_FLOATING, /**< Synonym for INPUT. */ + + PWM, /**< This is a special mode for when the pin will be used for + PWM output (a special case of digital output). */ + + PWM_OPEN_DRAIN, /**< Like PWM, except that instead of alternating + cycles of LOW and HIGH, the voltage on the pin + consists of alternating cycles of LOW and + floating (disconnected). */ +} WiringPinMode; + +void pinMode(uint8_t pin, WiringPinMode mode); + +//void pinMode(unsigned char pin, unsigned char mode); + +void digitalWrite(unsigned char pin, unsigned char value); + +unsigned long millis(); + +void delay (unsigned long delay); + +long random(long min, long max); + +void attachInterrupt(unsigned char pin, void (*handler)(void), int mode); + + + +//The following lines are borrowed from bcm2835.h, which is part of the BCM2835 library +//(https://www.airspayce.com/mikem/bcm2835/). The original RadioHead library expects +//BCM2835. We could eliminate this by modifying more RadioHead code, but this leaves +//the library more "original". Another option would be to include bcm2835.h directly, +//but this method is easier. + + typedef enum + { + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 + }bcm2835SPIBitOrder; + + typedef enum + { + BCM2835_SPI_MODE0 = 0, + BCM2835_SPI_MODE1 = 1, + BCM2835_SPI_MODE2 = 2, + BCM2835_SPI_MODE3 = 3 + }bcm2835SPIMode; + + typedef enum + { + BCM2835_SPI_CS0 = 0, + BCM2835_SPI_CS1 = 1, + BCM2835_SPI_CS2 = 2, + BCM2835_SPI_CS_NONE = 3 + } bcm2835SPIChipSelect; + + typedef enum + { + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, + BCM2835_SPI_CLOCK_DIVIDER_1 = 1 + } bcm2835SPIClockDivider; + + +#endif diff --git a/RadioHead.h b/RadioHead.h new file mode 100644 index 0000000..76920af --- /dev/null +++ b/RadioHead.h @@ -0,0 +1,1701 @@ +// RadioHead.h +// Author: Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY +// Copyright (C) 2014 Mike McCauley +// $Id: RadioHead.h,v 1.86 2020/07/05 08:52:21 mikem Exp mikem $ + +/*! \mainpage RadioHead Packet Radio library for embedded microprocessors + +This is the RadioHead Packet Radio library for embedded microprocessors. +It provides a complete object-oriented library for sending and receiving packetized messages +via a variety of common data radios and other transports on a range of embedded microprocessors. + +The version of the package that this documentation refers to can be downloaded +from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.110.zip +You can find the latest version of the documentation at http://www.airspayce.com/mikem/arduino/RadioHead + +You can also find online help and discussion at +http://groups.google.com/group/radiohead-arduino +Please use that group for all questions and discussions on this topic. +Do not contact the author directly, unless it is to discuss commercial licensing. +Before asking a question or reporting a bug, please read +- http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question +- http://www.catb.org/esr/faqs/smart-questions.html +- http://www.chiark.greenend.org.uk/~shgtatham/bugs.html + +Caution: Developing this type of software and using data radios +successfully is challenging and requires a substantial knowledge +base in software and radio and data transmission technologies and +theory. It may not be an appropriate project for beginners. If +you are a beginner, you will need to spend some time gaining +knowledge in these areas first. + +\par Overview + +RadioHead consists of 2 main sets of classes: Drivers and Managers. + +- Drivers provide low level access to a range of different packet radios and other packetized message transports. +- Managers provide high level message sending and receiving facilities for a range of different requirements. + +Every RadioHead program will have an instance of a Driver to +provide access to the data radio or transport, and usually a +Manager that uses that driver to send and receive messages for the +application. The programmer is required to instantiate a Driver +and a Manager, and to initialise the Manager. Thereafter the +facilities of the Manager can be used to send and receive +messages. + +It is also possible to use a Driver on its own, without a Manager, although this only allows unaddressed, +unreliable transport via the Driver's facilities. + +In some specialised use cases, it is possible to instantiate more than one Driver and more than one Manager. + +A range of different common embedded microprocessor platforms are supported, allowing your project to run +on your choice of processor. + +Example programs are included to show the main modes of use. + +\par Drivers + +The following Drivers are provided: + +- RH_RF22 +Works with Hope-RF +RF22B and RF23B based transceivers, and compatible chips and modules, +including the RFM22B transceiver module such as +hthis bare module: http://www.sparkfun.com/products/10153 +and this shield: http://www.sparkfun.com/products/11018 +and this board: http://www.anarduino.com/miniwireless +and RF23BP modules such as: http://www.anarduino.com/details.jsp?pid=130 +Supports GFSK, FSK and OOK. Access to other chip +features such as on-chip temperature measurement, analog-digital +converter, transmitter power control etc is also provided. + +- RH_RF24 +Works with Silicon Labs Si4460/4461/4463/4464 family of transceivers chip, and the equivalent +HopeRF RF24/26/27 family of chips and the HopeRF RFM24W/26W/27W modules. +Supports GFSK, FSK and OOK. Access to other chip +features such as on-chip temperature measurement, analog-digital +converter, transmitter power control etc is also provided. + +- RH_RF69 +Works with Hope-RF +RF69B based radio modules, such as the RFM69 module, (as used on the excellent Moteino and Moteino-USB +boards from LowPowerLab http://lowpowerlab.com/moteino/ ) +and compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H). +Also works with Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including +the marvellous high powered MinWireless-HW (with 20dBm output for excellent range). +Supports GFSK, FSK. + +- RH_NRF24 +Works with Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 and others. +Also works with Hope-RF RFM73 +and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate +with each other. + +- RH_NRF905 +Works with Nordic nRF905 based 433/868/915 MHz radio modules. + +- RH_NRF51 +Works with Nordic nRF51 compatible 2.4 GHz SoC/devices such as the nRF51822. +Also works with Sparkfun nRF52832 breakout board, with Arduino 1.8.9 and +Sparkfun nRF52 boards manager 0.2.3. + +- RH_RF95 +Works with Semtech SX1276/77/78/79, Modtronix inAir4 and inAir9, +and HopeRF RFM95/96/97/98 and other similar LoRa capable radios. +Supports Long Range (LoRa) with spread spectrum frequency hopping, large payloads etc. +FSK/GFSK/OOK modes are not (yet) supported. + +- RH_MRF89 +Works with Microchip MRF89XA and compatible transceivers. +and modules such as MRF89XAM9A. + +- RH_CC110 +Works with Texas Instruments CC110L transceivers and compatible modules such as +Anaren AIR BoosterPack 430BOOST-CC110L + +- RH_E32 +Works with EBYTE E32-TTL-1W serial radio transceivers (and possibly other transceivers in the same family) + +- RH_ASK +Works with a range of inexpensive ASK (amplitude shift keying) RF transceivers such as RX-B1 +(also known as ST-RX04-ASK) receiver; TX-C1 transmitter and DR3100 transceiver; FS1000A/XY-MK-5V transceiver; +HopeRF RFM83C / RFM85. Supports ASK (OOK). + +- RH_ABZ Works with EcoNode SmartTrap, Tlera Grasshopper and family. Almost any board equipped with a muRata cmwx1zzabz module +should work. Tested with EcoNode SmartTrap, Arduino 1.8.9, GrumpyOldPizza Arduino Core for STM32L0. +When building for EcoNode SmartTrap in Arduino IDE, select board type Grasshopper-L082CZ. + +- RH_Serial +Works with RS232, RS422, RS485, RS488 and other point-to-point and multidropped serial connections, +or with TTL serial UARTs such as those on Arduino and many other processors, +or with data radios with a +serial port interface. RH_Serial provides packetization and error detection over any hardware or +virtual serial connection. Also builds and runs on Linux and OSX. + +- RH_TCP +For use with simulated sketches compiled and running on Linux. +Works with tools/etherSimulator.pl to pass messages between simulated sketches, allowing +testing of Manager classes on Linux and without need for real radios or other transport hardware. + +- RHEncryptedDriver +Adds encryption and decryption to any RadioHead transport driver, using any encrpytion cipher +supported by ArduinoLibs Cryptographic Library http://rweather.github.io/arduinolibs/crypto.html + +Drivers can be used on their own to provide unaddressed, unreliable datagrams. +All drivers have the same identical API. +Or you can use any Driver with any of the Managers described below. + +We welcome contributions of well tested and well documented code to support other transports. + +If your radio or transciever is not on the list above, there is a good chance it +wont work without modifying RadioHead to suit it. If you wish for +support for another radio or transciever, and you send 2 of them to +AirSpayce Pty Ltd, we will consider adding support for it. + +\par Managers + +The drivers above all provide for unaddressed, unreliable, variable +length messages, but if you need more than that, the following +Managers are provided: + +- RHDatagram +Addressed, unreliable variable length messages, with optional broadcast facilities. + +- RHReliableDatagram + Addressed, reliable, retransmitted, acknowledged variable length messages. + +- RHRouter + Multi-hop delivery of RHReliableDatagrams from source node to destination node via 0 or more + intermediate nodes, with manual, pre-programmed routing. + +- RHMesh + Multi-hop delivery of RHReliableDatagrams with automatic route discovery and rediscovery. + +Any Manager may be used with any Driver. + +\par Platforms + +A range of processors and platforms are supported: + +- Arduino and the Arduino IDE (version 1.0 to 1.8.1 and later) +Including Diecimila, Uno, Mega, Leonardo, Yun, Due, Zero etc. http://arduino.cc/, Also similar boards such as + - Moteino http://lowpowerlab.com/moteino/ + - Anarduino Mini http://www.anarduino.com/mini/ + - RedBearLab Blend V1.0 http://redbearlab.com/blend/ (with Arduino 1.0.5 and RedBearLab Blend Add-On version 20140701) + - MoteinoMEGA https://lowpowerlab.com/shop/moteinomega + (with Arduino 1.0.5 and the MoteinoMEGA Arduino Core + https://github.com/LowPowerLab/Moteino/tree/master/MEGA/Core) + - ESP8266 on Arduino IDE and Boards Manager per https://github.com/esp8266/Arduino + Tested using Arduino 1.6.8 with esp8266 by ESP8266 Community version 2.1.0 + Also Arduino 1.8.1 with esp8266 by SparkFun Electronics 2.5.2 + Examples serial_reliable_datagram_* and ask_* are shown to work. + CAUTION: The GHz radio included in the ESP8266 is + not yet supported. + CAUTION: tests here show that when powered by an FTDI USB-Serial converter, + the ESP8266 can draw so much power when transmitting on its GHz WiFi that VCC will sag + causing random crashes. We strongly recommend a large cap, say 1000uF 10V on VCC if you are also using the WiFi. + - Various Talk2 Whisper boards eg https://wisen.com.au/store/products/whisper-node-lora. + Use Arduino Board Manager to install the Talk2 code support. + - etc. + +- STM32 F4 Discover board, using Arduino 1.8.2 or later and + Roger Clarkes Arduino_STM from https://github.com/rogerclarkmelbourne/Arduino_STM32 + Caution: with this library and board, sending text to Serial causes the board to hang in mysterious ways. + Serial2 emits to PA2. The default SPI pins are SCK: PB3, MOSI PB5, MISO PB4. + We tested with PB0 as slave select and PB1 as interrupt pin for various radios. RH_ASK and RH_Serial also work. + Also works with stm32duino 1.8.0 from https://github.com/stm32duino/Arduino_Core_STM32, wich can be + installed on Arduino with BoardManager. Select board: STM32 Discovery F407. + +- ChipKIT Core with Arduino IDE on any ChipKIT Core supported Digilent processor (tested on Uno32) + http://chipkit.net/wiki/index.php?title=ChipKIT_core + +- Maple and Flymaple boards with libmaple and the Maple-IDE development environment + http://leaflabs.com/devices/maple/ and http://www.open-drone.org/flymaple + +- Teensy including Teensy 3.1 and earlier built using Arduino IDE 1.0.5 to 1.6.4 and later with + teensyduino addon 1.18 to 1.23 and later. + http://www.pjrc.com/teensy + +- Particle Photon https://store.particle.io/collections/photon and ARM3 based CPU with built-in + Wi-Fi transceiver and extensive IoT software suport. RadioHead does not support the built-in transceiver + but can be used to control other SPI based radios, Serial ports etc. + See below for details on how to build RadioHead for Photon + +- ATTiny built using Arduino IDE 1.8 and the ATTiny core from + https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json + using the instructions at + https://medium.com/jungletronics/attiny85-easy-flashing-through-arduino-b5f896c48189 + (Caution: these are very small processors and not all RadioHead features may be available, depending on memory requirements) + (Caution: we have not had good success building RH_ASK sketches for ATTiny 85 with SpenceKonde ATTinyCore) + +- ATtiny Mega (tinyAVR 1-series) chips supported by Spencer Konde's megaTinyCore + (https://github.com/SpenceKonde/megaTinyCore) + (on Arduino 1.8.9 or later) such as AtTiny 3216, ATtiny 1616 etc. These chips can be easily programmed through their + UPDI pin, using an ordinary Arduino board programmed as a jtag2updi programmer as described in + https://github.com/SpenceKonde/megaTinyCore/blob/master/MakeUPDIProgrammer.md. + Make sure you set the programmer type to jtag2updi in the Arduino Tools->Programmer menu. + See https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/ImportantInfo.md for links to pinouts + and pin numbering information for all the suported chips. + +- nRF51 compatible Arm chips such as nRF51822 with Arduino 1.6.4 and later using the procedures + in http://redbearlab.com/getting-started-nrf51822/ + +- nRF52 compatible Arm chips such as as Adafruit BLE Feather board + https://www.adafruit.com/product/3406 + +- Adafruit Feather. These are excellent boards that are available with a variety of radios. We tested with the + Feather 32u4 with RFM69HCW radio, with Arduino IDE 1.6.8 and the Adafruit AVR Boards board manager version 1.6.10. + https://www.adafruit.com/products/3076 + +- Adafruit Feather M0 boards with Arduino 1.8.1 and later, using the Arduino and Adafruit SAMD board support. + https://learn.adafruit.com/adafruit-feather-m0-basic-proto/using-with-arduino-ide + +- ESP32 built using Arduino IDE 1.8.9 or later using the ESP32 toolchain installed per + https://github.com/espressif/arduino-esp32 + The internal 2.4GHz radio is not yet supported. Tested with RFM22 using SPI interface + +- Raspberry Pi + Uses BCM2835 library for GPIO http://www.airspayce.com/mikem/bcm2835/ + Currently works only with RH_NRF24 driver or other drivers that do not require interrupt support. + Contributed by Mike Poublon. + +- Linux and OSX + Using the RHutil/HardwareSerial class, the RH_Serial driver and any manager will + build and run on Linux and OSX. These can be used to build programs that talk securely and reliably to + Arduino and other processors or to other Linux or OSX hosts on a reliable, error detected (and possibly encrypted) datagram + protocol over various types of serial line. + +- Mongoose OS, courtesy Paul Austen. Mongoose OSis an Internet of Things Firmware Development Framework + available under Apache License Version 2.0. It supports low power, connected microcontrollers such as: + ESP32, ESP8266, TI CC3200, TI CC3220, STM32. + https://mongoose-os.com/ + +- muRata cmwx1zzabz module, which includes an STM32L0 processor, + a SX1276 LoRa radio and an antenna switch. + +Other platforms are partially supported, such as Generic AVR 8 bit processors, MSP430. +We welcome contributions that will expand the range of supported platforms. + +If your processor is not on the list above, there is a good chance it +wont work without modifying RadioHead to suit it. If you wish for +support for another processor, and you send 2 of them to +AirSpayce Pty Ltd, we will consider adding support for it. + +RadioHead is available (through the efforts of others) +for PlatformIO. PlatformIO is a cross-platform code builder and the missing library manager. +http://platformio.org/#!/lib/show/124/RadioHead + +\par History + +RadioHead was created in April 2014, substantially based on code from some of our other earlier Radio libraries: + +- RHMesh, RHRouter, RHReliableDatagram and RHDatagram are derived from the RF22 library version 1.39. +- RH_RF22 is derived from the RF22 library version 1.39. +- RH_RF69 is derived from the RF69 library version 1.2. +- RH_ASK is based on the VirtualWire library version 1.26, after significant conversion to C++. +- RH_Serial was new. +- RH_NRF24 is based on the NRF24 library version 1.12, with some significant changes. + +During this combination and redevelopment, we have tried to retain all the processor dependencies and support from +the libraries that were contributed by other people. However not all platforms can be tested by us, so if you +find that support from some platform has not been successfully migrated, please feel free to fix it and send us a +patch. + +Users of RHMesh, RHRouter, RHReliableDatagram and RHDatagram in the previous RF22 library will find that their +existing code will run mostly without modification. See the RH_RF22 documentation for more details. + +\par Installation + +Install in the usual way: unzip the distribution zip file to the libraries +sub-folder of your sketchbook. +The example sketches will be visible in in your Arduino, mpide, maple-ide or whatever. +http://arduino.cc/en/Guide/Libraries + +\par Building for Particle Photon + +The Photon is not supported by the Arduino IDE, so it takes a little effort to set up a build environment. +Heres what we did to enable building of RadioHead example sketches on Linux, +but there are other ways to skin this cat. +Basic reference for getting started is: http://particle-firmware.readthedocs.org/en/develop/build/ +- Download the ARM gcc cross compiler binaries and unpack it in a suitable place: +\code +cd /tmp +wget https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 +tar xvf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 +\endcode +- If dfu-util and friends not installed on your platform, download dfu-util and friends to somewhere in your path +\code +cd ~/bin +wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-util +wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-suffix +wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-prefix +\endcode +- Download the Particle firmware (contains headers and libraries require to compile Photon sketches) + to a suitable place: +\code +cd /tmp +wget https://github.com/spark/firmware/archive/develop.zip +unzip develop.zip +\endcode +- Make a working area containing the RadioHead library source code and your RadioHead sketch. You must + rename the sketch from .pde or .ino to application.cpp +\code +cd /tmp +mkdir RadioHead +cd RadioHead +cp /usr/local/projects/arduino/libraries/RadioHead/ *.h . +cp /usr/local/projects/arduino/libraries/RadioHead/ *.cpp . +cp /usr/local/projects/arduino/libraries/RadioHead/examples/cc110/cc110_client/cc110_client.pde application.cpp +\endcode +- Edit application.cpp and comment out any \#include so it looks like: +\code + // #include +\endcode +- Connect your Photon by USB. Put it in DFU mode as descibed in Photon documentation. Light should be flashing yellow +- Compile the RadioHead sketch and install it as the user program (this does not update the rest of the + Photon firmware, just the user part: +\code +cd /tmp/firmware-develop/main +PATH=$PATH:/tmp/gcc-arm-none-eabi-5_2-2015q4/bin make APPDIR=/tmp/RadioHead all PLATFORM=photon program-dfu +\endcode +- You should see RadioHead compile without errors and download the finished sketch into the Photon. + +\par Compatible Hardware Suppliers + +We have had good experiences with the following suppliers of RadioHead compatible hardware: + +- LittleBird http://littlebirdelectronics.com.au in Australia for all manner of Arduinos and radios. +- LowPowerLab http://lowpowerlab.com/moteino in USA for the excellent Moteino and Moteino-USB + boards which include Hope-RF RF69B radios on-board. +- Anarduino and HopeRF USA (http://www.hoperfusa.com and http://www.anarduino.com) who have a wide range + of HopeRF radios and Arduino integrated modules. +- SparkFun https://www.sparkfun.com/ in USA who design and sell a wide range of Arduinos and radio modules. +- Wisen http://wisen.com.au who design and sell a wide range of integrated radio/processor modules including the + excellent Talk2 range. + +\par Coding Style + +RadioHead is designed so it can run on small processors with very +limited resources and strict timing contraints. As a result, we +tend only to use the simplest and least demanding (in terms of memory and CPU) C++ +facilities. In particular we avoid as much as possible dynamic +memory allocation, and the use of complex objects like C++ +strings, IO and buffers. We are happy with this, but we are aware +that some people may think we are legaving useful tools on the +table. You should not use this code as an example of how to do +generalised C++ programming on well resourced processors. + +\par Donations + +This library is offered under a free GPL license for those who want to use it that way. +We try hard to keep it up to date, fix bugs +and to provide free support. If this library has helped you save time or money, please consider donating at +http://www.airspayce.com or here: + +\htmlonly
\endhtmlonly + +\subpage packingdata "Passing Sensor Data Between RadioHead nodes" + +\par Trademarks + +RadioHead is a trademark of AirSpayce Pty Ltd. The RadioHead mark was first used on April 12 2014 for +international trade, and is used only in relation to data communications hardware and software and related services. +It is not to be confused with any other similar marks covering other goods and services. + +\par Copyright + +This software is Copyright (C) 2011-2018 Mike McCauley. Use is subject to license +conditions. The main licensing options available are GPL V3 or Commercial: + +\par Open Source Licensing GPL V3 + +This is the appropriate option if you want to share the source code of your +application with everyone you distribute it to, and you also want to give them +the right to share who uses it. If you wish to use this software under Open +Source Licensing, you must contribute all your source code to the open source +community in accordance with the GPL Version 3 when your application is +distributed. See https://www.gnu.org/licenses/gpl-3.0.html + +\par Commercial Licensing + +This is the appropriate option if you are creating proprietary applications +and you are not prepared to distribute and share the source code of your +application. To purchase a commercial license, contact info@airspayce.com + +\par Revision History +\version 1.1 2014-04-14
+ Initial public release +\version 1.2 2014-04-23
+ Fixed various typos.
+ Added links to compatible Anarduino products.
+ Added RHNRFSPIDriver, RH_NRF24 classes to support Nordic NRF24 based radios. +\version 1.3 2014-04-28
+ Various documentation fixups.
+ RHDatagram::setThisAddress() did not set the local copy of thisAddress. Reported by Steve Childress.
+ Fixed a problem on Teensy with RF22 and RF69, where the interrupt pin needs to be set for input,
+ else pin interrupt doesn't work properly. Reported by Steve Childress and patched by + Adrien van den Bossche. Thanks.
+ Fixed a problem that prevented RF22 honouring setPromiscuous(true). Reported by Steve Childress.
+ Updated documentation to clarify some issues to do with maximum message lengths + reported by Steve Childress.
+ Added support for yield() on systems that support it (currently Arduino 1.5.5 and later) + so that spin-loops can suport multitasking. Suggested by Steve Childress.
+ Added RH_RF22::setGpioReversed() so the reversal it can be configured at run-time after + radio initialisation. It must now be called _after_ init(). Suggested by Steve Childress.
+\version 1.4 2014-04-29
+ Fixed further problems with Teensy compatibility for RH_RF22. Tested on Teensy 3.1. + The example/rf22_* examples now run out of the box with the wiring connections as documented for Teensy + in RH_RF22.
+ Added YIELDs to spin-loops in RHRouter, RHMesh and RHReliableDatagram, RH_NRF24.
+ Tested RH_Serial examples with Teensy 3.1: they now run out of the box.
+ Tested RH_ASK examples with Teensy 3.1: they now run out of the box.
+ Reduced default SPI speed for NRF24 from 8MHz to 1MHz on Teensy, to improve reliability when + poor wiring is in use.
+ on some devices such as Teensy.
+ Tested RH_NRF24 examples with Teensy 3.1: they now run out of the box.
+\version 1.5 2014-04-29
+ Added support for Nordic Semiconductor nRF905 transceiver with RH_NRF905 driver. Also + added examples for nRF905 and tested on Teensy 3.1 +\version 1.6 2014-04-30
+ NRF905 examples were missing +\version 1.7 2014-05-03
+ Added support for Arduino Due. Tested with RH_NRF905, RH_Serial, RH_ASK. + IMPORTANT CHANGE to interrupt pins on Arduino with RH_RF22 and RH_RF69 constructors: + previously, you had to specify the interrupt _number_ not the interrupt _pin_. Arduinos and Uno32 + are now consistent with all other platforms: you must specify the interrupt pin number. Default + changed to pin 2 (a common choice with RF22 shields). + Removed examples/maple/maple_rf22_reliable_datagram_client and + examples/maple/maple_rf22_reliable_datagram_client since the rf22 examples now work out + of the box with Flymaple. + Removed examples/uno32/uno32_rf22_reliable_datagram_client and + examples/uno32/uno32_rf22_reliable_datagram_client since the rf22 examples now work out + of the box with ChipKit Uno32. +\version 1.8 2014-05-08
+ Added support for YIELD in Teensy 2 and 3, suggested by Steve Childress.
+ Documentation updates. Clarify use of headers and Flags
+ Fixed misalignment in RH_RF69 between ModemConfigChoice definitions and the implemented choices + which meant you didnt get the choice you thought and GFSK_Rb55555Fd50 hung the transmitter.
+ Preliminary work on Linux simulator. +\version 1.9 2014-05-14
+ Added support for using Timer 2 instead of Timer 1 on Arduino in RH_ASK when + RH_ASK_ARDUINO_USE_TIMER2 is defined. With the kind assistance of + Luc Small. Thanks!
+ Updated comments in RHReliableDatagram concerning servers, retries, timeouts and delays. + Fixed an error in RHReliableDatagram where recvfrom return value was not checked. + Reported by Steve Childress.
+ Added Linux simulator support so simple RadioHead sketches can be compiled and run on Linux.
+ Added RH_TCP driver to permit message passing between simulated sketches on Linux.
+ Added example simulator sketches.
+ Added tools/etherSimulator.pl, a simulator of the 'Luminiferous Ether' that passes + messages between simulated sketches and can simulate random message loss etc.
+ Fixed a number of typos and improved some documentation.
+\version 1.10 2014-05-15
+ Added support for RFM73 modules to RH_NRF24. These 2 radios are very similar, and can interoperate + with each other. Added new RH_NRF24::TransmitPower enums for the RFM73, which has a different + range of available powers
+ reduced the default SPI bus speed for RH_NRF24 to 1MHz, since so many modules and CPU have problems + with 8MHz.
+\version 1.11 2014-05-18
+ Testing RH_RF22 with RFM23BP and 3.3V Teensy 3.1 and 5V Arduinos. + Updated documentation with respect to GPIO and antenna + control pins for RFM23. Updated documentation with respect to transmitter power control for RFM23
+ Fixed a problem with RH_RF22 driver, where GPIO TX and RX pins were not configured during + initialisation, causing poor transmit power and sensitivity on those RF22/RF23 devices where GPIO controls + the antenna selection pins. +\version 1.12 2014-05-20
+ Testing with RF69HW and the RH_RF69 driver. Works well with the Anarduino MiniWireless -CW and -HW + boards http://www.anarduino.com/miniwireless/ including + the marvellous high powered MinWireless-HW (with 20dBm output for excellent range).
+ Clarified documentation of RH_RF69::setTxPower values for different models of RF69.
+ Added RHReliableDatagram::resetRetransmissions().
+ Retransmission count precision increased to uin32_t.
+ Added data about actual power measurements from RFM22 module.
+\version 1.13 2014-05-23
+ setHeaderFlags(flags) changed to setHeaderFlags(set, clear), enabling any flags to be + individually set and cleared by either RadioHead or application code. Requested by Steve Childress.
+ Fixed power output setting for boost power on RF69HW for 18, 19 and 20dBm.
+ Added data about actual power measurements from RFM69W and RFM69HW modules.
+\version 1.14 2014-05-26
+ RH_RF69::init() now always sets the PA boost back to the default settings, else can get invalid + PA power modes after uploading new sketches without a power cycle. Reported by Bryan.
+ Added new macros RH_VERSION_MAJOR RH_VERSION_MINOR, with automatic maintenance in Makefile.
+ Improvements to RH_TCP: constructor now honours the server argument in the form "servername:port".
+ Added YIELD to RHReliableDatagram::recvfromAckTimeout. Requested by Steve Childress.
+ Fixed a problem with RH_RF22 reliable datagram acknowledgements that was introduced in version 1.13. + Reported by Steve Childress.
+\version 1.15 2014-05-27
+ Fixed a problem with the RadioHead .zip link. +\version 1.16 2014-05-30
+ Fixed RH_RF22 so that lastRssi() returns the signal strength in dBm. Suggested by Steve Childress.
+ Added support for getLastPreambleTime() to RH_RF69. Requested by Steve Childress.
+ RH_NRF24::init() now checks if there is a device connected and responding, else init() will fail. + Suggested by Steve Brown.
+ RHSoftwareSPI now initialises default values for SPI pins MOSI = 12, MISO = 11 and SCK = 13.
+ Fixed some problems that prevented RH_NRF24 working with mixed software and hardware SPI + on different devices: a race condition + due to slow SPI transfers and fast acknowledgement.
+\version 1.17 2014-06-02
+ Fixed a debug typo in RHReliableDatagram that was introduced in 1.16.
+ RH_NRF24 now sets default power, data rate and channel in init(), in case another + app has previously set different values without powerdown.
+ Caution: there are still problems with RH_NRF24 and Software SPI. Do not use.
+\version 1.18 2014-06-02
+ Improvements to performance of RH_NRF24 statusRead, allowing RH_NRF24 and Software SPI + to operate on slow devices like Arduino Uno.
+\version 1.19 2014-06-19
+ Added examples ask_transmitter.pde and ask_receiver.pde.
+ Fixed an error in the RH_RF22 doc for connection of Teensy to RF22.
+ Improved documentation of start symbol bit patterns in RH_ASK.cpp +\version 1.20 2014-06-24
+ Fixed a problem with compiling on platforms such as ATtiny where SS is not defined.
+ Added YIELD to RHMesh::recvfromAckTimeout().
+\version 1.21 2014-06-24
+ Fixed an issue in RH_Serial where characters might be lost with back-to-back frames. + Suggested by Steve Childress.
+ Brought previous RHutil/crc16.h code into mainline RHCRC.cpp to prevent name collisions + with other similarly named code in other libraries. Suggested by Steve Childress.
+ Fix SPI bus speed errors on 8MHz Arduinos. +\version 1.22 2014-07-01
+ Update RH_ASK documentation for common wiring connections.
+ Testing RH_ASK with HopeRF RFM83C/RFM85 courtesy Anarduino http://www.anarduino.com/
+ Testing RH_NRF24 with Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html + using both hardware SPI on the ITDB02 Parallel LCD Module Interface pins and software SPI + on the nRF24L01+ Module Interface pins. Documented wiring required.
+ Added support for AVR 1284 and 1284p, contributed by Peter Scargill. + Added support for Semtech SX1276/77/78 and HopeRF RFM95/96/97/98 and other similar LoRa capable radios + in LoRa mode only. Tested with the excellent MiniWirelessLoRa from + Anarduino http://www.anarduino.com/miniwireless
+\version 1.23 2014-07-03
+ Changed the default modulation for RH_RF69 to GFSK_Rb250Fd250, since the previous default + was not very reliable.
+ Documented RH_RF95 range tests.
+ Improvements to RH_RF22 RSSI readings so that lastRssi correctly returns the last message in dBm.
+\version 1.24 2014-07-18 + Added support for building RadioHead for STM32F4 Discovery boards, using the native STM Firmware libraries, + in order to support Codec2WalkieTalkie (http://www.airspayce.com/mikem/Codec2WalkieTalkie) + and other projects. See STM32ArduinoCompat.
+ Default modulation for RH_RF95 was incorrectly set to a very slow Bw125Cr48Sf4096 +\version 1.25 2014-07-25 + The available() function will longer terminate any current transmission, and force receive mode. + Now, if there is no unprocessed incoming message and an outgoing message is currently being transmitted, + available() will return false.
+ RHRouter::sendtoWait(uint8_t*, uint8_t, uint8_t, uint8_t) renamed to sendtoFromSourceWait due to conflicts + with new sendtoWait() with optional flags.
+ RHMEsh and RHRouter already supported end-to-end application layer flags, but RHMesh::sendtoWait() + and RHRouter::sendToWait have now been extended to expose a way to send optional application layer flags. +\version 1.26 2014-08-12 + Fixed a Teensy 2.0 compile problem due yield() not available on Teensy < 3.0.
+ Adjusted the algorithm of RH_RF69::temperatureRead() to more closely reflect reality.
+ Added functions to RHGenericDriver to get driver packet statistics: rxBad(), rxGood(), txGood().
+ Added RH_RF69::printRegisters().
+ RH_RF95::printRegisters() was incorrectly printing the register index instead of the address. + Reported by Phang Moh Lim.
+ RH_RF95, added definitions for some more registers that are usable in LoRa mode.
+ RH_RF95::setTxPower now uses RH_RF95_PA_DAC_ENABLE to achieve 21, 22 and 23dBm.
+ RH_RF95, updated power output measurements.
+ Testing RH_RF69 on Teensy 3.1 with RF69 on PJRC breakout board. OK.
+ Improvements so RadioHead will build under Arduino where SPI is not supported, such as + ATtiny.
+ Improvements so RadioHead will build for ATTiny using Arduino IDE and tinycore arduino-tiny-0100-0018.zip.
+ Testing RH_ASK on ATTiny85. Reduced RAM footprint. + Added helpful documentation. Caution: RAM memory is *very* tight on this platform.
+ RH_RF22 and RH_RF69, added setIdleMode() function to allow the idle mode radio operating state + to be controlled for lower idle power consumption at the expense of slower transitions to TX and RX.
+\version 1.27 2014-08-13 + All RH_RF69 modulation schemes now have data whitening enabled by default.
+ Tested and added a number of OOK modulation schemes to RH_RF69 Modem config table.
+ Minor improvements to a number of the faster RH_RF69 modulation schemes, but some slower ones + are still not working correctly.
+\version 1.28 2014-08-20 + Added new RH_RF24 driver to support Si446x, RF24/26/26, RFM24/26/27 family of transceivers. + Tested with the excellent + Anarduino Mini and RFM24W and RFM26W with the generous assistance of the good people at + Anarduino http://www.anarduino.com. +\version 1.29 2014-08-21 + Fixed a compile error in RH_RF24 introduced at the last minute in hte previous release.
+ Improvements to RH_RF69 modulation schemes: now include the AFCBW in teh ModemConfig.
+ ModemConfig RH_RF69::FSK_Rb2Fd5 and RH_RF69::GFSK_Rb2Fd5 are now working.
+\version 1.30 2014-08-25 + Fixed some compile problems with ATtiny84 on Arduino 1.5.5 reported by Glen Cook.
+\version 1.31 2014-08-27 + Changed RH_RF69 FSK and GFSK modulations from Rb2_4Fd2_4 to Rb2_4Fd4_8 and FSK_Rb4_8Fd4_8 to FSK_Rb4_8Fd9_6 + since the previous ones were unreliable (they had modulation indexes of 1).
+\version 1.32 2014-08-28 + Testing with RedBearLab Blend board http://redbearlab.com/blend/. OK.
+ Changed more RH_RF69 FSK and GFSK slowish modulations to have modulation index of 2 instead of 1. + This required chnaging the symbolic names.
+\version 1.33 2014-09-01 + Added support for sleep mode in RHGeneric driver, with new mode + RHModeSleep and new virtual function sleep().
+ Added support for sleep to RH_RF69, RH_RF22, RH_NRF24, RH_RF24, RH_RF95 drivers.
+\version 1.34 2014-09-19 + Fixed compile errors in example rf22_router_test.
+ Fixed a problem with RH_NRF24::setNetworkAddress, also improvements to RH_NRF24 register printing. + Patched by Yveaux.
+ Improvements to RH_NRF24 initialisation for version 2.0 silicon.
+ Fixed problem with ambigiguous print call in RH_RFM69 when compiling for Codec2.
+ Fixed a problem with RH_NRF24 on RFM73 where the LNA gain was not set properly, reducing the sensitivity + of the receiver. +\version 1.35 2014-09-19 + Fixed a problem with interrupt setup on RH_RF95 with Teensy3.1. Reported by AD.
+\version 1.36 2014-09-22 + Improvements to interrupt pin assignments for __AVR_ATmega1284__ and__AVR_ATmega1284P__, provided by + Peter Scargill.
+ Work around a bug in Arduino 1.0.6 where digitalPinToInterrupt is defined but NOT_AN_INTERRUPT is not.
+ \version 1.37 2014-10-19 + Updated doc for connecting RH_NRF24 to Arduino Mega.
+ Changes to RHGenericDriver::setHeaderFlags(), so that the default for the clear argument + is now RH_FLAGS_APPLICATION_SPECIFIC, which is less surprising to users. + Testing with the excellent MoteinoMEGA from LowPowerLab + https://lowpowerlab.com/shop/moteinomega with on-board RFM69W. + \version 1.38 2014-12-29 + Fixed compile warning on some platforms where RH_RF24::send and RH_RF24::writeTxFifo + did not return a value.
+ Fixed some more compiler warnings in RH_RF24 on some platforms.
+ Refactored printRegisters for some radios. Printing to Serial + is now controlled by the definition of RH_HAVE_SERIAL.
+ Added partial support for ARM M4 w/CMSIS with STM's Hardware Abstraction lib for + Steve Childress.
+ \version 1.39 2014-12-30 + Fix some compiler warnings under IAR.
+ RH_HAVE_SERIAL and Serial.print calls removed for ATTiny platforms.
+ \version 1.40 2015-03-09 + Added notice about availability on PlatformIO, thanks to Ivan Kravets.
+ Fixed a problem with RH_NRF24 where short packet lengths would occasionally not be trasmitted + due to a race condition with RH_NRF24_TX_DS. Reported by Mark Fox.
+ \version 1.41 2015-03-29 + RH_RF22, RH_RF24, RH_RF69 and RH_RF95 improved to allow driver.init() to be called multiple + times without reallocating a new interrupt, allowing the driver to be reinitialised + after sleeping or powering down. + \version 1.42 2015-05-17 + Added support for RH_NRF24 driver on Raspberry Pi, using BCM2835 + library for GPIO pin IO. Contributed by Mike Poublon.
+ Tested RH_NRF24 module with NRF24L01+PA+LNA SMA Antenna Wireless Transceiver modules + similar to: http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA + works with no software changes. Measured max power output 18dBm.
+ \version 1.43 2015-08-02 + Added RH_NRF51 driver to support Nordic nRF51 family processor with 2.4GHz radio such + as nRF51822, to be built on Arduino 1.6.4 and later. Tested with RedBearLabs nRF51822 board + and BLE Nano kit
+ \version 1.44 2015-08-08 + Fixed errors with compiling on some platforms without serial, such as ATTiny. + Reported by Friedrich Müller.
+ \version 1.45 2015-08-13 + Added support for using RH_Serial on Linux and OSX (new class RHutil/HardwareSerial + encapsulates serial ports on those platforms). Example examples/serial upgraded + to build and run on Linux and OSX using the tools/simBuild builder. + RHMesh, RHRouter and RHReliableDatagram updated so they can use RH_Serial without + polling loops on Linux and OSX for CPU efficiency.
+ \version 1.46 2015-08-14 + Amplified some doc concerning Linux and OSX RH_Serial. Added support for 230400 + baud rate in HardwareSerial.
+ Added sample sketches nrf51_audio_tx and nrf51_audio_rx which show how to + build an audio TX/RX pair with RedBear nRF51822 boards and a SparkFun MCP4725 DAC board. + Uses the built-in ADC of the nRF51822 to sample audio at 5kHz and transmit packets + to the receiver which plays them via the DAC.
+\version 1.47 2015-09-18 + Removed top level Makefile from distribution: its only used by the developer and + its presence confuses some people.
+ Fixed a problem with RHReliableDatagram with some versions of Raspberry Pi random() that causes + problems: random(min, max) sometimes exceeds its max limit. +\version 1.48 2015-09-30 + Added support for Arduino Zero. Tested on Arduino Zero Pro. +\version 1.49 2015-10-01 + Fixed problems that prevented interrupts working correctly on Arduino Zero and Due. + Builds and runs with 1.6.5 (with 'Arduino SAMD Boards' for Zero version 1.6.1) from arduino.cc. + Arduino version 1.7.7 from arduino.org is not currently supported. +\version 1.50 2015-10-25 + Verified correct building and operation with Arduino 1.7.7 from arduino.org. + Caution: You must burn the bootloader from 1.7.7 to the Arduino Zero before it will + work with Arduino 1.7.7 from arduino.org. Conversely, you must burn the bootloader from 1.6.5 + to the Arduino Zero before it will + work with Arduino 1.6.5 from arduino.cc. Sigh. + Fixed a problem with RH_NRF905 that prevented the power and frequency ranges being set + properly. Reported by Alan Webber. +\version 1.51 2015-12-11 + Changes to RH_RF6::setTxPower() to be compatible with SX1276/77/78/79 modules that + use RFO transmitter pins instead of PA_BOOST, such as the excellent + Modtronix inAir4 http://modtronix.com/inair4.html + and inAir9 modules http://modtronix.com/inair9.html. With the kind assistance of + David from Modtronix. +\version 1.52 2015-12-17 + Added RH_MRF89 module to suport Microchip MRF89XA and compatible transceivers. + and modules.
+\version 1.53 2016-01-02 + Added RH_CC110 module to support Texas Instruments CC110L and compatible transceivers and modules.
+\version 1.54 2016-01-29 + Added support for ESP8266 processor on Arduino IDE. Examples serial_reliable_datagram_* are shown to work. + CAUTION: SPI not supported yet. Timers used by RH_ASK are not tested. + The GHz radio included in the ESP8266 is not yet supported. +\version 1.55 2016-02-12 + Added macros for htons() and friends to RadioHead.h. + Added example sketch serial_gateway.pde. Acts as a transparent gateway between RH_RF22 and RH_Serial, + and with minor mods acts as a universal gateway between any 2 RadioHead driver networks. + Initial work on supporting STM32 F2 on Particle Photon: new platform type defined. + Fixed many warnings exposed by test building for Photon. + Particle Photon tested support for RH_Serial, RH_ASK, SPI, RH_CC110 etc. + Added notes on how to build RadioHead sketches for Photon. +\version 1.56 2016-02-18 + Implemented timers for RH_ASK on ESP8266, added some doc on IO pin selection. +\version 1.57 2016-02-23 + Fixed an issue reported by S3B, where RH_RF22 would sometimes not clear the rxbufvalid flag. +\version 1.58 2-16-04-04 + Tested RH_RF69 with Arduino Due. OK. Updated doc.
+ Added support for all ChipKIT Core supported boards + http://chipkit.net/wiki/index.php?title=ChipKIT_core + Tested on ChipKIT Uno32.
+ Digilent Uno32 under the old MPIDE is no longer formally + supported but may continue to work for some time.
+\version 1.59 2016-04-12 + Testing with the excellent Rocket Scream Mini Ultra Pro with the RFM95W and RFM69HCW modules from + http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/ (915MHz versions). Updated + documentation with hints to suit. Caution: requires Arduino 1.6.8 and Arduino SAMD Boards 1.6.5. + See also http://www.rocketscream.com/blog/2016/03/10/radio-range-test-with-rfm69hcw/ + for the vendors tests and range with the RFM69HCW version. They also have an RF95 version equipped with + TCXO temperature controllled oscillator for extra frequency stability and support of very slow and + long range protocols. + These boards are highly recommended. They also include battery charging support. +\version 1.60 2016-06-25 + Tested with the excellent talk2 Whisper Node boards + (https://talk2.wisen.com.au/ and https://bitbucket.org/talk2/), + an Arduino Nano compatible board, which include an on-board RF69 radio, external antenna, + run on 2xAA batteries and support low power operations. RF69 examples work without modification. + Added support for ESP8266 SPI, provided by David Skinner. +\version 1.61 2016-07-07 + Patch to RH_ASK.cpp for ESP8266, to prevent crashes in interrupt handlers. Patch from Alexander Mamchits. +\version 1.62 2016-08-17 + Fixed a problem in RH_ASK where _rxInverted was not properly initialised. Reported by "gno.sun.sop". + Added support for waitCAD() and isChannelActive() and setCADTimeout() to RHGeneric. + Implementation of RH_RF95::isChannelActive() allows the RF95 module to support + Channel Activity Detection (CAD). Based on code contributed by Bent Guldbjerg Christensen. + Implmentations of isChannelActive() plus documentation for other radio modules wil be welcomed. +\version 1.63 2016-10-20 + Testing with Adafruit Feather 32u4 with RFM69HCW. Updated documentation to reflect.
+\version 1.64 2016-12-10 + RHReliableDatagram now initialises _seenids. Fix from Ben Lim.
+ In RH_NRF51, added get_temperature().
+ In RH_NRF51, added support for AES packet encryption, which required a slight change + to the on-air message format.
+\version 1.65 2017-01-11 + Fixed a race condition with RH_NRF51 that prevented ACKs being reliably received.
+ Removed code in RH_NRF51 that enabled the DC-DC converter. This seems not to be a necessary condition + for the radio to work and is now left to the application if that is required.
+ Proven interoperation between nRF51822 and nRF52832.
+ Modification and testing of RH_NRF51 so it works with nRF52 family processors, + such Sparkfun nRF52832 breakout board, with Arduino 1.6.13 and + Sparkfun nRF52 boards manager 0.2.3 using the procedures outlined in + https://learn.sparkfun.com/tutorials/nrf52832-breakout-board-hookup-guide
+ Caution, the Sparkfun development system for Arduino is still immature. We had to + rebuild the nrfutil program since the supplied one was not suitable for + the Linux host we were developing on. See https://forum.sparkfun.com/viewtopic.php?f=32&t=45071 + Also, after downloading a sketch in the nRF52832, the program does not start executing cleanly: + you have to reset the processor again by pressing the reset button. + This appears to be a problem with nrfutil, rather than a bug in RadioHead. +\version 1.66 2017-01-15 + Fixed some errors in (unused) register definitions in RH_RF95.h.
+ Fixed a problem that caused compilation errors in RH_NRF51 if the appropriate board + support was not installed. +\version 1.67 2017-01-24 + Added RH_RF95::frequencyError() to return the estimated centre frequency offset in Hz + of the last received message +\version 1.68 2017-01-25 + Fixed arithmetic error in RH_RF95::frequencyError() for some platforms. +\version 1.69 2017-02-02 + Added RH_RF95::lastSNR() and improved lastRssi() calculations per the manual. +\version 1.70 2017-02-03 + Added link to Binpress commercial license purchasing. +\version 1.71 2017-02-07 + Improved support for STM32. Patch from Bent Guldbjerg Christensen. +\version 1.72 2017-03-02 + In RH_RF24, fixed a problem where some important properties were not set by the ModemConfig. + Added properties 2007, 2008, 2009. Also properties 200a was not being set in the chip. + Reported by Shannon Bailey and Alan Adamson. + Fixed corresponding convert.pl and added it to the distribution. +\version 1.73 2017-03-04 + Significant changes to RH_RF24 and its API. It is no longer possible to change the modulation scheme + programatically: it proved impossible to cater for all the possible crystal frequencies, + base frequency and modulation schemes. Instead you can use one of a small set of supplied radio + configuration header files, or generate your own with Silicon Labs WDS application. Changing + modulation scheme required editing RH_RF24.cpp to specify the appropriate header and recompiling. + convert.pl is now redundant and removed from the distribution. +\version 1.74 2017-03-08 + Changed RHReliableDatagram so it would not ACK messages heard addressed to other nodes + in promiscuous mode.
+ Added RH_RF24::deviceType() to return the integer value of the connected device.
+ Added documentation about how to connect RFM69 to an ESP8266. Tested OK.
+ RH_RF24 was not correctly changing state in sleep() and setModeIdle().
+ Added example rf24_lowpower_client.pde showing how to put an arduino and radio into a low power + mode between transmissions to save battery power.
+ Improvements to RH_RF69::setTxPower so it now takes an optional ishighpowermodule + flag to indicate if the connected module is a high power RFM69HW, and so set the power level + correctly. Based on code contributed by bob. +\version 1.75 2017-06-22 + Fixed broken compiler issues with RH_RF95::frequencyError() reported by Steve Rogerson.
+ Testing with the very excellent Rocket Scream boards equipped with RF95 TCXO modules. The + temperature controlled oscillator stabilises the chip enough to be able to use even the slowest + protocol Bw125Cr48Sf4096. Caution, the TCXO model radios are not low power when in sleep (consuming + about ~600 uA, reported by Phang Moh Lim).
+ Added support for EBYTE E32-TTL-1W and family serial radio transceivers. These RF95 LoRa based radios + can deliver reliable messages at up to 7km measured. +\version 1.76 2017-06-23 + Fixed a problem with RH_RF95 hanging on transmit under some mysterious circumstances. + Reported by several people at https://forum.pjrc.com/threads/41878-Probable-race-condition-in-Radiohead-library?p=146601#post146601
+ Increased the size of rssi variables to 16 bits to permit RSSI less than -128 as reported by RF95. +\version 1.77 2017-06-25 + Fixed a compilation error with lastRssi().
+\version 1.78 2017-07-19 + Fixed a number of unused variable warnings from g++.
+ Added new module RHEncryptedDriver and examples, contributed by Philippe Rochat, which + adds encryption and decryption to any RadioHead transport driver, using any encryption cipher + supported by ArduinoLibs Cryptographic Library http://rweather.github.io/arduinolibs/crypto.html + Includes several examples.
+\version 1.79 2017-07-25 + Added documentation about 'Passing Sensor Data Between RadioHead nodes'.
+ Changes to RH_CC110 driver to calculate RSSI in dBm, based on a patch from Jurie Pieterse.
+ Added missing passthroughmethoids to RHEncryptedDriver, allowing it to be used with RHDatagram, + RHReliableDatagram etc. Tested with RH_Serial. Added examples +\version 1.80 2017-10-04 + Testing with the very fine Talk2 Whisper Node LoRa boards https://wisen.com.au/store/products/whisper-node-lora + an Arduino compatible board, which include an on-board RFM95/96 LoRa Radio (Semtech SX1276), external antenna, + run on 2xAAA batteries and support low power operations. RF95 examples work without modification. + Use Arduino Board Manager to install the Talk2 code support. Upload the code with an FTDI adapter set to 5V.
+ Added support for SPI transactions in development environments that support it with SPI_HAS_TRANSACTION. + Tested on ESP32 with RFM-22 and Teensy 3.1 with RF69 + Added support for ESP32, tested with RFM-22 connected by SPI.
+\version 1.81 2017-11-15 + RH_CC110, moved setPaTable() from protected to public.
+ RH_RF95 modem config Bw125Cr48Sf4096 altered to enable slow daat rate in register 26 + as suggested by Dieter Kneffel. + Added support for nRF52 compatible Arm chips such as as Adafruit BLE Feather board + https://www.adafruit.com/product/3406, with a patch from Mike Bell.
+ Fixed a problem where rev 1.80 broke Adafruit M0 LoRa support by declaring + bitOrder variable always as a unsigned char. Reported by Guilherme Jardim.
+ In RH_RF95, all modes now have AGC enabled, as suggested by Dieter Kneffel.
+\version 1.82 2018-01-07 + Added guard code to RH_NRF24::waitPacketSent() so that if the transmit never completes for some + reason, the code will eventually return with FALSE. + Added the low-datarate-optimization bit to config for RH_RF95::Bw125Cr48Sf4096. + Fix from Jurie Pieterse to ensure RH_CC110::sleep always enters sleep mode. + Update ESP32 support to include ASK timers. RH_ASK module is now working on ESP32. +\version 1.83 2018-02-12 + Testing adafruit M0 Feather with E32. Updated RH_E32 documentation to show suggested connections + and contructor initialisation.
+ Fixed a problem with RHEncryptedDriver that could cause a crash on some platforms when used + with RHReliableDatagram. Reported by Joachim Baumann.
+ Improvments to doxygen doc layout in RadioHead.h +\version 1.84 2018-05-07 + Compiles with Roger Clarkes Arduino_STM32 https://github.com/rogerclarkmelbourne/Arduino_STM32, + to support STM32F103C etc, and STM32 F4 Discovery etc.
+ Tested STM32 F4 Discovery board with RH_RF22, RH_ASK and RH_Serial. + +\version 1.85 2018-07-09 + RHGenericDriver methods changed to virtual, to allow overriding by RHEncrypredDriver: + lastRssi(), mode(), setMode(). Reported by Eyal Gal.
+ Fixed a problem with compiling RH_E32 on some older IDEs, contributed by Philippe Rochat.
+ Improvements to RH_RF95 to improve detection of bad packets, contributed by PiNi.
+ Fixed an error in RHEncryptedDriver that caused incorrect message lengths for messages multiples of 16 bytes + when STRICT_CONTENT_LEN is defined.
+ Fixed a bug in RHMesh which causes the creation of a route to the address which is the byte + behind the end of the route array. Reported by Pascal Gillès de Pélichy.
+\version 1.86 2018-08-28 + Update commercial licensing, remove binpress. +\version 1.87 2018-10-06 + RH_RF22 now resets all registers to default state before initialisation commences. Suggested by Wothke.
+ Added RH_ENABLE_EXPLICIT_RETRY_DEDUP which improves the handling of duplicate detection especiually + in the case where a transmitter periodically wakes up and start tranmitting from the first sequence number. + Patch courtesy Justin Newitter. Thanks. +\version 1.88 2018-11-13 + Updated to support ATTiny using instructions in + https://medium.com/jungletronics/attiny85-easy-flashing-through-arduino-b5f896c48189 + Updated examples ask_transmitter and ask_receiver to compile cleanly on ATTiny. + Tested using ATTiny85 and Arduino 1.8.1.
+\version 1.89 2018-11-15 + Testing with ATTiny core from https://github.com/SpenceKonde/ATTinyCore and RH_ASK, + using example ask_transmitter. This resulted in 'Low Memory, instability may occur', + and the resulting sketch would transmit only one packet. Suggest ATTiny users do not use this core, but use + the one from https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json + as described in https://medium.com/jungletronics/attiny85-easy-flashing-through-arduino-b5f896c48189
+ Added support for RH_RF95::setSpreadingFactor(), RH_RF95::setSignalBandwidth(), RH_RF95::setLowDatarate() and + RH_RF95::setPayloadCRC(). Patch from Brian Norman. Thanks.
+ +\version 1.90 2019-05-21 + Fixed a block size error in RhEncryptedDriver for the case when + using STRICT_CONTENT_LEN and sending messages of exactly _blockcipher.blockSize() bytes in length. + Reported and patched by Philippe Rochat. + Patch from Samuel Archibald to prevent compile errors with RH_AAK.cpp fo ATSAMD51. + Fixed a probem in RH_RF69::setSyncWords that prevented setSyncWords(NULL, 0) correctly + disabling sync detection and generation. Reported by Federico Maggi. + RHHardwareSPI::usingInterrupt() was a noop. Fixed to call SPI.usingInterrupt(interrupt);. + +\version 1.91 2019-06-01 + Fixed a problem with new RHHardwareSPI::usingInterrupt() that prevented compilation on ESP8266 + which does not have that call. + +\version 1.92 2019-07-14 + Retested serial_reliable_datagram_client.pde and serial_reliable_datagram_server.pde built on Linux + as described in their headers, and with USB-RS485 adapters. No changes, working correctly. + Testing of nRF5232 with Sparkfun nRF52 board support 0.2.3 shows that there appears to be a problem with + interrupt handlers on this board, and none of the interrupt based radio drivers can be expected to work + with this chip. + Ensured all interrupt routines are flagged with ICACHE_RAM_ATTR when compiled for ESP8266, to prevent crashes. + +\version 1.94 2019-09-02 + Fixed a bug in RHSoftwareSPI where RHGenericSPI::setBitOrder() has no effect for + on RHSoftwareSPI. Reported by Peter.
+ Added support in RHRouter for a node to optionally be leaf node, and not participate as a router in the + network. See RHRouter::setNodeTypePatch from Alex Evans.
+ Fixed a problem with ESP32 causing compile errors over missing SPI.usingInterrupt().
+ +\version 1.95 2019-10-14 + Fixed some typos in RH_RF05.h macro definitions reported by Clayton Smith.
+ Patch from Michael Cain from RH_ASK on ESP32, untested by me.
+ Added support for RPi Zero and Zero W for the RF95, contributed by Brody Mahoney. + Not tested by me.
+ +\version 1.96 2019-10-14 + Added examples for RPi Zero and Zero W to examples/raspi/rf95, contributed by Brody Mahoney + not tested by me.
+ +\version 1.97 2019-11-02 + Added support for Mongoose OS, contributed by Paul Austen. + +\version 1.98 2020-01-06 + Rationalised use of RH_PLATFORM_ATTINY to be consistent with other platforms.
+ Added support for RH_PLATFORM_ATTINY_MEGA, for use with Spencer Konde's megaTinyCore + https://github.com/SpenceKonde/megaTinyCore on Atmel megaAVR ATtiny 1-series chips. + Tested with AtTiny 3217, 3216 and 1614, using + RH_Serial, RH_ASK, and RH_RF22 drivers.
+ +\version 1.99 2020-03-07 + Release under GPL V3 + +\version 1.100 2020-03-12 + Fixed a problem that prevented compilation of RH_NRF51 + on Arduino for Sparkfun nRF52832 Breakout board.
+ +\version 1.101 2020-04-10 + Tested nRF52832 with RFM69W module and RH_RF69, using Software SPI and hardware interrutps OK.
+ Fixed warnings about 'deleting object of polymorphic class' if driver is dynamically allocated.
+ Fixed problems in RH_ASK and HardwareSPI to work with STM32F4 Discovery with latest + version of stm32duino https://github.com/stm32duino/Arduino_Core_STM32. + Testing with stm32duino 1.8.0 downloaded with Board Manager per + https://github.com/stm32duino/Arduino_Core_STM32 . + Now builds and run RH_ASK examples with STM32F4 Discovery board. + Build without error for STM32 F1 and F4 but Does not compile for Generic STM32F3. + +\version 1.102 2020-05-15 + Updated RH_RF95::setPayloadCRC to affect CRC generation on outgoing packets as well + as CRC detection and checking on incoming packets. + Added new modem config for RH_RF95. RH_RF95::Bw125Cr45Sf2048 + Bw = 125 kHz, Cr = 4/5, Sf = 2048chips/symbol, CRC on. Slow+long range. Tested + against RPI with LoRa-file-ops driver https://github.com/starnight/LoRa/tree/file-ops + and send.c test program. + Fixed a problem with (re-)definition of SS on ESP32, reported and fixed by Justin Newitter. + +\version 1.103 2020-05-30 + Fixed some errors in RH_RF95::setTxPower which cased the power levels to be set incorrectly. + Checked operation and improved documentation. Valid settings are: + 2 to 20 (useRFO false) and 0 to 15 (useRFO true). 18, 19 and 20 (useRFO false) turn on the PA_DAC. + Fixed RF95 examples to reflect correct use. + Added RH_ABZ driver, which supports the muRata CMWX1ZZABZ (TypeABZ) module + which includes an STM32L0 processor, a SX1276 LoRa radio and an antenna switch. + Requires the Grumpy Old Pizza Arduino Core installed per https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0 + Examples provided. + +\version 1.104 2020-06-08 + Fixed a problem with new RH_ABZ module that prevents compilation with standard 0.0.10 version of STM32L0
+ Arduino Core installed with Board Manager: STM32L0_EXTI_CONTROL_PRIORITY_CRITICAL + is only available in later versions.
+ +\version 1.105 2020-06-03 + Added support for RH_ABZ on STM32L072xx on Grumpy Old Pizza Arduino Core
+ +\version 1.106 2020-06-16 + Patch from Livio Tenze for RH_RF22 to fix a problem with interrupts on on ESP8266.
+ Added examples/rf22/rf22_cw, with example showing how to emit a carrier wave (CW).
+ Reverted delay in RHSPIDriver::init() back to 100ms for all platforms except ABZ, where + 100ms interferes with the USB serial port with at least some versions of the core.
+ Updated and clarified documentation about TCXO use in RH_ABZ and examples.
+ Fixed documentation SS->NSEL pin for RH_RF22 with AtMega. Seems that SS on that platform + is now defined as pin 10, not 53. Dont know when that changed.
+ +\version 1.107 2020-06-26 + Improvements to RHEncryptedDriver, so that you will get an explanatory error message if you + include RHEncryptedDriver.h without enabling the class with RH_ENABLE_ENCRYPTION_MODULE in RadioHead.h.
+ Fixed newly introduced errors when compiling for ATTiny 25/45/85.
+ +\version 1.108 2020-07-05 + Fixed a problem with RH_RF22 on ESP8266 introcuced in version 1.106 which prevented + messages being received. Some other cleanups in RH_RF22
+ +\version 1.109 2020-07-23 + Fixed a problem that prevented RHEncryptedDriver compiling when RH_ENCRYPTED_DRIVER was enabled.
+ Added optiona hops argument to RHRouter and RHMesh recvfromAck() and receivedFomAckTimeout() + to allow the hop count to be retreived if desired. +\version 1.110 2020-07-28 + Fixed a problem where _spi.beginTransaction and _spi.endTransaction were missing from RHSPIDriver::spiRead. + + +\author Mike McCauley. DO NOT CONTACT THE AUTHOR DIRECTLY. USE THE GOOGLE GROUP GIVEN ABOVE +*/ + +/*! \page packingdata +\par Passing Sensor Data Between RadioHead nodes + +People often ask about how to send data (such as numbers, sensor +readings etc) from one RadioHead node to another. Although this issue +is not specific to RadioHead, and more properly lies in the area of +programming for networks, we will try to give some guidance here. + +One reason for the uncertainty and confusion in this area, especially +amongst beginners, is that there is no *best* way to do it. The best +solution for your project may depend on the range of processors and +data that you have to deal with. Also, it gets more difficult if you +need to send several numbers in one packet, and/or deal with floating +point numbers and/or different types of processors. + +The principal cause of difficulty is that different microprocessors of +the kind that run RadioHead may have different ways of representing +binary data such as integers. Some processors are little-endian and +some are big-endian in the way they represent multi-byte integers +(https://en.wikipedia.org/wiki/Endianness). And different processors +and maths libraries may represent floating point numbers in radically +different ways: +(https://en.wikipedia.org/wiki/Floating-point_arithmetic) + +All the RadioHead examples show how to send and receive simple NUL +terminated ASCII strings, and if thats all you want, refer to the +examples folder in your RadioHead distribution. But your needs may be +more complicated than that. + +The essence of all engineering is compromise so it will be up to you to +decide whats best for your particular needs. The main choices are: +- Raw Binary +- Network Order Binary +- ASCII + +\par Raw Binary + +With this technique you just pack the raw binary numbers into the packet: + +\code +// Sending a single 16 bit unsigned integer +// in the transmitter: +... +uint16_t data = getsomevalue(); +if (!driver.send((uint8_t*)&data, sizeof(data))) +{ + ... +\endcode + +\code +// and in the receiver: +... +uint16_t data; +uint8_t datalen = sizeof(data); +if ( driver.recv((uint8_t*)&data, &datalen) + && datalen == sizeof(data)) +{ + // Have the data, so do something with it + uint16_t xyz = data; + ... +\endcode + +If you need to send more than one number at a time, its best to pack +them into a structure + +\code +// Sending several 16 bit unsigned integers in a structure +// in a common header for your project: +typedef struct +{ + uint16_t dataitem1; + uint16_t dataitem2; +} MyDataStruct; +... +\endcode + +\code +// In the transmitter +... +MyDataStruct data; +data.dataitem1 = getsomevalue(); +data.dataitem2 = getsomeothervalue(); +if (!driver.send((uint8_t*)&data, sizeof(data))) +{ + ... +\endcode + +\code +// in the receiver +MyDataStruct data; +uint8_t datalen = sizeof(data); +if ( driver.recv((uint8_t*)&data, &datalen) + && datalen == sizeof(data)) +{ + // Have the data, so do something with it + uint16_t pqr = data.dataitem1; + uint16_t xyz = data.dataitem2; + .... +\endcode + + +The disadvantage with this simple technique becomes apparent if your +transmitter and receiver have different endianness: the integers you +receive will not be the same as the ones you sent (actually they are, +but with the internal bytes swapped around, so they probably wont make +sense to you). Endianness is not a problem if *every* data item you +send is a just single byte (uint8_t or int8_t or char), or if the +transmitter and receiver have the same endianness. + +So you should only adopt this technique if: +- You only send data items of a single byte each, or +- You are absolutely sure (now and forever into the future) that you +will only ever use the same processor endianness in the transmitter and receiver. + +\par Network Order Binary + +One solution to the issue of endianness in your processors is to +always convert your data from the processor's native byte order to +'network byte order' before transmission and then convert it back to +the receiver's native byte order on reception. You do this with the +htons (host to network short) macro and friends. These functions may +be a no-op on big-endian processors. + +With this technique you convert every multi-byte number to and from +network byte order (note that in most Arduino processors an integer is +in fact a short, and is the same as int16_t. We prefer to use types +that explicitly specify their size so we can be sure of applying the +right conversions): + +\code +// Sending a single 16 bit unsigned integer +// in the transmitter: +... +uint16_t data = htons(getsomevalue()); +if (!driver.send((uint8_t*)&data, sizeof(data))) +{ + ... +\endcode +\code +// and in the receiver: +... +uint16_t data; +uint8_t datalen = sizeof(data); +if ( driver.recv((uint8_t*)&data, &datalen) + && datalen == sizeof(data)) +{ + // Have the data, so do something with it + uint16_t xyz = ntohs(data); + ... +\endcode + +If you need to send more than one number at a time, its best to pack +them into a structure + +\code +// Sending several 16 bit unsigned integers in a structure +// in a common header for your project: +typedef struct +{ + uint16_t dataitem1; + uint16_t dataitem2; +} MyDataStruct; +... +\endcode +\code +// In the transmitter +... +MyDataStruct data; +data.dataitem1 = htons(getsomevalue()); +data.dataitem2 = htons(getsomeothervalue()); +if (!driver.send((uint8_t*)&data, sizeof(data))) +{ + ... +\endcode +\code +// in the receiver +MyDataStruct data; +uint8_t datalen = sizeof(data); +if ( driver.recv((uint8_t*)&data, &datalen) + && datalen == sizeof(data)) +{ + // Have the data, so do something with it + uint16_t pqr = ntohs(data.dataitem1); + uint16_t xyz = ntohs(data.dataitem2); + .... +\endcode + +This technique is quite general for integers but may not work if you +want to send floating point number between transmitters and receivers +that have different floating point number representations. + + +\par ASCII + +In this technique, you transmit the printable ASCII equivalent of +each floating point and then convert it back to a float in the receiver: + +\code +// In the transmitter +... +float data = getsomevalue(); +uint8_t buf[15]; // Bigger than the biggest possible ASCII +snprintf(buf, sizeof(buf), "%f", data); +if (!driver.send(buf, strlen(buf) + 1)) // Include the trailing NUL +{ + ... +\endcode +\code + +// In the receiver +... +float data; +uint8_t buf[15]; // Bigger than the biggest possible ASCII +uint8_t buflen = sizeof(buf); +if (driver.recv(buf, &buflen)) +{ + // Have the data, so do something with it + float data = atof(buf); // String to float + ... +\endcode + +\par Conclusion: + +- This is just a basic introduction to the issues. You may need to +extend your study into related C/C++ programming techniques. + +- You can extend these ideas to signed 16 bit (int16_t) and 32 bit +(uint32_t, int32_t) numbers. + +- Things can be simple or complicated depending on the needs of your +project. + +- We are not going to write your code for you: its up to you to take +these examples and explanations and extend them to suit your needs. + +*/ + + + +#ifndef RadioHead_h +#define RadioHead_h + +// Official version numbers are maintained automatically by Makefile: +#define RH_VERSION_MAJOR 1 +#define RH_VERSION_MINOR 110 + +// Symbolic names for currently supported platform types +#define RH_PLATFORM_ARDUINO 1 +#define RH_PLATFORM_MSP430 2 +#define RH_PLATFORM_STM32 3 +#define RH_PLATFORM_GENERIC_AVR8 4 +#define RH_PLATFORM_UNO32 5 +#define RH_PLATFORM_UNIX 6 +#define RH_PLATFORM_STM32STD 7 +#define RH_PLATFORM_STM32F4_HAL 8 +#define RH_PLATFORM_RASPI 9 +#define RH_PLATFORM_NRF51 10 +#define RH_PLATFORM_ESP8266 11 +#define RH_PLATFORM_STM32F2 12 +#define RH_PLATFORM_CHIPKIT_CORE 13 +#define RH_PLATFORM_ESP32 14 +#define RH_PLATFORM_NRF52 15 +#define RH_PLATFORM_MONGOOSE_OS 16 +#define RH_PLATFORM_ATTINY 17 +// Spencer Kondes megaTinyCore: +#define RH_PLATFORM_ATTINY_MEGA 18 +#define RH_PLATFORM_STM32L0 19 + +//////////////////////////////////////////////////// +// Select platform automatically, if possible +#ifndef RH_PLATFORM + #if (defined(MPIDE) && MPIDE>=150 && defined(ARDUINO)) + // Using ChipKIT Core on Arduino IDE + #define RH_PLATFORM RH_PLATFORM_CHIPKIT_CORE + #elif defined(MPIDE) + // Uno32 under old MPIDE, which has been discontinued: + #define RH_PLATFORM RH_PLATFORM_UNO32 + #elif defined(NRF51) || defined(NRF52) + #define RH_PLATFORM RH_PLATFORM_NRF51 + #elif defined(NRF52) + #define RH_PLATFORM RH_PLATFORM_NRF52 + #elif defined(ESP8266) + #define RH_PLATFORM RH_PLATFORM_ESP8266 + #elif defined(ESP32) + #define RH_PLATFORM RH_PLATFORM_ESP32 + #elif defined(STM32L0) || defined(ARDUINO_ARCH_STM32L0) + #define RH_PLATFORM RH_PLATFORM_STM32L0 + #elif defined(MGOS) + #define RH_PLATFORM RH_PLATFORM_MONGOOSE_OS + #elif defined(ARDUINO_attinyxy2) || defined(ARDUINO_attinyxy4) || defined(ARDUINO_attinyxy6) || defined(ARDUINO_attinyxy7) + #define RH_PLATFORM RH_PLATFORM_ATTINY_MEGA + #elif defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtinyX4__) || defined(__AVR_ATtinyX5__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) || defined(__AVR_ATtinyX313__) || defined(ARDUINO_attiny) + #define RH_PLATFORM RH_PLATFORM_ATTINY + #elif defined(ARDUINO) + #define RH_PLATFORM RH_PLATFORM_ARDUINO + #elif defined(__MSP430G2452__) || defined(__MSP430G2553__) + #define RH_PLATFORM RH_PLATFORM_MSP430 + #elif defined(MCU_STM32F103RE) + #define RH_PLATFORM RH_PLATFORM_STM32 + #elif defined(STM32F2XX) + #define RH_PLATFORM RH_PLATFORM_STM32F2 + #elif defined(USE_STDPERIPH_DRIVER) + #define RH_PLATFORM RH_PLATFORM_STM32STD + #elif defined(RASPBERRY_PI) + #define RH_PLATFORM RH_PLATFORM_RASPI + #elif defined(__unix__) // Linux + #define RH_PLATFORM RH_PLATFORM_UNIX + #elif defined(__APPLE__) // OSX + #define RH_PLATFORM RH_PLATFORM_UNIX + #else + #error Platform not defined! + #endif +#endif + +//////////////////////////////////////////////////// +// Platform specific headers: +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) + #if (ARDUINO >= 100) + #include + #else + #include + #endif + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + #if defined(ARDUINO_ARCH_STM32F4) + // output to Serial causes hangs on STM32 F4 Discovery board + // There seems to be no way to output text to the USB connection + #define Serial Serial2 + #endif +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY) + #include +// #warning Arduino TinyCore does not support hardware SPI. Use software SPI instead. +#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA) + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) // ESP8266 processor on Arduino IDE + #include + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + #define RH_MISSING_SPIUSINGINTERRUPT + +#elif (RH_PLATFORM == RH_PLATFORM_ESP32) // ESP32 processor on Arduino IDE + #include + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + #define RH_MISSING_SPIUSINGINTERRUPT + + #elif (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) // Mongoose OS platform + #include + #include + #include + #include + #include + #include + #include // We use the floor() math function. + #define RH_HAVE_HARDWARE_SPI + //If a Radio is connected via a serial port then this defines the serial + //port the radio is connected to. + #if defined(RH_SERIAL_PORT) + #if RH_SERIAL_PORT == 0 + #define Serial Serial0 + #elif RH_SERIAL_PORT == 1 + #define Serial Serial1 + #elif RH_SERIAL_PORT == 2 + #define Serial Serial2 + #endif + #else + #warning "RH_SERIAL_PORT not defined. Therefore serial port 0 selected" + #define Serial Serial0 + #endif + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_MSP430) // LaunchPad specific + #include "legacymsp430.h" + #include "Energia.h" + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_UNO32 || RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE) + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI + #define memcpy_P memcpy + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_STM32) // Maple, Flymaple etc + #include + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI + // Defines which timer to use on Maple + #define MAPLE_TIMER 1 + #define PROGMEM + #define memcpy_P memcpy + #define Serial SerialUSB + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) // https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0 + #include + #include + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Particle Photon with firmware-develop + #include + #include + #include // floor + #define RH_HAVE_SERIAL + #define RH_HAVE_HARDWARE_SPI + +#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32 with STM32F4xx_StdPeriph_Driver + #include + #include + #include + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI + #define Serial SerialUSB + #define RH_HAVE_SERIAL + +#elif (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) + #include + #include + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI + #include + +// For Steve Childress port to ARM M4 w/CMSIS with STM's Hardware Abstraction lib. +// See ArduinoWorkarounds.h (not supplied) +#elif (RH_PLATFORM == RH_PLATFORM_STM32F4_HAL) + #include + #include // Also using ST's CubeMX to generate I/O and CPU setup source code for IAR/EWARM, not GCC ARM. + #include + #include + #include + #define RH_HAVE_HARDWARE_SPI // using HAL (Hardware Abstraction Libraries from ST along with CMSIS, not arduino libs or pins concept. + +#elif (RH_PLATFORM == RH_PLATFORM_RASPI) + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + #define PROGMEM + #if (__has_include ()) + #include + #else + #include + #endif + #include + //Define SS for CS0 or pin 24 + #define SS 8 + +#elif (RH_PLATFORM == RH_PLATFORM_NRF51) + #define RH_HAVE_SERIAL + #define PROGMEM + #include + +#elif (RH_PLATFORM == RH_PLATFORM_NRF52) + #include + #define RH_HAVE_HARDWARE_SPI + #define RH_HAVE_SERIAL + #define PROGMEM + #include + +#elif (RH_PLATFORM == RH_PLATFORM_UNIX) + // Simulate the sketch on Linux and OSX + #include + #define RH_HAVE_SERIAL +#include // For htons and friends + +#else + #error Platform unknown! +#endif + +//////////////////////////////////////////////////// +// This is an attempt to make a portable atomic block +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) +#if defined(__arm__) + #include + #else + #include + #endif + #define ATOMIC_BLOCK_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + #define ATOMIC_BLOCK_END } +#elif (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE) + // UsingChipKIT Core on Arduino IDE + #define ATOMIC_BLOCK_START unsigned int __status = disableInterrupts(); { + #define ATOMIC_BLOCK_END } restoreInterrupts(__status); +#elif (RH_PLATFORM == RH_PLATFORM_UNO32) + // Under old MPIDE, which has been discontinued: + #include + #define ATOMIC_BLOCK_START unsigned int __status = INTDisableInterrupts(); { + #define ATOMIC_BLOCK_END } INTRestoreInterrupts(__status); +#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) + #define ATOMIC_BLOCK_START uint32_t primask = __get_PRIMASK(); __disable_irq(); { + #define ATOMIC_BLOCK_END } __set_PRIMASK(primask); +#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Particle Photon with firmware-develop + #define ATOMIC_BLOCK_START { int __prev = HAL_disable_irq(); + #define ATOMIC_BLOCK_END HAL_enable_irq(__prev); } +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) +// See hardware/esp8266/2.0.0/cores/esp8266/Arduino.h + #define ATOMIC_BLOCK_START { uint32_t __savedPS = xt_rsil(15); + #define ATOMIC_BLOCK_END xt_wsr_ps(__savedPS);} +#else + // TO BE DONE: + #define ATOMIC_BLOCK_START + #define ATOMIC_BLOCK_END +#endif + +//////////////////////////////////////////////////// +// Try to be compatible with systems that support yield() and multitasking +// instead of spin-loops +// Recent Arduino IDE or Teensy 3 has yield() +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO && ARDUINO >= 155) || (defined(TEENSYDUINO) && defined(__MK20DX128__)) + #define YIELD yield(); +#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) +// ESP8266 also has it + #define YIELD yield(); +#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) + #define YIELD yield(); +#elif (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) + //ESP32 and ESP8266 use freertos so we include calls + //that we would normall exit a function and return to + //the rtos in mgosYield() (E.G flush TX uart buffer + extern "C" { + void mgosYield(void); + } + #define YIELD mgosYield() +#else + #define YIELD +#endif + +//////////////////////////////////////////////////// +// digitalPinToInterrupt is not available prior to Arduino 1.5.6 and 1.0.6 +// See http://arduino.cc/en/Reference/attachInterrupt +#ifndef NOT_AN_INTERRUPT + #define NOT_AN_INTERRUPT -1 +#endif +#ifndef digitalPinToInterrupt + #if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && !defined(__arm__) + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + // Arduino Mega, Mega ADK, Mega Pro + // 2->0, 3->1, 21->2, 20->3, 19->4, 18->5 + #define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT))) + + #elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) + // Arduino 1284 and 1284P - See Manicbug and Optiboot + // 10->0, 11->1, 2->2 + #define digitalPinToInterrupt(p) ((p) == 10 ? 0 : ((p) == 11 ? 1 : ((p) == 2 ? 2 : NOT_AN_INTERRUPT))) + + #elif defined(__AVR_ATmega32U4__) + // Leonardo, Yun, Micro, Pro Micro, Flora, Esplora + // 3->0, 2->1, 0->2, 1->3, 7->4 + #define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT))))) + + #else + // All other arduino except Due: + // Serial Arduino, Extreme, NG, BT, Uno, Diecimila, Duemilanove, Nano, Menta, Pro, Mini 04, Fio, LilyPad, Ethernet etc + // 2->0, 3->1 + #define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT)) + + #endif + + #elif (RH_PLATFORM == RH_PLATFORM_UNO32) || (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE) + // Hmmm, this is correct for Uno32, but what about other boards on ChipKIT Core? + #define digitalPinToInterrupt(p) ((p) == 38 ? 0 : ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 735 ? 4 : NOT_AN_INTERRUPT))))) + + #else + // Everything else (including Due and Teensy) interrupt number the same as the interrupt pin number + #define digitalPinToInterrupt(p) (p) + #endif +#endif + +// On some platforms, attachInterrupt() takes a pin number, not an interrupt number +#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_SAM_DUE)) || defined(ARDUINO_ARCH_STM32L0) + #define RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER +#endif + +// Slave select pin, some platforms such as ATTiny do not define it. +// ESP32 pins_arduino.h uses static const uint8_t SS = ; instead +// of a #define to declare the SS constant. +#if (RH_PLATFORM != RH_PLATFORM_ESP32) + #ifndef SS + #define SS 10 + #endif +#endif + +// Some platforms require special attributes for interrupt routines +#if (RH_PLATFORM == RH_PLATFORM_ESP8266) + // interrupt handler and related code must be in RAM on ESP8266, + // according to issue #46. + #define RH_INTERRUPT_ATTR ICACHE_RAM_ATTR + +#elif (RH_PLATFORM == RH_PLATFORM_ESP32) + #define RH_INTERRUPT_ATTR IRAM_ATTR +#else + #define RH_INTERRUPT_ATTR +#endif + +// These defs cause trouble on some versions of Arduino +#undef abs +#undef round +#undef double + +// Sigh: there is no widespread adoption of htons and friends in the base code, only in some WiFi headers etc +// that have a lot of excess baggage +#if RH_PLATFORM != RH_PLATFORM_UNIX && !defined(htons) +// #ifndef htons +// These predefined macros available on modern GCC compilers + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + // Atmel processors + #define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) ) + #define ntohs(x) htons(x) + #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) + #define ntohl(x) htonl(x) + + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + // Others + #define htons(x) (x) + #define ntohs(x) (x) + #define htonl(x) (x) + #define ntohl(x) (x) + + #else + #error "RadioHead.h: Dont know how to define htons and friends for this processor" + #endif +#endif + +// This is the address that indicates a broadcast +#define RH_BROADCAST_ADDRESS 0xff + +// Specifies an invalid IO pin selection +#define RH_INVALID_PIN 0xff + +// Uncomment this is to enable Encryption (see RHEncryptedDriver): +// But ensure you have installed the Crypto directory from arduinolibs first: +// http://rweather.github.io/arduinolibs/index.html +//#define RH_ENABLE_ENCRYPTION_MODULE + +// Some platforms like RocketScream need this to see debug Serial output from within RH +// and if it goes to Serial, get a hang after a few minutes. +//#define Serial SerialUSB + +#endif diff --git a/STM32ArduinoCompat/HardwareSPI.cpp b/STM32ArduinoCompat/HardwareSPI.cpp new file mode 100644 index 0000000..14cc9b9 --- /dev/null +++ b/STM32ArduinoCompat/HardwareSPI.cpp @@ -0,0 +1,181 @@ +// ArduinoCompat/HardwareSPI.cpp +// +// Interface between Arduino-like SPI interface and STM32F4 Discovery and similar +// using STM32F4xx_DSP_StdPeriph_Lib_V1.3.0 + +#include +#if (RH_PLATFORM == RH_PLATFORM_STM32STD) + +#include +#include +#include "stm32f4xx.h" +#include "stm32f4xx_spi.h" +extern "C" +{ +#include "gdb_stdio.h" +} + +// Symbolic definitions for the SPI pins we intend to use +// Currently we only support SPI1 +#define SPIx SPI1 +#define SPIx_CLK RCC_APB2Periph_SPI1 +#define SPIx_CLK_INIT RCC_APB2PeriphClockCmd +#define SPIx_IRQn SPI2_IRQn +#define SPIx_IRQHANDLER SPI2_IRQHandler + +#define SPIx_SCK_PIN GPIO_Pin_5 +#define SPIx_SCK_GPIO_PORT GPIOA +#define SPIx_SCK_GPIO_CLK RCC_AHB1Periph_GPIOA +#define SPIx_SCK_SOURCE GPIO_PinSource5 +#define SPIx_SCK_AF GPIO_AF_SPI1 + +#define SPIx_MISO_PIN GPIO_Pin_6 +#define SPIx_MISO_GPIO_PORT GPIOA +#define SPIx_MISO_GPIO_CLK RCC_AHB1Periph_GPIOA +#define SPIx_MISO_SOURCE GPIO_PinSource6 +#define SPIx_MISO_AF GPIO_AF_SPI1 + +#define SPIx_MOSI_PIN GPIO_Pin_7 +#define SPIx_MOSI_GPIO_PORT GPIOA +#define SPIx_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOA +#define SPIx_MOSI_SOURCE GPIO_PinSource7 +#define SPIx_MOSI_AF GPIO_AF_SPI1 + +HardwareSPI::HardwareSPI(uint32_t spiPortNumber) : + _spiPortNumber(spiPortNumber) +{ +} + +void HardwareSPI::begin(SPIFrequency frequency, uint32_t bitOrder, uint32_t mode) +{ + GPIO_InitTypeDef GPIO_InitStructure; +// NVIC_InitTypeDef NVIC_InitStructure; + SPI_InitTypeDef SPI_InitStructure; + + /* Peripheral Clock Enable -------------------------------------------------*/ + /* Enable the SPI clock */ + RCC_APB2PeriphClockCmd(SPIx_CLK, ENABLE); + + /* Enable GPIO clocks */ + RCC_AHB1PeriphClockCmd(SPIx_SCK_GPIO_CLK | SPIx_MISO_GPIO_CLK | SPIx_MOSI_GPIO_CLK, ENABLE); + + /* SPI GPIO Configuration --------------------------------------------------*/ + /* GPIO Deinitialisation */ + GPIO_DeInit(SPIx_SCK_GPIO_PORT); + GPIO_DeInit(SPIx_MISO_GPIO_PORT); + GPIO_DeInit(SPIx_MOSI_GPIO_PORT); + + /* Connect SPI pins to AF5 */ + GPIO_PinAFConfig(SPIx_SCK_GPIO_PORT, SPIx_SCK_SOURCE, SPIx_SCK_AF); + GPIO_PinAFConfig(SPIx_MISO_GPIO_PORT, SPIx_MISO_SOURCE, SPIx_MISO_AF); + GPIO_PinAFConfig(SPIx_MOSI_GPIO_PORT, SPIx_MOSI_SOURCE, SPIx_MOSI_AF); + + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; + + /* SPI SCK pin configuration */ + GPIO_InitStructure.GPIO_Pin = SPIx_SCK_PIN; + GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStructure); + + /* SPI MISO pin configuration */ + GPIO_InitStructure.GPIO_Pin = SPIx_MISO_PIN; + GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStructure); + + /* SPI MOSI pin configuration */ + GPIO_InitStructure.GPIO_Pin = SPIx_MOSI_PIN; + GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStructure); + + /* SPI configuration -------------------------------------------------------*/ + SPI_I2S_DeInit(SPIx); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + if (mode == SPI_MODE0) + { + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + } + else if (mode == SPI_MODE1) + { + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; + } + else if (mode == SPI_MODE2) + { + SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + } + else if (mode == SPI_MODE3) + { + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; + } + + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + // Prescaler is divided into PCLK2 (84MHz) to get SPI baud rate/clock speed + // 256 => 328.125kHz + // 128 => 656.25kHz + // 64 => 1.3125MHz + // 32 => 2.625MHz + // 16 => 5.25MHz + // 8 => 10.5MHz + // 4 => 21.0MHz + switch (frequency) + { + case SPI_21_0MHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; + break; + case SPI_10_5MHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; + break; + case SPI_5_25MHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; + break; + case SPI_2_625MHZ: + default: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; + break; + case SPI_1_3125MHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; + break; + case SPI_656_25KHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; + break; + case SPI_328_125KHZ: + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; + break; + + } + + if (bitOrder == LSBFIRST) + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB; + else + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + + /* Initializes the SPI communication */ + SPI_Init(SPIx, &SPI_InitStructure); + /* Enable SPI1 */ + SPI_Cmd(SPIx, ENABLE); +} + +void HardwareSPI::end(void) +{ + SPI_DeInit(SPIx); +} + +uint8_t HardwareSPI::transfer(uint8_t data) +{ + // Wait for TX empty + while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET) + ; + SPI_SendData(SPIx, data); + // Wait for RX not empty + while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET) + ; + return SPI_ReceiveData(SPIx); +} + +#endif diff --git a/STM32ArduinoCompat/HardwareSPI.h b/STM32ArduinoCompat/HardwareSPI.h new file mode 100644 index 0000000..ccb0084 --- /dev/null +++ b/STM32ArduinoCompat/HardwareSPI.h @@ -0,0 +1,38 @@ +// ArduinoCompat/HardwareSPI.h +// STM32 implementattion of Arduino compatible SPI class + +#ifndef _HardwareSPI_h +#define _HardwareSPI_h + +#include + +typedef enum SPIFrequency { + SPI_21_0MHZ = 0, /**< 21 MHz */ + SPI_10_5MHZ = 1, /**< 10.5 MHz */ + SPI_5_25MHZ = 2, /**< 5.25 MHz */ + SPI_2_625MHZ = 3, /**< 2.625 MHz */ + SPI_1_3125MHZ = 4, /**< 1.3125 MHz */ + SPI_656_25KHZ = 5, /**< 656.25 KHz */ + SPI_328_125KHZ = 6, /**< 328.125 KHz */ +} SPIFrequency; + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +class HardwareSPI +{ +public: + HardwareSPI(uint32_t spiPortNumber); // Only port SPI1 is currently supported + void begin(SPIFrequency frequency, uint32_t bitOrder, uint32_t mode); + void end(void); + uint8_t transfer(uint8_t data); + +private: + uint32_t _spiPortNumber; // Not used yet. +}; +extern HardwareSPI SPI; + + +#endif diff --git a/STM32ArduinoCompat/HardwareSerial.cpp b/STM32ArduinoCompat/HardwareSerial.cpp new file mode 100644 index 0000000..d59d879 --- /dev/null +++ b/STM32ArduinoCompat/HardwareSerial.cpp @@ -0,0 +1,349 @@ +// ArduinoCompat/HardwareSerial.cpp +// +// Author: mikem@airspayce.com + +#include +#if (RH_PLATFORM == RH_PLATFORM_STM32STD) +#include +#include + +// Preinstantiated Serial objects +HardwareSerial Serial1(USART1); +HardwareSerial Serial2(USART2); +HardwareSerial Serial3(USART3); +HardwareSerial Serial4(UART4); +HardwareSerial Serial5(UART5); +HardwareSerial Serial6(USART6); + +/////////////////////////////////////////////////////////////// +// RingBuffer +/////////////////////////////////////////////////////////////// + +RingBuffer::RingBuffer() + : _head(0), + _tail(0), + _overruns(0), + _underruns(0) +{ +} + +bool RingBuffer::isEmpty() +{ + return _head == _tail; +} + +bool RingBuffer::isFull() +{ + return ((_head + 1) % ARDUINO_RINGBUFFER_SIZE) == _tail; +} + +bool RingBuffer::write(uint8_t ch) +{ + if (isFull()) + { + _overruns++; + return false; + } + _buffer[_head] = ch; + if (++_head >= ARDUINO_RINGBUFFER_SIZE) + _head = 0; + return true; +} + +uint8_t RingBuffer::read() +{ + if (isEmpty()) + { + _underruns++; + return 0; // What else can we do? + } + uint8_t ret = _buffer[_tail]; + if (++_tail >= ARDUINO_RINGBUFFER_SIZE) + _tail = 0; + return ret; +} + +/////////////////////////////////////////////////////////////// +// HardwareSerial +/////////////////////////////////////////////////////////////// + +// On STM32F4 Discovery, USART 1 is not very useful conflicts with the Green lED +HardwareSerial::HardwareSerial(USART_TypeDef* usart) + : _usart(usart) +{ +} + +void HardwareSerial::begin(unsigned long baud) +{ + USART_InitTypeDef USART_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure_TX; + GPIO_InitTypeDef GPIO_InitStructure_RX; + + // Common GPIO structure init: + GPIO_InitStructure_TX.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure_TX.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure_TX.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure_TX.GPIO_PuPd = GPIO_PuPd_UP; + + GPIO_InitStructure_RX.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure_RX.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure_RX.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure_RX.GPIO_PuPd = GPIO_PuPd_UP; + // CTS or SCLK outputs are not supported. + + USART_InitStructure.USART_BaudRate = baud * 25/8; // Why? + // Only 8N1 is currently supported + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + + // Different for each USART: + if (_usart == USART1) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_9; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_10; + GPIO_Init(GPIOA, &GPIO_InitStructure_TX); + GPIO_Init(GPIOA, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(USART1, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(USART1_IRQn); + } + else if (_usart == USART2) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_3; + GPIO_Init(GPIOA, &GPIO_InitStructure_TX); + GPIO_Init(GPIOA, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(USART2, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(USART2_IRQn); + } + else if (_usart == USART3) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_8; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_9; + GPIO_Init(GPIOD, &GPIO_InitStructure_TX); + GPIO_Init(GPIOD, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(USART3, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(USART3_IRQn); + } + else if (_usart == UART4) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_UART4); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_UART4); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_0; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_1; + GPIO_Init(GPIOA, &GPIO_InitStructure_TX); + GPIO_Init(GPIOA, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(UART4, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(UART4, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(UART4_IRQn); + } + else if (_usart == UART5) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE); + + GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_12; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_2; + GPIO_Init(GPIOC, &GPIO_InitStructure_TX); + GPIO_Init(GPIOD, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(UART5, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(UART5, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(UART5_IRQn); + } + else if (_usart == USART6) + { + // Initialise the clocks for this USART and its RX, TX pins port + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); + + GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6); + GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6); + + GPIO_InitStructure_TX.GPIO_Pin = GPIO_Pin_6; + GPIO_InitStructure_RX.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOC, &GPIO_InitStructure_TX); + GPIO_Init(GPIOC, &GPIO_InitStructure_RX); + // Initialise the USART + USART_Init(USART6, &USART_InitStructure); + // Enable the RXNE interrupt + USART_ITConfig(USART6, USART_IT_RXNE, ENABLE); + // Enable global interrupt + NVIC_EnableIRQ(USART6_IRQn); + } + + USART_Cmd(_usart, ENABLE); +} + +void HardwareSerial::end() +{ + USART_Cmd(_usart, DISABLE); + USART_DeInit(_usart); +} + +int HardwareSerial::available(void) +{ + return !_rxRingBuffer.isEmpty(); +} + +int HardwareSerial::read(void) +{ + return _rxRingBuffer.read(); +} + +size_t HardwareSerial::write(uint8_t ch) +{ + _txRingBuffer.write(ch); // Queue it + USART_ITConfig(_usart, USART_IT_TXE, ENABLE); // Enable the TX interrupt + return 1; +} + +extern "C" +{ + void USART1_IRQHandler(void) + { + if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) + { + Serial1._rxRingBuffer.write(USART_ReceiveData(USART1)); + } + if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial1._txRingBuffer.isEmpty()) + USART_ITConfig(USART1, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(USART1, Serial1._txRingBuffer.read()); + } + } + void USART2_IRQHandler(void) + { + if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) + { + // Newly received char, try to put it in our rx buffer + Serial2._rxRingBuffer.write(USART_ReceiveData(USART2)); + } + if (USART_GetITStatus(USART2, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial2._txRingBuffer.isEmpty()) + USART_ITConfig(USART2, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(USART2, Serial2._txRingBuffer.read()); + } + } + void USART3_IRQHandler(void) + { + if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) + { + // Newly received char, try to put it in our rx buffer + Serial3._rxRingBuffer.write(USART_ReceiveData(USART3)); + } + if (USART_GetITStatus(USART3, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial3._txRingBuffer.isEmpty()) + USART_ITConfig(USART3, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(USART3, Serial3._txRingBuffer.read()); + } + } + void UART4_IRQHandler(void) + { + if (USART_GetITStatus(UART4, USART_IT_RXNE) != RESET) + { + // Newly received char, try to put it in our rx buffer + Serial4._rxRingBuffer.write(USART_ReceiveData(UART4)); + } + if (USART_GetITStatus(UART4, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial4._txRingBuffer.isEmpty()) + USART_ITConfig(UART4, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(UART4, Serial4._txRingBuffer.read()); + } + } + void UART5_IRQHandler(void) + { + if (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET) + { + // Newly received char, try to put it in our rx buffer + Serial5._rxRingBuffer.write(USART_ReceiveData(UART5)); + } + if (USART_GetITStatus(UART5, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial5._txRingBuffer.isEmpty()) + USART_ITConfig(UART5, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(UART5, Serial5._txRingBuffer.read()); + } + } + void USART6_IRQHandler(void) + { + if (USART_GetITStatus(USART6, USART_IT_RXNE) != RESET) + { + // Newly received char, try to put it in our rx buffer + Serial6._rxRingBuffer.write(USART_ReceiveData(USART6)); + } + if (USART_GetITStatus(USART6, USART_IT_TXE) != RESET) + { + // Transmitter is empty, maybe send another char? + if (Serial6._txRingBuffer.isEmpty()) + USART_ITConfig(USART6, USART_IT_TXE, DISABLE); // No more to send, disable the TX interrupt + else + USART_SendData(USART6, Serial6._txRingBuffer.read()); + } + } +} + +#endif diff --git a/STM32ArduinoCompat/HardwareSerial.h b/STM32ArduinoCompat/HardwareSerial.h new file mode 100644 index 0000000..f9f2089 --- /dev/null +++ b/STM32ArduinoCompat/HardwareSerial.h @@ -0,0 +1,78 @@ +// ArduinoCompat/HardwareSerial.h +// STM32 implementation of Arduino compatible serial class + +#include +#if (RH_PLATFORM == RH_PLATFORM_STM32STD) +#ifndef _HardwareSerial_h +#define _HardwareSerial_h + +#include +#include +#include + +#ifndef ARDUINO_RINGBUFFER_SIZE +#define ARDUINO_RINGBUFFER_SIZE 64 +#endif + +class RingBuffer +{ +public: + RingBuffer(); + bool isEmpty(); + bool isFull(); + bool write(uint8_t ch); + uint8_t read(); + +private: + uint8_t _buffer[ARDUINO_RINGBUFFER_SIZE]; // In fact we can hold up to ARDUINO_RINGBUFFER_SIZE-1 bytes + uint16_t _head; // Index of next write + uint16_t _tail; // Index of next read + uint32_t _overruns; // Write attempted when buffer full + uint32_t _underruns; // Read attempted when buffer empty +}; + +// Mostly compatible wuith Arduino HardwareSerial +// Theres just enough here to support RadioHead RH_Serial +class HardwareSerial +{ +public: + HardwareSerial(USART_TypeDef* usart); + void begin(unsigned long baud); + void end(); + virtual int available(void); + virtual int read(void); + virtual size_t write(uint8_t); + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + + // These need to be public so the IRQ handler can read and write to them: + RingBuffer _rxRingBuffer; + RingBuffer _txRingBuffer; + +private: + USART_TypeDef* _usart; + +}; + +// Predefined serial ports are configured so: +// Serial STM32 UART RX pin Tx Pin Comments +// Serial1 USART1 PA10 PA9 TX Conflicts with GREEN LED on Discovery +// Serial2 USART2 PA3 PA2 +// Serial3 USART3 PD9 PD10 +// Serial4 UART4 PA1 PA0 TX conflicts with USER button on Discovery +// Serial5 UART5 PD2 PC12 TX conflicts with CS43L22 SDIN on Discovery +// Serial6 USART6 PC7 PC6 RX conflicts with CS43L22 MCLK on Discovery +// +// All ports are idle HIGH, LSB first, 8 bits, No parity, 1 stop bit +extern HardwareSerial Serial1; +extern HardwareSerial Serial2; +extern HardwareSerial Serial3; +extern HardwareSerial Serial4; +extern HardwareSerial Serial5; +extern HardwareSerial Serial6; + +#endif + +#endif diff --git a/STM32ArduinoCompat/README b/STM32ArduinoCompat/README new file mode 100644 index 0000000..aecef6f --- /dev/null +++ b/STM32ArduinoCompat/README @@ -0,0 +1,6 @@ +This directory contains some files to allow RadioHead to be built on STM32F4 +Discovery boards, using the native STM Firmware libraries, in order to support +Codec2WalkieTalkie and other projects. + +The files provide just enough Arduino compatibility to allow RadioHead to +build in that environment. diff --git a/STM32ArduinoCompat/wirish.cpp b/STM32ArduinoCompat/wirish.cpp new file mode 100644 index 0000000..fb9d432 --- /dev/null +++ b/STM32ArduinoCompat/wirish.cpp @@ -0,0 +1,413 @@ +// ArduinoCompat/wirish.cpp +// +// Arduino-like API for STM32F4 Discovery and similar +// using STM32F4xx_DSP_StdPeriph_Lib_V1.3.0 + +#include +#if (RH_PLATFORM == RH_PLATFORM_STM32STD) +#include + +SerialUSBClass SerialUSB; + +// Describes all the STM32 things we need to know about a digital IO pin to +// make it input or output or to configure as an interrupt +typedef struct +{ + uint32_t ahbperiph; + GPIO_TypeDef* port; + uint16_t pin; + uint8_t extiportsource; + uint8_t extipinsource; +} GPIOPin; + +// These describe the registers and bits for each digital IO pin to allow us to +// provide Arduino-like pin addressing, digitalRead etc. +// Indexed by pin number +GPIOPin pins[] = +{ + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_0, EXTI_PortSourceGPIOA, EXTI_PinSource0 }, // 0 = PA0 + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_1, EXTI_PortSourceGPIOA, EXTI_PinSource1 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_2, EXTI_PortSourceGPIOA, EXTI_PinSource2 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_3, EXTI_PortSourceGPIOA, EXTI_PinSource3 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_4, EXTI_PortSourceGPIOA, EXTI_PinSource4 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_5, EXTI_PortSourceGPIOA, EXTI_PinSource5 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_6, EXTI_PortSourceGPIOA, EXTI_PinSource6 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_7, EXTI_PortSourceGPIOA, EXTI_PinSource7 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_8, EXTI_PortSourceGPIOA, EXTI_PinSource8 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_9, EXTI_PortSourceGPIOA, EXTI_PinSource9 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_10, EXTI_PortSourceGPIOA, EXTI_PinSource10 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_11, EXTI_PortSourceGPIOA, EXTI_PinSource11 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_12, EXTI_PortSourceGPIOA, EXTI_PinSource12 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_13, EXTI_PortSourceGPIOA, EXTI_PinSource13 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_14, EXTI_PortSourceGPIOA, EXTI_PinSource14 }, + { RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_15, EXTI_PortSourceGPIOA, EXTI_PinSource15 }, // 15 = PA15 + + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_0, EXTI_PortSourceGPIOB, EXTI_PinSource0 }, // 16 = PB0 + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_1, EXTI_PortSourceGPIOB, EXTI_PinSource1 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_2, EXTI_PortSourceGPIOB, EXTI_PinSource2 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_3, EXTI_PortSourceGPIOB, EXTI_PinSource3 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_4, EXTI_PortSourceGPIOB, EXTI_PinSource4 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_5, EXTI_PortSourceGPIOB, EXTI_PinSource5 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_6, EXTI_PortSourceGPIOB, EXTI_PinSource6 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_7, EXTI_PortSourceGPIOB, EXTI_PinSource7 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_8, EXTI_PortSourceGPIOB, EXTI_PinSource8 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_9, EXTI_PortSourceGPIOB, EXTI_PinSource9 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_10, EXTI_PortSourceGPIOB, EXTI_PinSource10 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_11, EXTI_PortSourceGPIOB, EXTI_PinSource11 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_12, EXTI_PortSourceGPIOB, EXTI_PinSource12 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_13, EXTI_PortSourceGPIOB, EXTI_PinSource13 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_14, EXTI_PortSourceGPIOB, EXTI_PinSource14 }, + { RCC_AHB1Periph_GPIOB, GPIOB, GPIO_Pin_15, EXTI_PortSourceGPIOB, EXTI_PinSource15 }, // 31 = PB15 + + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_0, EXTI_PortSourceGPIOC, EXTI_PinSource0 }, // 32 = PC0 + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_1, EXTI_PortSourceGPIOC, EXTI_PinSource1 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_2, EXTI_PortSourceGPIOC, EXTI_PinSource2 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_3, EXTI_PortSourceGPIOC, EXTI_PinSource3 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_4, EXTI_PortSourceGPIOC, EXTI_PinSource4 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_5, EXTI_PortSourceGPIOC, EXTI_PinSource5 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_6, EXTI_PortSourceGPIOC, EXTI_PinSource6 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_7, EXTI_PortSourceGPIOC, EXTI_PinSource7 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_8, EXTI_PortSourceGPIOC, EXTI_PinSource8 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_9, EXTI_PortSourceGPIOC, EXTI_PinSource9 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_10, EXTI_PortSourceGPIOC, EXTI_PinSource10 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_11, EXTI_PortSourceGPIOC, EXTI_PinSource11 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_12, EXTI_PortSourceGPIOC, EXTI_PinSource12 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_13, EXTI_PortSourceGPIOC, EXTI_PinSource13 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_14, EXTI_PortSourceGPIOC, EXTI_PinSource14 }, + { RCC_AHB1Periph_GPIOC, GPIOC, GPIO_Pin_15, EXTI_PortSourceGPIOC, EXTI_PinSource15 }, // 47 = PC15 + + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_0, EXTI_PortSourceGPIOD, EXTI_PinSource0 }, // 48 = PD0 + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_1, EXTI_PortSourceGPIOD, EXTI_PinSource1 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_2, EXTI_PortSourceGPIOD, EXTI_PinSource2 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_3, EXTI_PortSourceGPIOD, EXTI_PinSource3 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_4, EXTI_PortSourceGPIOD, EXTI_PinSource4 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_5, EXTI_PortSourceGPIOD, EXTI_PinSource5 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_6, EXTI_PortSourceGPIOD, EXTI_PinSource6 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_7, EXTI_PortSourceGPIOD, EXTI_PinSource7 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_8, EXTI_PortSourceGPIOD, EXTI_PinSource8 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_9, EXTI_PortSourceGPIOD, EXTI_PinSource9 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_10, EXTI_PortSourceGPIOD, EXTI_PinSource10 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_11, EXTI_PortSourceGPIOD, EXTI_PinSource11 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_12, EXTI_PortSourceGPIOD, EXTI_PinSource12 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_13, EXTI_PortSourceGPIOD, EXTI_PinSource13 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_14, EXTI_PortSourceGPIOD, EXTI_PinSource14 }, + { RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_15, EXTI_PortSourceGPIOD, EXTI_PinSource15 }, // 63 = PD15 + + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_0, EXTI_PortSourceGPIOE, EXTI_PinSource0 }, // 64 = PE0 + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_1, EXTI_PortSourceGPIOE, EXTI_PinSource1 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_2, EXTI_PortSourceGPIOE, EXTI_PinSource2 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_3, EXTI_PortSourceGPIOE, EXTI_PinSource3 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_4, EXTI_PortSourceGPIOE, EXTI_PinSource4 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_5, EXTI_PortSourceGPIOE, EXTI_PinSource5 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_6, EXTI_PortSourceGPIOE, EXTI_PinSource6 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_7, EXTI_PortSourceGPIOE, EXTI_PinSource7 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_8, EXTI_PortSourceGPIOE, EXTI_PinSource8 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_9, EXTI_PortSourceGPIOE, EXTI_PinSource9 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_10, EXTI_PortSourceGPIOE, EXTI_PinSource10 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_11, EXTI_PortSourceGPIOE, EXTI_PinSource11 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_12, EXTI_PortSourceGPIOE, EXTI_PinSource12 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_13, EXTI_PortSourceGPIOE, EXTI_PinSource13 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_14, EXTI_PortSourceGPIOE, EXTI_PinSource14 }, + { RCC_AHB1Periph_GPIOE, GPIOE, GPIO_Pin_15, EXTI_PortSourceGPIOE, EXTI_PinSource15 }, // 79 = PE15 + +}; +#define NUM_PINS (sizeof(pins) / sizeof(GPIOPin)) + +typedef struct +{ + uint32_t extiline; + uint8_t extiirqn; + void (*handler)(void); +} IRQLine; + +// IRQ line data indexed by pin source number with its port +// and the programmable handler that will handle interrupts on that line +IRQLine irqlines[] = +{ + { EXTI_Line0, EXTI0_IRQn, 0 }, + { EXTI_Line1, EXTI1_IRQn, 0 }, + { EXTI_Line2, EXTI2_IRQn, 0 }, + { EXTI_Line3, EXTI3_IRQn, 0 }, + { EXTI_Line4, EXTI4_IRQn, 0 }, + { EXTI_Line5, EXTI9_5_IRQn, 0 }, + { EXTI_Line6, EXTI9_5_IRQn, 0 }, + { EXTI_Line7, EXTI9_5_IRQn, 0 }, + { EXTI_Line8, EXTI9_5_IRQn, 0 }, + { EXTI_Line9, EXTI9_5_IRQn, 0 }, + { EXTI_Line10, EXTI15_10_IRQn, 0 }, + { EXTI_Line11, EXTI15_10_IRQn, 0 }, + { EXTI_Line12, EXTI15_10_IRQn, 0 }, + { EXTI_Line13, EXTI15_10_IRQn, 0 }, + { EXTI_Line14, EXTI15_10_IRQn, 0 }, + { EXTI_Line15, EXTI15_10_IRQn, 0 }, +}; + +#define NUM_IRQ_LINES (sizeof(irqlines) / sizeof(IRQLine)) + +// Functions we expect to find in the sketch +extern void setup(); +extern void loop(); + +volatile unsigned long systick_count = 0; + +void SysTickConfig() +{ + /* Setup SysTick Timer for 1ms interrupts */ + if (SysTick_Config(SystemCoreClock / 1000)) + { + /* Capture error */ + while (1); + } + + /* Configure the SysTick handler priority */ + NVIC_SetPriority(SysTick_IRQn, 0x0); + // SysTick_Handler will now be called every 1 ms +} + +// These interrupt handlers have to be extern C else they dont get linked in to the interrupt vectors +extern "C" +{ + // Called every 1 ms + void SysTick_Handler(void) + { + systick_count++; + } + + // Interrupt handlers for optional external GPIO interrupts + void EXTI0_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line0) != RESET) + { + if (irqlines[0].handler) + irqlines[0].handler(); + EXTI_ClearITPendingBit(EXTI_Line0); + } + } + void EXTI1_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line1) != RESET) + { + if (irqlines[1].handler) + irqlines[1].handler(); + EXTI_ClearITPendingBit(EXTI_Line1); + } + } + void EXTI2_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line2) != RESET) + { + if (irqlines[2].handler) + irqlines[2].handler(); + EXTI_ClearITPendingBit(EXTI_Line2); + } + } + void EXTI3_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line3) != RESET) + { + if (irqlines[3].handler) + irqlines[3].handler(); + EXTI_ClearITPendingBit(EXTI_Line3); + } + } + void EXTI4_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line4) != RESET) + { + if (irqlines[4].handler) + irqlines[4].handler(); + EXTI_ClearITPendingBit(EXTI_Line4); + } + } + void EXTI9_5_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line5) != RESET) + { + if (irqlines[5].handler) + irqlines[5].handler(); + EXTI_ClearITPendingBit(EXTI_Line5); + } + if (EXTI_GetITStatus(EXTI_Line6) != RESET) + { + if (irqlines[6].handler) + irqlines[6].handler(); + EXTI_ClearITPendingBit(EXTI_Line6); + } + if (EXTI_GetITStatus(EXTI_Line7) != RESET) + { + if (irqlines[7].handler) + irqlines[7].handler(); + EXTI_ClearITPendingBit(EXTI_Line7); + } + if (EXTI_GetITStatus(EXTI_Line8) != RESET) + { + if (irqlines[8].handler) + irqlines[8].handler(); + EXTI_ClearITPendingBit(EXTI_Line8); + } + if (EXTI_GetITStatus(EXTI_Line9) != RESET) + { + if (irqlines[9].handler) + irqlines[9].handler(); + EXTI_ClearITPendingBit(EXTI_Line9); + } + } + void EXTI15_10_IRQHandler(void) + { + if (EXTI_GetITStatus(EXTI_Line10) != RESET) + { + if (irqlines[10].handler) + irqlines[10].handler(); + EXTI_ClearITPendingBit(EXTI_Line10); + } + if (EXTI_GetITStatus(EXTI_Line11) != RESET) + { + if (irqlines[11].handler) + irqlines[11].handler(); + EXTI_ClearITPendingBit(EXTI_Line11); + } + if (EXTI_GetITStatus(EXTI_Line12) != RESET) + { + if (irqlines[12].handler) + irqlines[12].handler(); + EXTI_ClearITPendingBit(EXTI_Line12); + } + if (EXTI_GetITStatus(EXTI_Line13) != RESET) + { + if (irqlines[13].handler) + irqlines[13].handler(); + EXTI_ClearITPendingBit(EXTI_Line13); + } + if (EXTI_GetITStatus(EXTI_Line14) != RESET) + { + if (irqlines[14].handler) + irqlines[14].handler(); + EXTI_ClearITPendingBit(EXTI_Line14); + } + if (EXTI_GetITStatus(EXTI_Line15) != RESET) + { + if (irqlines[15].handler) + irqlines[15].handler(); + EXTI_ClearITPendingBit(EXTI_Line15); + } + } +} + +// The sketch we want to run +//#include "examples/rf22/rf22_client/rf22_client.pde" + +// Run the Arduino standard functions in the main loop +int main(int argc, char** argv) +{ + SysTickConfig(); + // Seed the random number generator +// srand(getpid() ^ (unsigned) time(NULL)/2); + setup(); + while (1) + loop(); +} + +void pinMode(uint8_t pin, WiringPinMode mode) +{ + if (pin > NUM_PINS) + return; + // Enable the GPIO clock + RCC_AHB1PeriphClockCmd(pins[pin].ahbperiph, ENABLE); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = pins[pin].pin; + if (mode == INPUT) + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // REVISIT + else if (mode == OUTPUT) + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // REVISIT + else + return; // Unknown so far + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(pins[pin].port, &GPIO_InitStructure); +} + +// This takes about 150ns on STM32F4 Discovery +void digitalWrite(uint8_t pin, uint8_t val) +{ + if (pin > NUM_PINS) + return; + if (val) + GPIO_SetBits(pins[pin].port, pins[pin].pin); + else + GPIO_ResetBits(pins[pin].port, pins[pin].pin); +} + +uint8_t digitalRead(uint8_t pin) +{ + if (pin > NUM_PINS) + return 0; + return GPIO_ReadInputDataBit(pins[pin].port, pins[pin].pin); +} + +void attachInterrupt(uint8_t pin, void (*handler)(void), int mode) +{ + EXTI_InitTypeDef EXTI_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + // Record the handler to call when the interrupt occurs + irqlines[pins[pin].extipinsource].handler = handler; + + /* Connect EXTI Line to GPIO Pin */ + SYSCFG_EXTILineConfig(pins[pin].extiportsource, pins[pin].extipinsource); + + /* Configure EXTI line */ + EXTI_InitStructure.EXTI_Line = irqlines[pins[pin].extipinsource].extiline; + + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + if (mode == RISING) + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; + else if (mode == FALLING) + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; + else if (mode == CHANGE) + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; + EXTI_InitStructure.EXTI_LineCmd = ENABLE; + EXTI_Init(&EXTI_InitStructure); + + /* Enable and set EXTI Interrupt to the lowest priority */ + NVIC_InitStructure.NVIC_IRQChannel = irqlines[pins[pin].extipinsource].extiirqn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + + NVIC_Init(&NVIC_InitStructure); + + // The relevant EXTI?_IRQHandler + // will now be called when the pin makes the selected transition +} + +void delay(unsigned long ms) +{ + unsigned long start = millis(); + + while (millis() - start < ms) + ; +} + +unsigned long millis() +{ + return systick_count; +} + +long random(long from, long to) +{ + return from + (RNG_GetRandomNumber() % (to - from)); +} + +long random(long to) +{ + return random(0, to); +} + +extern "C" +{ + // These need to be in C land for correct linking + void _init() {} + void _fini() {} +} + +#endif diff --git a/STM32ArduinoCompat/wirish.h b/STM32ArduinoCompat/wirish.h new file mode 100644 index 0000000..91d7ada --- /dev/null +++ b/STM32ArduinoCompat/wirish.h @@ -0,0 +1,157 @@ +// ArduinoCompat/wirish.h + +#ifndef _wirish_h +#define _wirish_h + +#include +#include +#include +#include +#include + +#define PROGMEM +#define memcpy_P memcpy + +typedef enum WiringPinMode { + OUTPUT, /**< Basic digital output: when the pin is HIGH, the + voltage is held at +3.3v (Vcc) and when it is LOW, it + is pulled down to ground. */ + + OUTPUT_OPEN_DRAIN, /**< In open drain mode, the pin indicates + "low" by accepting current flow to ground + and "high" by providing increased + impedance. An example use would be to + connect a pin to a bus line (which is pulled + up to a positive voltage by a separate + supply through a large resistor). When the + pin is high, not much current flows through + to ground and the line stays at positive + voltage; when the pin is low, the bus + "drains" to ground with a small amount of + current constantly flowing through the large + resistor from the external supply. In this + mode, no current is ever actually sourced + from the pin. */ + + INPUT, /**< Basic digital input. The pin voltage is sampled; when + it is closer to 3.3v (Vcc) the pin status is high, and + when it is closer to 0v (ground) it is low. If no + external circuit is pulling the pin voltage to high or + low, it will tend to randomly oscillate and be very + sensitive to noise (e.g., a breath of air across the pin + might cause the state to flip). */ + + INPUT_ANALOG, /**< This is a special mode for when the pin will be + used for analog (not digital) reads. Enables ADC + conversion to be performed on the voltage at the + pin. */ + + INPUT_PULLUP, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled up" towards +3.3v. This means + the state will be high unless an external device + is specifically pulling the pin down to ground, + in which case the "gentle" pull up will not + affect the state of the input. */ + + INPUT_PULLDOWN, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled down" towards 0v. This means + the state will be low unless an external device + is specifically pulling the pin up to 3.3v, in + which case the "gentle" pull down will not + affect the state of the input. */ + + INPUT_FLOATING, /**< Synonym for INPUT. */ + + PWM, /**< This is a special mode for when the pin will be used for + PWM output (a special case of digital output). */ + + PWM_OPEN_DRAIN, /**< Like PWM, except that instead of alternating + cycles of LOW and HIGH, the voltage on the pin + consists of alternating cycles of LOW and + floating (disconnected). */ +} WiringPinMode; + +extern void pinMode(uint8_t pin, WiringPinMode mode); +extern uint32_t millis(); +extern void delay(uint32_t millis); +extern void attachInterrupt(uint8_t, void (*)(void), int mode); +extern void digitalWrite(uint8_t pin, uint8_t val); +extern uint8_t digitalRead(uint8_t pin); + +//extern long random(long to); +//extern long random(long from, long to); + +#define HIGH 0x1 +#define LOW 0x0 + +#define LSBFIRST 0 +#define MSBFIRST 1 + +#define CHANGE 1 +#define FALLING 2 +#define RISING 3 + +// Equivalent to HardwareSerial in Arduino +class SerialUSBClass +{ +public: +#define DEC 10 +#define HEX 16 +#define OCT 8 +#define BIN 2 + + // TODO: move these from being inlined + void begin(int baud) {} + + size_t println(const char* s) + { + print(s); + printf("\n"); + return 0; + } + size_t print(const char* s) + { + printf(s); + return 0; + } + size_t print(unsigned int n, int base = DEC) + { + if (base == DEC) + printf("%d", n); + else if (base == HEX) + printf("%02x", n); + else if (base == OCT) + printf("%o", n); + // TODO: BIN + return 0; + } + size_t print(char ch) + { + printf("%c", ch); + return 0; + } + size_t println(char ch) + { + printf("%c\n", ch); + return 0; + } + size_t print(unsigned char ch, int base = DEC) + { + return print((unsigned int)ch, base); + } + size_t println(unsigned char ch, int base = DEC) + { + print((unsigned int)ch, base); + printf("\n"); + return 0; + } + +}; + +// Global instance of the Serial output +extern SerialUSBClass SerialUSB; + +#endif + diff --git a/examples/abz/abz_client/abz_client.pde b/examples/abz/abz_client/abz_client.pde new file mode 100644 index 0000000..2fda91f --- /dev/null +++ b/examples/abz/abz_client/abz_client.pde @@ -0,0 +1,102 @@ +// abz_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_ABZ class. RH_ABZ class does not provide for addressing or +// reliability, so you should only use RH_ABZ directly if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example abz_server +// Tested with Tested with EcoNode SmartTrap, Arduino 1.8.9, GrumpyOldPizza Arduino Core for STM32L0. + +#include +#include + +// Singleton instance of the radio driver +RH_ABZ abz; + +// Valid for SmartTrap, maybe not other boards + +#define GREEN_LED 13 +#define YELLOW_LED 12 +#define RED_LED 11 + + +void setup() +{ + pinMode(GREEN_LED, OUTPUT); + pinMode(YELLOW_LED, OUTPUT); + pinMode(RED_LED, OUTPUT); + + Serial.begin(9600); + // Wait for serial port to be available + // If you do this, it will block here until a USB serial connection is made. + // If not, it will continue without a Serial connection, but DFU mode will not be available + // to the host without resetting the CPU with the Boot button +// while (!Serial) ; + + // You must be sure that the TCXO settings are appropriate for your board and radio. + // See the RH_ABZ documentation for more information. + // This call is adequate for Tlera boards supported by the Grumpy Old Pizza Arduino Core + // It may or may not be innocuous for others + SX1276SetBoardTcxo(true); + delay(1); + + if (!abz.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + abz.setFrequency(868.0); + + // You can change the modulation speed etc from the default + //abz.setModemConfig(RH_RF95::Bw125Cr45Sf128); + //abz.setModemConfig(RH_RF95::Bw125Cr45Sf2048); + + // The default transmitter power is 13dBm, using PA_BOOST. + // You can set transmitter powers from 2 to 20 dBm: + //abz.setTxPower(20); // Max power +} + +void loop() +{ + digitalWrite(YELLOW_LED, 1); + digitalWrite(GREEN_LED, 0); + digitalWrite(RED_LED, 0); + + Serial.println("Sending to abz_server"); + // Send a message to abz_server + uint8_t data[] = "Hello World!"; + abz.send(data, sizeof(data)); + abz.waitPacketSent(); + + // Now wait for a reply + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + +// You might need a longer timeout for slow modulatiuon schemes and/or long messages + if (abz.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (abz.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(abz.lastRssi(), DEC); + digitalWrite(GREEN_LED, 1); + + } + else + { + Serial.println("recv failed"); + digitalWrite(RED_LED, 1); + + } + } + else + { + Serial.println("No reply, is abz_server running?"); + digitalWrite(RED_LED, 1); + } + digitalWrite(YELLOW_LED, 0); + + delay(400); +} diff --git a/examples/abz/abz_server/abz_server.pde b/examples/abz/abz_server/abz_server.pde new file mode 100644 index 0000000..5b5ba38 --- /dev/null +++ b/examples/abz/abz_server/abz_server.pde @@ -0,0 +1,90 @@ +// abz_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_ABZ class. RH_ABZ class does not provide for addressing or +// reliability, so you should only use RH_ABZ directly if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example abz_server +// Tested with Tested with EcoNode SmartTrap, Arduino 1.8.9, GrumpyOldPizza Arduino Core for STM32L0. + +#include +#include + +// Singleton instance of the radio driver +RH_ABZ abz; + +// Valid for SmartTrap, maybe not other boards +#define GREEN_LED 13 +#define YELLOW_LED 12 +#define RED_LED 11 + +void setup() +{ + pinMode(GREEN_LED, OUTPUT); + pinMode(YELLOW_LED, OUTPUT); + pinMode(RED_LED, OUTPUT); + + Serial.begin(9600); + // Wait for serial port to be available + // If you do this, it will block here until a USB serial connection is made. + // If not, it will continue without a Serial connection, but DFU mode will not be available + // to the host without resetting the CPU with the Boot button +// while (!Serial) ; + + // You must be sure that the TCXO settings are appropriate for your board and radio. + // See the RH_ABZ documentation for more information. + // This call is adequate for Tlera boards supported by the Grumpy Old Pizza Arduino Core + // It may or may not be innocuous for others + SX1276SetBoardTcxo(true); + delay(1); + + if (!abz.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + abz.setFrequency(868.0); + + // You can change the modulation speed etc from the default + //abz.setModemConfig(RH_RF95::Bw125Cr45Sf128); + //abz.setModemConfig(RH_RF95::Bw125Cr45Sf2048); + + // The default transmitter power is 13dBm, using PA_BOOST. + // You can set transmitter powers from 2 to 20 dBm: + //abz.setTxPower(20); // Max power + +} + +void loop() +{ + digitalWrite(YELLOW_LED, 1); + digitalWrite(GREEN_LED, 0); + digitalWrite(RED_LED, 0); + + if (abz.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (abz.recv(buf, &len)) + { + digitalWrite(GREEN_LED, 1); + +// RH_ABZ::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(abz.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + abz.send(data, sizeof(data)); + abz.waitPacketSent(); + Serial.println("Sent a reply"); + digitalWrite(GREEN_LED, 0); + } + else + { + Serial.println("recv failed"); + } + } +} diff --git a/examples/ask/ask_receiver/ask_receiver.pde b/examples/ask/ask_receiver/ask_receiver.pde new file mode 100644 index 0000000..68ccab8 --- /dev/null +++ b/examples/ask/ask_receiver/ask_receiver.pde @@ -0,0 +1,43 @@ +// ask_receiver.pde +// -*- mode: C++ -*- +// Simple example of how to use RadioHead to receive messages +// with a simple ASK transmitter in a very simple way. +// Implements a simplex (one-way) receiver with an Rx-B1 module +// Tested on Arduino Mega, Duemilanova, Uno, Due, Teensy, ESP-12 + +#include +#ifdef RH_HAVE_HARDWARE_SPI +#include // Not actually used but needed to compile +#endif + +RH_ASK driver; +// RH_ASK driver(2000, 4, 5, 0); // ESP8266 or ESP32: do not use pin 11 or 2 +// RH_ASK driver(2000, 3, 4, 0); // ATTiny, RX on D3 (pin 2 on attiny85) TX on D4 (pin 3 on attiny85), +// RH_ASK driver(2000, PD14, PD13, 0); STM32F4 Discovery: see tx and rx on Orange and Red LEDS + +void setup() +{ +#ifdef RH_HAVE_SERIAL + Serial.begin(9600); // Debugging only +#endif + if (!driver.init()) +#ifdef RH_HAVE_SERIAL + Serial.println("init failed"); +#else + ; +#endif +} + +void loop() +{ + uint8_t buf[RH_ASK_MAX_MESSAGE_LEN]; + uint8_t buflen = sizeof(buf); + + if (driver.recv(buf, &buflen)) // Non-blocking + { + int i; + + // Message with a good checksum received, dump it. + driver.printBuffer("Got:", buf, buflen); + } +} diff --git a/examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde b/examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde new file mode 100644 index 0000000..9683e90 --- /dev/null +++ b/examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde @@ -0,0 +1,60 @@ +// ask_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_ASK driver to control a ASK radio. +// It is designed to work with the other example ask_reliable_datagram_server +// Tested on Arduino Mega, Duemilanova, Uno, Due, Teensy, ESP-12 + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_ASK driver; +// RH_ASK driver(2000, 4, 5, 0); // ESP8266 or ESP32: do not use pin 11 or 2 +// RH_ASK driver(2000, PD14, PD13, 0); STM32F4 Discovery: see tx and rx on Orange and Red LEDS + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_ASK_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to ask_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is ask_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde b/examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde new file mode 100644 index 0000000..c347a9d --- /dev/null +++ b/examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde @@ -0,0 +1,54 @@ +// ask_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_ASK driver to control a ASK radio. +// It is designed to work with the other example ask_reliable_datagram_client +// Tested on Arduino Mega, Duemilanova, Uno, Due, Teensy, ESP-12 + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_ASK driver; +// RH_ASK driver(2000, 4, 5, 0); // ESP8266 or ESP32: do not use pin 11 or 2 +// RH_ASK driver(2000, PD14, PD13, 0); STM32F4 Discovery: see tx and rx on Orange and Red LEDS + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_ASK_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/ask/ask_transmitter/ask_transmitter.pde b/examples/ask/ask_transmitter/ask_transmitter.pde new file mode 100644 index 0000000..2959d40 --- /dev/null +++ b/examples/ask/ask_transmitter/ask_transmitter.pde @@ -0,0 +1,38 @@ +// ask_transmitter.pde +// -*- mode: C++ -*- +// Simple example of how to use RadioHead to transmit messages +// with a simple ASK transmitter in a very simple way. +// Implements a simplex (one-way) transmitter with an TX-C1 module +// Tested on Arduino Mega, Duemilanova, Uno, Due, Teensy, ESP-12 + +#include +#ifdef RH_HAVE_HARDWARE_SPI +#include // Not actually used but needed to compile +#endif + +RH_ASK driver; +// RH_ASK driver(2000, 4, 5, 0); // ESP8266 or ESP32: do not use pin 11 or 2 +// RH_ASK driver(2000, 3, 4, 0); // ATTiny, RX on D3 (pin 2 on attiny85) TX on D4 (pin 3 on attiny85), +// RH_ASK driver(2000, PD14, PD13, 0); STM32F4 Discovery: see tx and rx on Orange and Red LEDS + +void setup() +{ +#ifdef RH_HAVE_SERIAL + Serial.begin(9600); // Debugging only +#endif + if (!driver.init()) +#ifdef RH_HAVE_SERIAL + Serial.println("init failed"); +#else + ; +#endif +} + +void loop() +{ + const char *msg = "hello"; + + driver.send((uint8_t *)msg, strlen(msg)); + driver.waitPacketSent(); + delay(200); +} diff --git a/examples/cc110/cc110_client/cc110_client.pde b/examples/cc110/cc110_client/cc110_client.pde new file mode 100644 index 0000000..ef73716 --- /dev/null +++ b/examples/cc110/cc110_client/cc110_client.pde @@ -0,0 +1,75 @@ +// cc110_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_CC110 class. RH_CC110 class does not provide for addressing or +// reliability, so you should only use RH_CC110 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example cc110_server +// Tested with Teensy 3.1 and Anaren 430BOOST-CC110L + +#include +#include + +// Singleton instance of the radio driver +RH_CC110 cc110; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for native USB + + // CC110L may be equipped with either 26 or 27MHz crystals. You MUST + // tell the driver if a 27MHz crystal is installed for the correct configuration to + // occur. Failure to correctly set this flag will cause incorrect frequency and modulation + // characteristics to be used. You can call this function, or pass it to the constructor + cc110.setIs27MHz(true); // Anaren 430BOOST-CC110L Air BoosterPack test boards have 27MHz + if (!cc110.init()) + Serial.println("init failed"); + // After init(), the following default values apply: + // TxPower: TransmitPower5dBm + // Frequency: 915.0 + // Modulation: GFSK_Rb1_2Fd5_2 (GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity) + // Sync Words: 0xd3, 0x91 + // But you can change them: +// cc110.setTxPower(RH_CC110::TransmitPowerM30dBm); +// cc110.setModemConfig(RH_CC110::GFSK_Rb250Fd127); +//cc110.setFrequency(928.0); +} + +void loop() +{ + Serial.println("Sending to cc110_server"); + // Send a message to cc110_server + uint8_t data[] = "Hello World!"; + cc110.send(data, sizeof(data)); + + cc110.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_CC110_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (cc110.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (cc110.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(cc110.lastRssi(), DEC); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is cc110_server running?"); + } + + delay(400); +} + + diff --git a/examples/cc110/cc110_server/cc110_server.pde b/examples/cc110/cc110_server/cc110_server.pde new file mode 100644 index 0000000..5055685 --- /dev/null +++ b/examples/cc110/cc110_server/cc110_server.pde @@ -0,0 +1,69 @@ +// cc110_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_CC110 class. RH_CC110 class does not provide for addressing or +// reliability, so you should only use RH_CC110 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example cc110_client +// Tested with Teensy 3.1 and Anaren 430BOOST-CC110L + + +#include +#include + +// Singleton instance of the radio driver +RH_CC110 cc110; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for native USB + + // CC110L may be equipped with either 26 or 27MHz crystals. You MUST + // tell the driver if a 27MHz crystal is installed for the correct configuration to + // occur. Failure to correctly set this flag will cause incorrect frequency and modulation + // characteristics to be used. You can call this function, or pass it to the constructor + cc110.setIs27MHz(true); // Anaren 430BOOST-CC110L Air BoosterPack test boards have 27MHz + if (!cc110.init()) + Serial.println("init failed"); + // After init(), the following default values apply: + // TxPower: TransmitPower5dBm + // Frequency: 915.0 + // Modulation: GFSK_Rb1_2Fd5_2 (GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity) + // Sync Words: 0xd3, 0x91 + // But you can change them: +// cc110.setTxPower(RH_CC110::TransmitPowerM30dBm); +// cc110.setModemConfig(RH_CC110::GFSK_Rb250Fd127); +//cc110.setFrequency(928.0); +} + +void loop() +{ + if (cc110.available()) + { + // Should be a message for us now + uint8_t buf[RH_CC110_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (cc110.recv(buf, &len)) + { +// RH_CC110::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(cc110.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + cc110.send(data, sizeof(data)); + cc110.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + + diff --git a/examples/cc110/jsrpc/config.h b/examples/cc110/jsrpc/config.h new file mode 100644 index 0000000..95c6bd4 --- /dev/null +++ b/examples/cc110/jsrpc/config.h @@ -0,0 +1,27 @@ +# define VERSION "20180205" + +// radio bidirectional comunication +#define TWOWAY "Yes" +#define CLIENT "Yes" +#define SERVER "Yes" + +// freq added to standard channel +//#define FREQCORR 0.050 +#define FREQCORR 0.0 + +// define the pins used +#define PINS 4,5,A6,A7 + +#define SERIALBUFFERSIZE 160 +#define SERIALBAUDRATE 115200 +#define DEBUGONSERIAL + + +#ifdef DEBUGONSERIAL + +#define DBGSERIAL Serial + +#define IF_SDEBUG(x) ({x;}) +#else +#define IF_SDEBUG(x) +#endif diff --git a/examples/cc110/jsrpc/jsrpc.ino b/examples/cc110/jsrpc/jsrpc.ino new file mode 100644 index 0000000..63b0fa9 --- /dev/null +++ b/examples/cc110/jsrpc/jsrpc.ino @@ -0,0 +1,569 @@ +// create a simple messageing +// with the RH_CC110 class. RH_CC110 class does not provide for addressing or +// reliability, so you should only use RH_CC110 if you do not need the higher +// level messaging abilities. + +#include + +#define CONFVER "conf00" + + +#include "config.h" +#include +#include "EEPROMAnything.h" + +#include +#include +#include + +// Singleton instance of the radio driver +RH_CC110 cc110; + +// GFSK_Rb1_2Fd5_2 = 0, ///< GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity +// GFSK_Rb2_4Fd5_2, ///< GFSK, Data Rate: 2.4kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity +// GFSK_Rb4_8Fd25_4, ///< GFSK, Data Rate: 4.8kBaud, Dev: 25.4kHz, RX BW 100kHz, optimised for sensitivity +// GFSK_Rb10Fd19, ///< GFSK, Data Rate: 10kBaud, Dev: 19kHz, RX BW 100kHz, optimised for sensitivity +// GFSK_Rb38_4Fd20, ///< GFSK, Data Rate: 38.4kBaud, Dev: 20kHz, RX BW 100kHz, optimised for sensitivity +// GFSK_Rb76_8Fd32, ///< GFSK, Data Rate: 76.8kBaud, Dev: 32kHz, RX BW 232kHz, optimised for sensitivity +// GFSK_Rb100Fd47, ///< GFSK, Data Rate: 100kBaud, Dev: 47kHz, RX BW 325kHz, optimised for sensitivity +// GFSK_Rb250Fd127, ///< GFSK, Data Rate: 250kBaud, Dev: 127kHz, RX BW 540kHz, optimised for sensitivity + + +// include the aJSON library +#include + +// include the JsonRPC library +#include + +// initialize an instance of the JsonRPC library for registering +// exactly 3 method + + +#ifdef CLIENT +JsonRPC rpcclient(5,false); //serial port +#endif +#ifdef SERVER +JsonRPC rpcserver(3,true ); //radio port with compact protocoll +#endif + +#ifdef CLIENT +// initialize a serial json stream for receiving json objects +// through a serial/USB connection +aJsonStream stream(&Serial); +aJsonObject *serialmsg = NULL; +#endif + +#ifdef SERVER +aJsonObject *radiomsg = NULL; +#endif + +char confver[7] = CONFVER; // version of configuration saved on eeprom + +struct config_t // configuration to save and load fron eeprom +{ + int did; // sample time for mqtt (seconds) + void save () { + int p=0; // save to eeprom + p+=EEPROM_writeAnything(p, confver); + p+=EEPROM_writeAnything(p, did); + } +bool load () { // load from eeprom + int p=0; + char ver[7]; + p+=EEPROM_readAnything(p, ver); + if (strcmp(ver,confver ) == 0){ + p+=EEPROM_readAnything(p, did); + return true; + } + else{ + return false; + } + } +} configuration; + +//------------- + +const uint8_t pins [] = {PINS}; + +//------------- + +void Reboot() { + IF_SDEBUG(DBGSERIAL.println(F("#Reboot"))); + wdt_enable(WDTO_30MS); while(1) {} +} + +#ifdef CLIENT +int client(aJsonObject* params) +{ + + uint8_t status=0; + aJsonObject *newrpc=NULL ; + newrpc = aJson.createObject(); + //aJson.addStringToObject(newrpc, "m", method); + +#ifdef TWOWAY + aJsonObject* id = aJson.getObjectItem(serialmsg, "id"); + if(id){ + aJson.addNumberToObject(newrpc, "i",id -> valueint ); + } +#endif + + aJsonObject* mymethod = aJson.detachItemFromObject(serialmsg, "method"); + aJson.addItemToObject(newrpc, "m",mymethod ); + + aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); + aJson.addItemToObject(newrpc, "p",myparams ); + + char buf[RH_CC110_MAX_MESSAGE_LEN]; + aJson.print(newrpc,buf, sizeof(buf)); + aJson.deleteItem(newrpc); + + IF_SDEBUG(DBGSERIAL.print(F("#send: "))); + IF_SDEBUG(DBGSERIAL.println(buf)); + + cc110.send((uint8_t*)buf, strlen(buf)); + cc110.waitPacketSent(); + +#ifdef TWOWAY + + // Now wait for a reply + uint8_t len = sizeof(buf); + + if (cc110.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (cc110.recv((uint8_t*)buf, &len)) + { + IF_SDEBUG(DBGSERIAL.print(F("#got reply: "))); + IF_SDEBUG(DBGSERIAL.println((char*)buf)); + IF_SDEBUG(DBGSERIAL.print(F("#RSSI: "))); + IF_SDEBUG(DBGSERIAL.println(cc110.lastRssi(), DEC)); + +#endif + + //IF_SDEBUG(DBGSERIAL.println("{\"jsonrpc\": \"2.0\", \"result\":true, \"id\": 0}")); + aJson.addTrueToObject(serialmsg, "result"); + +#ifdef TWOWAY + + } else { + IF_SDEBUG(DBGSERIAL.println(F("#recv failed"))); + aJson.addFalseToObject(serialmsg, "result"); + status = 1; + } + } else { + IF_SDEBUG(DBGSERIAL.println(F("#No reply, is cc110_server running?"))); + aJson.addFalseToObject(serialmsg, "result"); + status = 1; + } + +#endif + char serialbuf[SERIALBUFFERSIZE]; + + aJson.print(serialmsg,serialbuf, sizeof(serialbuf)); + Serial.println(serialbuf); + + return status; + +} +#endif + +#ifdef CLIENT +int setdid(aJsonObject* params) +{ + uint8_t status=1; + aJson.deleteItemFromObject(serialmsg, "method"); + + aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); + aJsonObject* didParam = aJson.getObjectItem(myparams, "d"); + if (didParam){ + int did = didParam -> valueint; + configuration.did=did; + + aJson.addTrueToObject(serialmsg, "result"); + char buf[SERIALBUFFERSIZE]; + aJson.print(serialmsg,buf, sizeof(buf)); + Serial.println(buf); + + status= 0; + } + aJson.deleteItem(params); + return status; +} + +int save(aJsonObject* params) +{ + uint8_t status=1; + aJson.deleteItemFromObject(serialmsg, "method"); + + aJsonObject* myparams = aJson.detachItemFromObject(serialmsg, "params"); + + aJsonObject* saveParam = aJson.getObjectItem(myparams, "eeprom"); + if (saveParam){ + bool eeprom = saveParam -> valuebool; + + if (eeprom) configuration.save(); + + aJson.addTrueToObject(serialmsg, "result"); + char buf[SERIALBUFFERSIZE]; + aJson.print(serialmsg,buf, sizeof(buf)); + Serial.println(buf); + + status = 0; + } + + aJson.deleteItem(params); + return status; + +} +#endif +#ifdef SERVER +int changedidserver(aJsonObject* params) +{ + aJsonObject* olddidParam = aJson.getObjectItem(params, "olddid"); + if (olddidParam){ + int olddid = olddidParam -> valueint; + if (olddid == configuration.did || olddid == 0 ){ + + aJsonObject* didParam = aJson.getObjectItem(params, "d"); + if (didParam){ + int did = didParam -> valueint; + configuration.did=did; + + aJson.deleteItemFromObject(radiomsg, "m"); + aJson.deleteItemFromObject(radiomsg, "p"); + aJson.addTrueToObject(radiomsg, "r"); + + } + } + } + //aJson.deleteItem(params); + return 0; +} + +int saveserver(aJsonObject* params) +{ + + aJsonObject* didParam = aJson.getObjectItem(params, "d"); + if (didParam){ + int did = didParam -> valueint; + if (did == configuration.did || did == 0 ){ //my did or broadcast + + aJsonObject* saveParam = aJson.getObjectItem(params, "eeprom"); + if (saveParam){ + boolean eeprom = saveParam -> valuebool; + + if (eeprom) configuration.save(); + aJson.deleteItemFromObject(radiomsg, "m"); + aJson.deleteItemFromObject(radiomsg, "p"); + aJson.addTrueToObject(radiomsg, "r"); + + } + } + } + //aJson.deleteItem(params); + return 0; +} + +int singleserver(aJsonObject* params) +{ + //{"jsonrpc":"2.0","method":"single","params":{"d":1,"u":1,"o":true},"id":0} + + aJsonObject* didParam = aJson.getObjectItem(params, "d"); + if (didParam){ + int did = didParam -> valueint; + if (did == configuration.did || did == 0 ){ //my did or broadcast + + aJsonObject* dstunitParam = aJson.getObjectItem(params, "u"); + if (dstunitParam){ + int dstunit = dstunitParam -> valueint; + + if (dstunit >= 0 && dstunit < sizeof(pins)/sizeof(*pins)){ + aJsonObject* onoffParam = aJson.getObjectItem(params, "o"); + if (onoffParam){ + boolean onoff = onoffParam -> valuebool; + IF_SDEBUG(DBGSERIAL.print(F("#did: "))); + IF_SDEBUG(DBGSERIAL.print(did)); + IF_SDEBUG(DBGSERIAL.print(F(" dstunit: "))); + IF_SDEBUG(DBGSERIAL.print(dstunit)); + IF_SDEBUG(DBGSERIAL.print(F(" onoff: "))); + IF_SDEBUG(DBGSERIAL.println(onoff)); + + digitalWrite(pins[dstunit], ! onoff); + + aJson.deleteItemFromObject(radiomsg, "m"); + aJson.deleteItemFromObject(radiomsg, "p"); + aJson.addTrueToObject(radiomsg, "r"); + + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#no onoff"))); + } + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#wrong dstunit"))); + } + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#no dstunit"))); + } + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#not for me"))); + } + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#no did"))); + } + //IF_SDEBUG(DBGSERIAL.println(F("{\"result\": \"OK\"}")); + //aJson.deleteItem(params); + return 0; +} +#endif +void setup() +{ + + /* + Nel caso di un chip in standalone senza bootloader, la prima + istruzione che è bene mettere nel setup() è sempre la disattivazione + del Watchdog stesso: il Watchdog, infatti, resta attivo dopo il + reset e, se non disabilitato, esso può provare il reset perpetuo del + microcontrollore + */ + wdt_disable(); + + wdt_enable(WDTO_8S); + + IF_SDEBUG(DBGSERIAL.begin(SERIALBAUDRATE)); + Serial.begin(SERIALBAUDRATE); + while (!Serial); // wait for serial port to connect. Needed for native USB + Serial.println(F("#Started: "VERSION)); +#ifdef TWOWAY + Serial.println(F("#Twovay: "TWOWAY)); +#endif +#ifdef CLIENT + Serial.println(F("#Client: "CLIENT)); +#endif +#ifdef SERVER + Serial.println(F("#Server: "SERVER)); +#endif + + if (configuration.load()){ + IF_SDEBUG(DBGSERIAL.println(F("#Configuration loaded"))); + IF_SDEBUG(DBGSERIAL.print(F("#did:"))); + IF_SDEBUG(DBGSERIAL.println(configuration.did)); + } else { + IF_SDEBUG(DBGSERIAL.println(F("#Configuration not loaded"))); + } + + // register the local single method +#ifdef SERVER + // Radio port + rpcserver.registerMethod("single", &singleserver); + rpcserver.registerMethod("changedid", &changedidserver); + rpcserver.registerMethod("remotesave",&saveserver); +#endif + +#ifdef CLIENT + // Serial port + rpcclient.registerMethod("single", &client); + rpcclient.registerMethod("changedid", &client); + rpcclient.registerMethod("remotesave",&client); + rpcclient.registerMethod("setdid", &setdid); + rpcclient.registerMethod("save", &save); +#endif + + // CC110L may be equipped with either 26 or 27MHz crystals. You MUST + // tell the driver if a 27MHz crystal is installed for the correct configuration to + // occur. Failure to correctly set this flag will cause incorrect frequency and modulation + // characteristics to be used. You can call this function, or pass it to the constructor + //cc110.setIs27MHz(true); // Anaren 430BOOST-CC110L Air BoosterPack test boards have 27MHz + + if (!cc110.init()){ + IF_SDEBUG(DBGSERIAL.println(F("init failed"))); + Reboot(); + } + // After init(), the following default values apply: + // TxPower: TransmitPower5dBm + // Frequency: 915.0 + // Modulation: GFSK_Rb1_2Fd5_2 (GFSK, Data Rate: 1.2kBaud, Dev: 5.2kHz, RX BW 58kHz, optimised for sensitivity) + // Sync Words: 0xd3, 0x91 + // But you can change them: + // cc110.setTxPower(RH_CC110::TransmitPowerM30dBm); + // cc110.setModemConfig(RH_CC110::GFSK_Rb250Fd127); + //cc110.setFrequency(928.0); + +/* +Canale Frequenza (MHz) Canale Frequenza (MHz) Canale Frequenza (MHz) +1 433.075 24 433.650 47 434.225 +2 433.100 25 433.675 48 434.250 +3 433.125 26 433.700 49 434.275 +4 433.150 27 433.725 50 434.300 +5 433.175 28 433.750 51 434.325 +6 433.200 29 433.775 52 434.350 +7 433.225 30 433.800 53 434.375 +8 433.250 31 433.825 54 434.400 +9 433.275 32 433.850 55 434.425 +10 433.300 33 433.875 56 434.450 +11 433.325 34 433.900 57 434.475 +12 433.350 35 433.925 58 434.500 +13 433.375 36 433.950 59 434.525 +14 433.400 37 433.975 60 434.550 +15 433.425 38 434.000 61 434.575 +16 433.450 39 434.025 62 434.600 +17 433.475 40 434.050 63 434.625 +18 433.500 41 434.075 64 434.650 +19 433.525 42 434.100 65 434.675 +20 433.550 43 434.125 66 434.700 +21 433.575 44 434.150 67 434.725 +22 433.600 45 434.175 68 434.750 +23 433.625 46 434.200 69 434.775 +*/ + + cc110.setTxPower(RH_CC110::TransmitPower0dBm); + //cc110.setModemConfig(RH_CC110::GFSK_Rb4_8Fd25_4); // Giacomo + cc110.setModemConfig(RH_CC110::GFSK_Rb100Fd47); // Pat1 + + // For 26MHz crystals + //PROGMEM static const RH_CC110::ModemConfig GFSK_R1_2Fd25_4 = + static const RH_CC110::ModemConfig GFSK_R1_2Fd25_4 = + // 0B 0C 10 11 12 15 19 1A 1B 1C 1D 21 22 23 24 25 26 2C 2D 2E + {0x06, 0x00, 0xC5, 0x83, 0x13, 0x40, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}; // GFSK_R1_2Fd47 GFSK, Data Rate: 1.2kBaud, Dev: 47kHz, RX BW 325kHz, optimised for sensitivity + //{0x06, 0x00, 0xc7, 0x83, 0x13, 0x40, 0x16, 0x6c, 0x43, 0x40, 0x91, 0x56, 0x10, 0xe9, 0x2a, 0x00, 0x1f, 0x81, 0x35, 0x09}; // GFSK_Rb4_8Fd25_4 + + //cc110.setModemRegisters(&GFSK_R1_2Fd25_4); + + cc110.setFrequency(434.0+FREQCORR); + + + for (int dstunit=0 ;dstunit < sizeof(pins)/sizeof(*pins); dstunit++) + { + pinMode(pins[dstunit], OUTPUT); + digitalWrite(pins[dstunit], 1); + } + +#ifdef CLIENT + if (stream.available()) { + // skip any accidental whitespace like newlines + stream.skip(); + } +#endif +} + +#ifdef CLIENT +void mgr_serial(){ + unsigned int err; + + if (stream.available()) { + + serialmsg = aJson.parse(&stream); + if (serialmsg){ + IF_SDEBUG(DBGSERIAL.print(F("#rpc.processMessage:"))); + char serialbuf[SERIALBUFFERSIZE]; + aJson.print(serialmsg, serialbuf, sizeof(serialbuf)); + IF_SDEBUG(DBGSERIAL.println(serialbuf)); + + err=rpcclient.processMessage(serialmsg); + IF_SDEBUG(DBGSERIAL.print(F("#rpcclient.processMessage return status:"))); + IF_SDEBUG(DBGSERIAL.println(err)); + if (!err){ + aJson.deleteItem(serialmsg); + }else{ + err = 1; + } + + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#skip wrong message"))); + err = 2; + if (stream.available()) { + stream.flush(); + } + } + + if (err == 1){ + aJsonObject *result = aJson.createObject(); + aJson.addItemToObject(serialmsg, "error", result); + aJson.addNumberToObject(result, "code", E_INTERNAL_ERROR); + aJson.addStringToObject(result,"message", strerror(E_INTERNAL_ERROR)); + + /* + if (!rpcid || !msg){ + IF_SDEBUG(IF_SDEBUG(DBGSERIAL.println(F("#add null id in response")))); + aJson.addNullToObject(serialmsg, "id"); + } else { + IF_SDEBUG(IF_SDEBUG(DBGSERIAL.println(F("#add id in response")))); + aJson.addNumberToObject(serialmsg, "id", rpcid->valueint); + } + */ + + char serialbuf[SERIALBUFFERSIZE]; + + aJson.print(serialmsg,serialbuf, sizeof(serialbuf)); + Serial.println(serialbuf); + aJson.deleteItem(serialmsg); + + } + } +} +#endif + +#ifdef SERVER +void mgr_radio(){ + unsigned int err; + if (cc110.available()) + { + // Should be a message for us now + uint8_t buf[RH_CC110_MAX_MESSAGE_LEN+1]; + uint8_t len = RH_CC110_MAX_MESSAGE_LEN; + if (cc110.recv(buf, &len)) + { + buf[len]=NULL; // terminate the string + //RH_CC110::printBuffer("#request: ", buf, len); + IF_SDEBUG(DBGSERIAL.print(F("#got request: "))); + IF_SDEBUG(DBGSERIAL.println((char*)buf)); + IF_SDEBUG(DBGSERIAL.print(F("#RSSI: "))); + IF_SDEBUG(DBGSERIAL.println(cc110.lastRssi(), DEC)); + + radiomsg = aJson.parse((char*)buf); + + if (radiomsg){ + err=rpcserver.processMessage(radiomsg); + IF_SDEBUG(DBGSERIAL.print(F("#rpcserver.processMessage return status:"))); + IF_SDEBUG(DBGSERIAL.println(err)); + if (!err) { +#ifdef TWOWAY + + // Send a reply + // "{\"jsonrpc\": \"2.0\", \"result\":true, \"id\": 0}" + + aJson.print(radiomsg, (char*)buf, sizeof(buf)); + IF_SDEBUG(DBGSERIAL.print(F("#Send: "))); + IF_SDEBUG(DBGSERIAL.println((char*)buf)); + cc110.send(buf, len); + cc110.waitPacketSent(); + IF_SDEBUG(DBGSERIAL.println(F("#Sent a reply"))); +#endif + + aJson.deleteItem(radiomsg); + }else{ + err = 1; + aJson.deleteItem(radiomsg); + } + }else{ + IF_SDEBUG(DBGSERIAL.println(F("#skip wrong message"))); + err = 2; + } + } else { + IF_SDEBUG(DBGSERIAL.println(F("recv failed"))); + err = 3; + } + } +} +#endif + +void loop() +{ + wdt_reset(); +#ifdef CLIENT + mgr_serial(); +#endif +#ifdef SERVER + wdt_reset(); + mgr_radio(); +#endif +} diff --git a/examples/e32/e32_client/e32_client.pde b/examples/e32/e32_client/e32_client.pde new file mode 100644 index 0000000..de0722b --- /dev/null +++ b/examples/e32/e32_client/e32_client.pde @@ -0,0 +1,70 @@ +// e32_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_E32 class. RH_E32 class does not provide for addressing or +// reliability, so you should only use RH_E32 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example e32_server +// Tested on Uno with E32-TTL-1W + +#include +#include "SoftwareSerial.h" + +SoftwareSerial mySerial(7, 6); +RH_E32 driver(&mySerial, 4, 5, 8); + +void setup() +{ + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + + // Init the serial connection to the E32 module + // which is assumned to be running at 9600baud. + // If your E32 has been configured to another baud rate, change this: + mySerial.begin(9600); + while (!mySerial) ; + + if (!driver.init()) + Serial.println("init failed"); + // Defaults after initialising are: + // 433MHz, 21dBm, 5kbps + // You can change these as below +// if (!driver.setDataRate(RH_E32::DataRate1kbps)) +// Serial.println("setDataRate failed"); +// if (!driver.setPower(RH_E32::Power30dBm)) +// Serial.println("setPower failed"); +// if (!driver.setFrequency(434)) +// Serial.println("setFrequency failed"); +} + +void loop() +{ + Serial.println("Sending to e32_server"); + // Send a message to e32_server + uint8_t data[] = "Hello World!"; + driver.send(data, sizeof(data)); + + driver.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_E32_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (driver.waitAvailableTimeout(10000)) // At 1kbps, reply can take a long time + { + // Should be a reply message for us now + if (driver.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is e32_server running?"); + } + delay(1000); +} diff --git a/examples/e32/e32_server/e32_server.pde b/examples/e32/e32_server/e32_server.pde new file mode 100644 index 0000000..174143c --- /dev/null +++ b/examples/e32/e32_server/e32_server.pde @@ -0,0 +1,64 @@ +// e32_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_E32 class. RH_E32 class does not provide for addressing or +// reliability, so you should only use RH_E32 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example e32_client +// Tested on Uno with E32-TTL-1W + +#include +#include "SoftwareSerial.h" + +SoftwareSerial mySerial(7, 6); +RH_E32 driver(&mySerial, 4, 5, 8); + +void setup() +{ + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + + // Init the serial connection to the E32 module + // which is assumned to be running at 9600baud. + // If your E32 has been configured to another baud rate, change this: + mySerial.begin(9600); + while (!mySerial) ; + + if (!driver.init()) + Serial.println("init failed"); + // Defaults after initialising are: + // 433MHz, 21dBm, 5kbps + // You can change these as below +// if (!driver.setDataRate(RH_E32::DataRate1kbps)) +// Serial.println("setDataRate failed"); +// if (!driver.setPower(RH_E32::Power30dBm)) +// Serial.println("setPower failed"); +// if (!driver.setFrequency(434)) +// Serial.println("setFrequency failed"); +} + +void loop() +{ + if (driver.available()) + { + // Should be a message for us now + uint8_t buf[RH_E32_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (driver.recv(buf, &len)) + { +// RH_E32::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); + + // Send a reply + uint8_t data[] = "And hello back to you"; + driver.send(data, sizeof(data)); + driver.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} diff --git a/examples/mrf89/mrf89_client/mrf89_client.pde b/examples/mrf89/mrf89_client/mrf89_client.pde new file mode 100644 index 0000000..c1955e3 --- /dev/null +++ b/examples/mrf89/mrf89_client/mrf89_client.pde @@ -0,0 +1,68 @@ +// mrf89_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_MRF89 class. RH_MRF89 class does not provide for addressing or +// reliability, so you should only use RH_RF95 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example mrf89_server +// Tested with Teensy and MRF89XAM9A + +#include +#include + +// Singleton instance of the radio driver +RH_MRF89 mrf89; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for native USB + + if (!mrf89.init()) + Serial.println("init failed"); + // Default after init is 1dBm, 915.4MHz, FSK_Rb20Fd40 + // But you can change that if you want: +// mrf89.setTxPower(RH_MRF89_TXOPVAL_M8DBM); // Min power -8dBm +// mrf89.setTxPower(RH_MRF89_TXOPVAL_13DBM); // Max power 13dBm +// if (!mrf89.setFrequency(920.0)) +// Serial.println("setFrequency failed"); +// if (!mrf89.setModemConfig(RH_MRF89::FSK_Rb200Fd200)) // Fastest +// Serial.println("setModemConfig failed"); +} + +void loop() +{ + Serial.println("Sending to mrf89_server"); + // Send a message to mrf89_server + uint8_t data[] = "Hello World!"; + mrf89.send(data, sizeof(data)); + + mrf89.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_MRF89_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (mrf89.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (mrf89.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(mrf89.lastRssi(), DEC); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is mrf89_server running?"); + } + delay(400); +} + + diff --git a/examples/mrf89/mrf89_server/mrf89_server.pde b/examples/mrf89/mrf89_server/mrf89_server.pde new file mode 100644 index 0000000..dd2b6bf --- /dev/null +++ b/examples/mrf89/mrf89_server/mrf89_server.pde @@ -0,0 +1,67 @@ +// mrf89_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_MRF89 class. RH_MRF89 class does not provide for addressing or +// reliability, so you should only use RH_MRF89 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example mrf89_client +// Tested with Teensy and MRF89XAM9A + + +#include +#include + +// Singleton instance of the radio driver +RH_MRF89 mrf89; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for native USB + + if (!mrf89.init()) + Serial.println("init failed"); + + // Default after init is 1dBm, 915.4MHz, FSK_Rb20Fd40 + // But you can change that if you want: +// mrf89.setTxPower(RH_MRF89_TXOPVAL_M8DBM); // Min power -8dBm +// mrf89.setTxPower(RH_MRF89_TXOPVAL_13DBM); // Max power 13dBm +// if (!mrf89.setFrequency(920.0)) +// Serial.println("setFrequency failed"); +// if (!mrf89.setModemConfig(RH_MRF89::FSK_Rb200Fd200)) // Fastest +// Serial.println("setModemConfig failed"); +} + +void loop() +{ + if (mrf89.available()) + { + // Should be a message for us now + uint8_t buf[RH_MRF89_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (mrf89.recv(buf, &len)) + { +// RH_MRF89::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(mrf89.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + mrf89.send(data, sizeof(data)); + mrf89.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +// delay(10000); +// mrf89.printRegisters(); +// while (1); +} + + diff --git a/examples/nrf24/nrf24_client/nrf24_client.pde b/examples/nrf24/nrf24_client/nrf24_client.pde new file mode 100644 index 0000000..d9a09c5 --- /dev/null +++ b/examples/nrf24/nrf24_client/nrf24_client.pde @@ -0,0 +1,67 @@ +// nrf24_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_NRF24 class. RH_NRF24 class does not provide for addressing or +// reliability, so you should only use RH_NRF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf24_server. +// Tested on Uno with Sparkfun NRF25L01 module +// Tested on Anarduino Mini (http://www.anarduino.com/mini/) with RFM73 module +// Tested on Arduino Mega with Sparkfun WRL-00691 NRF25L01 module + +#include +#include + +// Singleton instance of the radio driver +RH_NRF24 nrf24; +// RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf +// RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin +// RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf24.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf24.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) + Serial.println("setRF failed"); +} + + +void loop() +{ + Serial.println("Sending to nrf24_server"); + // Send a message to nrf24_server + uint8_t data[] = "Hello World!"; + nrf24.send(data, sizeof(data)); + + nrf24.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (nrf24.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (nrf24.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is nrf24_server running?"); + } + delay(400); +} + diff --git a/examples/nrf24/nrf24_encrypted_client/nrf24_encrypted_client.pde b/examples/nrf24/nrf24_encrypted_client/nrf24_encrypted_client.pde new file mode 100644 index 0000000..c98f21b --- /dev/null +++ b/examples/nrf24/nrf24_encrypted_client/nrf24_encrypted_client.pde @@ -0,0 +1,78 @@ +// nrf24_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple encrypted messageing client +// with the RH_NRF24 class. RH_NRF24 class does not provide for addressing or +// reliability, so you should only use RH_NRF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf24_encrypted_server. +// Tested on Duemilanove with Sparkfun NRF25L01 module + +#include +#include +#include +#include + +// Singleton instance of the radio driver +RH_NRF24 nrf24; +// RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf +// RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin +// RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini + +// You can choose any of several encryption ciphers +Speck myCipher; // Instantiate a Speck block ciphering +// The RHEncryptedDriver acts as a wrapper for the actual radio driver +RHEncryptedDriver driver(nrf24, myCipher); +// The key MUST be the same as the one in the server +unsigned char encryptkey[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf24.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf24.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) + Serial.println("setRF failed"); + + // Now set up the encryption key in our cipher + myCipher.setKey(encryptkey, sizeof(encryptkey)); + +} + + +void loop() +{ + Serial.println("Sending to nrf24_encrypted_server"); + // Send a message to nrf24_server + uint8_t data[] = "Hello World!"; // Dont make this too long + driver.send(data, sizeof(data)); + + driver.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (driver.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (driver.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is nrf24_encrypted_server running?"); + } + delay(400); +} + diff --git a/examples/nrf24/nrf24_encrypted_server/nrf24_encrypted_server.pde b/examples/nrf24/nrf24_encrypted_server/nrf24_encrypted_server.pde new file mode 100644 index 0000000..632119e --- /dev/null +++ b/examples/nrf24/nrf24_encrypted_server/nrf24_encrypted_server.pde @@ -0,0 +1,69 @@ +// nrf24_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple encrypted messageing server +// with the RH_NRF24 class. RH_NRF24 class does not provide for addressing or +// reliability, so you should only use RH_NRF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf24_encrypted_client + +#include +#include +#include +#include + +// Singleton instance of the radio driver +RH_NRF24 nrf24; +// RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf +// RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin +// RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini + +// You can choose any of several encryption ciphers +Speck myCipher; // Instantiate a Speck block ciphering +// The RHEncryptedDriver acts as a wrapper for the actual radio driver +RHEncryptedDriver driver(nrf24, myCipher); // Instantiate the driver with those two +// The key MUST be the same as the one in the client +unsigned char encryptkey[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf24.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf24.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) + Serial.println("setRF failed"); + + // Now set up the encryption key in our cipher + myCipher.setKey(encryptkey, sizeof(encryptkey)); +} + +void loop() +{ + if (driver.available()) + { + // Should be a message for us now + uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (driver.recv(buf, &len)) + { +// RH_NRF24::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); + + // Send a reply + uint8_t data[] = "And hello back"; // Dont make this too long + driver.send(data, sizeof(data)); + driver.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde b/examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde new file mode 100644 index 0000000..0a9b2de --- /dev/null +++ b/examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde @@ -0,0 +1,63 @@ +// nrf24_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_NRF24 driver to control a NRF24 radio. +// It is designed to work with the other example nrf24_reliable_datagram_server +// Tested on Uno with Sparkfun WRL-00691 NRF24L01 module +// Tested on Teensy with Sparkfun WRL-00691 NRF24L01 module +// Tested on Anarduino Mini (http://www.anarduino.com/mini/) with RFM73 module +// Tested on Arduino Mega with Sparkfun WRL-00691 NRF25L01 module + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF24 driver; +// RH_NRF24 driver(8, 7); // For RFM73 on Anarduino Mini + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to nrf24_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is nrf24_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde b/examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde new file mode 100644 index 0000000..43560b0 --- /dev/null +++ b/examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde @@ -0,0 +1,57 @@ +// nrf24_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_NRF24 driver to control a NRF24 radio. +// It is designed to work with the other example nrf24_reliable_datagram_client +// Tested on Uno with Sparkfun WRL-00691 NRF24L01 module +// Tested on Teensy with Sparkfun WRL-00691 NRF24L01 module +// Tested on Anarduino Mini (http://www.anarduino.com/mini/) with RFM73 module +// Tested on Arduino Mega with Sparkfun WRL-00691 NRF25L01 module + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF24 driver; +// RH_NRF24 driver(8, 7); // For RFM73 on Anarduino Mini + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/nrf24/nrf24_server/nrf24_server.pde b/examples/nrf24/nrf24_server/nrf24_server.pde new file mode 100644 index 0000000..c8dbc8c --- /dev/null +++ b/examples/nrf24/nrf24_server/nrf24_server.pde @@ -0,0 +1,60 @@ +// nrf24_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_NRF24 class. RH_NRF24 class does not provide for addressing or +// reliability, so you should only use RH_NRF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf24_client +// Tested on Uno with Sparkfun NRF25L01 module +// Tested on Anarduino Mini (http://www.anarduino.com/mini/) with RFM73 module +// Tested on Arduino Mega with Sparkfun WRL-00691 NRF25L01 module + +#include +#include + +// Singleton instance of the radio driver +RH_NRF24 nrf24; +// RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf +// RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin +// RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf24.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf24.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) + Serial.println("setRF failed"); +} + +void loop() +{ + if (nrf24.available()) + { + // Should be a message for us now + uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (nrf24.recv(buf, &len)) + { +// NRF24::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); + + // Send a reply + uint8_t data[] = "And hello back to you"; + nrf24.send(data, sizeof(data)); + nrf24.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde b/examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde new file mode 100644 index 0000000..965fd89 --- /dev/null +++ b/examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde @@ -0,0 +1,113 @@ +// nrf51_audio_rx.pde +// Sample sketch for nRF51822 and RadioHead +// +// Plays audio samples received in the radio receiver +// through a MCP4725 DAC such as on a SparkFun I2C DAC Breakout - MCP4725 (BOB-12918) +// works with matching transmitter (see nrf51_audio_tx.pde) +// Works with RedBear nRF51822 board. +// See examples/nrf51_audiotx/nrf51_audio.pdf for connection details + +#include +#include +#include +#include +#include + +// Number of samples per second to play at. +// Should match SAMPLE_RATE in nrf51_audio_tx +// The limiting factor is the time it takes to output a new sample through the DAC +#define SAMPLE_RATE 5000 + +// Number of 8 bit samples per packet +// Should equal or exceed the PACKET_SIZE in nrf51_audio_tx +#define MAX_PACKET_SIZE 255 + +// Singleton instance of the radio driver +RH_NRF51 driver; + +void setup() +{ + delay(1000); + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. + + if (!driver.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + + // Set up TIMER + // Use TIMER0 + // Timer freq before prescaling is 16MHz (VARIANT_MCK) + // We set up a 32 bit timer that restarts every 100us and outputs a new sample + NRF_TIMER0->PRESCALER = 0 << TIMER_PRESCALER_PRESCALER_Pos; + NRF_TIMER0->MODE = TIMER_MODE_MODE_Timer << TIMER_BITMODE_BITMODE_Pos; + NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos; + NRF_TIMER0->CC[0] = VARIANT_MCK / SAMPLE_RATE; // Counts per cycle + // When timer count expires, its cleared and restarts + NRF_TIMER0->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk; + NRF_TIMER0->TASKS_START = 1; + // Enable an interrupt when timer completes + NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk; + + // Enable the TIMER0 interrupt, and set the priority + // TIMER0_IRQHandler() will be called after each sample is available + NVIC_SetPriority(TIMER0_IRQn, 1); + NVIC_EnableIRQ(TIMER0_IRQn); + + // Initialise comms with the I2C DAC as fast as we can + // Shame the 51822 does not suport the high speed I2C mode that the DAC does + Wire.begin(TWI_SCL, TWI_SDA, TWI_FREQUENCY_400K); +} + +volatile uint32_t count = 0; + +uint8_t buffer_length = 0; +uint8_t buffer[MAX_PACKET_SIZE]; +uint16_t buffer_play_index = 0; + +// Write this sample to analog out +void analog_out(uint8_t val) +{ + // This takes about 120usecs, which + // is the limiting factor for our sample rate of 5kHz + // Writes to MCP4725 DAC over I2C using the Wire library + Wire.beginTransmission(0x60); // 7 bit addressing + Wire.write((val >> 4) & 0x0f); + Wire.write((val << 4) & 0xf0); + Wire.endTransmission(); +} + +// Called by timer interrupt +// Output the next available sample +void output_next_sample() +{ + if (buffer_play_index < buffer_length) + { + analog_out(buffer[buffer_play_index++]); + } +} + +void loop() +{ + // Look for a new packet of samples + if (driver.available()) + { + // expect one of these every 40ms = 25Hz + // This takes about 400us: + buffer_length = sizeof(buffer); + driver.recv(buffer, &buffer_length); + buffer_play_index = 0; // Trigger the interrupt playing of this buffer from the start + } +} + +// This interrupt handler called when the timer interrupt fires +// Time to output the next sample +void TIMER0_IRQHandler(void) +{ + // It is vitally important that analog output completes before + // the next interrupt becomes due! + output_next_sample(); + NRF_TIMER0->EVENTS_COMPARE[0] = 0; // Clear the COMPARE[0] event and the interrupt +} + diff --git a/examples/nrf51/nrf51_audio_tx/nrf51_audio.pdf b/examples/nrf51/nrf51_audio_tx/nrf51_audio.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f223703f4196d1a71c3fa7634199a4402d1dbee6 GIT binary patch literal 11291 zcmb7q2|QHa`~S#R_L5{DAv-g}7+dx|QudN%Fl3v-j4au*MMBnyC~Njz+4n7pEZIUx z6tb@=|2w8W%lG^H|9`Ll?R7i%Jm=ZYbI!fydCr^Xx{8`GR79Kt3Wk6&Rt^-hvJ|2Q z_BbaLnD5sy2n})r*@EC8D98eY1i6E3K=vRENCbohS%dEIQ-~_Np^!L?+X=5KND-tC z(gvx5z#t&}0AUn_<5DuW-<^vSo<_DgjASsX} z@J|>71Gu4pZM*~$u&fWr*@AF@)$48;8+U6I!R(C_1#m#QINrb?r|dCk6(kOrg^Dx` z0);~)q2ds@I07!kPa!8qfkNBhCky-Mq`h2FU{Pfx4(Wuk18gAeP*|`Sz@YaJ0rCG5 zQ1!&A>*D}R6r%cyV4Ryf{y_%}5rM!cM6Hovs0aiIlOmy2)YJ#tBAu`&JV>k~So9hO zjr!Xk_;2;5fO#Aa<%R}}s-W)LTch;UmCXJ)E%9GI;C;n=?1n<)e!DEHj6vgoE*30# zG6hjR6c*#|W(~{<3L#Jw(P#`X4#ls$9)+k5%Elh4gz*HM;>RV$VPLov-0UB7Kau;h zMU*j47&ml$G_b^>D&DwsJkJ(@G3?B3fqaex;_$&5A8{) z|C&3X7|*H(%;bbi4FN{{#blyu<$$t2833664&ljk@LrmN|B4ts1b-aYMxpI+cfe8v zU)AiLfG~qa)trD8R6$u|Y*2VzSRAmgohdw1S6O<|tUar87Vj5HtIgKP=}1a#WE5zK zsLphAc5@DVx!>Us)5pH~0~u2|s$BsWtF9=wp)BvVjp^_(+!?MvXq%cz^#4^`U(@ho zZ?oASvqs}!sQA7q`^ZOLetle&Hu`}@q6G~HEywKf;V6Ga{7hF+b&Er=zm?>@RU-aq zvd3yi%j=74iRm}A&*r%5S&245j7J`CAMDI7wy_NUbiw+2ZgGl8w=ewSpe*y#_5aR6 z3$kNqbvtg`l(}wx>{oNV@wo44Y9xCk@}c5G$^tQIqCEY=?s>TaaZ$ZMKH-P5nxA8; z-WlInzrcTMnc*hc8|oWQ=2dSLWYwQ)*D&y}X)M21G9nezn~vD^q08=Jx^%U!lO3vD z8T;jZ26{rH@q=4q4|5n(2ETfPsrBF&?Wt~Z2_;IXerR{C{hMVQSk1lfQ}V#tQ09hs)7bXzeaVUEyf=QlWm7igBwf*+^L-mZB{V%X|MZ=A14r z>Vum$s;FcCVzFALk$={Gr>_jPtDlMb;s%-LVmy*E`jcxs5`0u*WP(%gr^DaKxVJGS z>6YKMqmCJ!c063f6sN?@bE%ttH&1^hZ8OzAZCN8HZV$$sC5FYBZiFwhv)7)`+EVe-d6^pJMn8XxYewnwP9x(%z2Ww!nK9?F zRG&Iq9=$VkEIw8kr*gbqYDC&_2X)S=Mfjyy+9=o zS(C3jIEECL_CS;?b12m2!lzltHRH7B63;5W9)QT3>k7DD&E9v*Z)ciK%|m(}_Rr8F z7S*nNIp=PXVR#f^xgjU#aozc-K0wc4uzHLt;`=8?h04&qyW+_&1C z-mM@KZsSXNoE}6H$*RUwd^rZjdTD&r1+p=|6J>AceVIKeK6n#;VWJ^R-Of8&m^S$v6GePF zpJOaiZ|?{!2YqIvPnTw*&AU$117$HeOveiNeDNsc5OJYfd9X?GvZ{YvF;VW+eXOIL zTM)ymxeu}nWSOk2!9iQQf<5%YO;_Tkno-F{>m#X(o2Jq=@jDA^X}Z^&~$phtWtBUJZjD%(dPc?Itp!3F-7ykvfY&F zuNlXIPvA@;5-RV2d+%uGXqOOox$6gmhXk$byCz+UaaUd`)eRjQ)*;`LS+5%{Z zAi70Ui|V*p%*$9hom`}YRxG!$ciWzfr$>#i_fdCHTXgBYiAe7+kgin{U?kPw{rEWc z=4|)5w0oEVtAi;FYcM^1`g z8Cd7rUTtml%F0(5uk?&In14I)@pdPVs!TjvG$&$m%(> zB6??G=Ht(Wsa0j8f-_|w!LM8_B|h*U7fuPMZEeG;6DE_Trog;;sA2c9qV0v^iis_r z5(`|wm&6Lne%RUr9r&U^Qp)1nnQ?T)K|@7<{OEPB0=JGThv{<-!C-jY%lGZ|-?(zW ze^m1k4ABT@xIELGsBE)fIyo0Q+k?2LSmeTfm1WKU8m86kV!=;S(N*T*&d4;s#fAFw z8WYB4Bx+T)PQBUW45tO2I9Qo$NP}n}B*}Ku$e}KGNV=V)fFZd9MTXWhCjE zGt1r_$2E^#cShYB5EGrd_I~VCN+)EK*=&J1()xi^OsF(^*P9oh^2wx_&n$ z#YOX^qAVwdNSBXpBGcnpJ#%H)@C(~-BRrShttKNjYQNpqny|lS{7h!k$X8T%a`x_Z zBGDZ^6gfm-Qma6awTjd9lKFYIx7-w-NGmF4RC$mRtSDB-j&H_7IWjY4N;y7bw7c8e zJjTn(Tr=Yvn+oexyNs9IqqXpf()n9!7xk%pM>o;U?6j9#)78FS)K6tT9b~?9Q1hOu zOtm7w`npnT#?36Ocu-0P(?{06K&<##*1$n2sO^&ML)-L7KG!JQk+{ro&n2D48Pc8c624sm+4&#KwDN547V70Y zO7EWTN}!X@wob?>V&WsUrnoi07k4EAChh&f8Mp1!zqB+?9!EwKoA*^B)N*$z`Dd?J zx;NF?&@G{bGU9M}wtfI)(B_5s*~MOj17AhB(f5bZ^`Rwf^JAx;7>9I2Hkd6IP8oF4 zU@uXt1B+}S!sRnCNRMQ^Mh$thrE*$_3_=~B>UoyD#wp-NXF9WGe3YSxLL8BH+YMsO zhzIPs5qqnI^F_i3|v8_Zw*6D*>&q5FA6a_T0X59(8zr^xy?rX@>$TQ#I7L7TkLls z6XKwA+7VuJJgnG+*~Bh-QSBbLGO`>g%1GR|dYzdb5c(m>D_QoC1lh%$a5zLju`=Ux z^Hs?&0&T+4yyo55#0WT~@8+E>{Y7WMbC)LG*9#kbg`cHL$R*gA6#gH~j~(mSeMT&0ZajS-zpv-F7+7y?t#i_U%cWD*=%EHf5kJSkvo$}00VTzuFb2#^u+ z?MJ?mk1EcIWc7fIM?%6=BjSjPaj`LsuCRbl#qHgu@i+zn{M&fJZa$^askv)me%Aj| zqD>2sqy7FJ{ySU4|7OSkjE6~y!~P^g(7#hSu&BXF3Wv`ZR8UxJH+vT#H6`Q^*O1OY za(6>V5x4{mP$ueLZYT|u8wM+^hq7~bLgLw+kak!g3n2(7on%77aF`TW7$zwWhKj+( zfK*9b638kQ~k>Y$)4X#YNW~bM+habDx!&b3Uls)l^tr{n>c$lgqC4YSr=1G|Q=A zI*#&0m*CHDqf>AB6>dSIoH{&edh{@|$tDQ$$yaC2d^EA;6A^ar59j3W4!4G)B-fK# z_Ac($mB}~0{Z{zHHD^<3_c-7uMyQ&hW6~*w@mz){@ejeG4tGPF%bJ%}m_mFFQDr0R zDkXKGA2q?JigSn6_RZu&~D9*}yU#(x^q9rb ztG{;q)t!pAajJn896IYgq~--rb~(Fcg0S;Yeotx1Zo=36tV*xiBj+5+0Lh{1qb09H z?w##8!N>=xVs_FpU)ktmM^k#4`JDMYWMbbGf|w@{t1eJqFF31uv0iN7E}zjmnUs?x zBd_rmt3J>6bS_CZFW)gw{2o+k_!p{ zWkq9xl{bz&-`zn(y0buVIA6%-_RGs8lW$CNZuwDV6@QgL6{R!jGS(3bndx@$e&b!c zok=<%u~Iy>V-S6*>AOPs&Q6Y$OXg7mJP*Qu%0airw@bZ1jmWnc%KNIZtnr1L#fDAN z)Y0V(4fdnJV^Xc>s^_6^wWLc>3#Yl5p(Iechj-3UGsoWFP+%g0FKb0Yrd+#mtB#Ei z(9?QNXAPuG1+?z2k=FW8eqZ$UK5DE(%QT2k*M8s_bhv9EGLCvkx?j@NQ2C_TgZuNc z+S>gbL(|<;D_&oJq~L58E0~mR#M;Ve zGa)8(Vbd98mQ+9g7n8Wt)ZD#RCUGxwEgH66d zmxB!2$rU$KX2$MKlU&@-`G$z>DN*$NSzT>Gd{kSs3f`IA%z&vmzU{T9B%1AS6;n0{a_xe(xpT{A3)S;O7C4?J16ygM z^lfI9&*MY4%mYhV3(PV7LxVAW#(SSZ@j|QhYr6DX72t~1%*d;E6S`Wv784sSb(&9E zPR;K?CqhpjB^}?>%d7O|L#t1ow^r}NXs)@myiUDbJkPz*tcN8UDKs0amwY(TA_qO= z7Lu9N_Or|?!&(5v9H|s2vXCap$ZZ=E$;$S6{@~NJC`r?=C7}kYz4_<2a+G+S-4DvAqMr|pT#)6r8B3! z%|!XeGF$Kd-8d+x$+R7`!~GSTq?36ls5jE6@xi_0x8+wpNM3wWhdgK@m8z_fo=yG? z@rrg?aNxgSGf?bAGeXiIo>B+0RQz>+9uyO@gFW}8Kk|k{yx4sCm!NbByD!Pce0L~W z2A)v7Z?fC+;tu3K2UFx_P++619+-WYb-i4R`k&$IhetgQui zg-i$0QRHEJ?JD$EOKsB96G6=>6lDt?bZytSYb;~2nR#TlV^kw+Hh3t@#gESK6&$d< zvOc`2cq}oH;?Or30E2PmP%c=JGhX0Nn|M@Tzv0^WBz^E0>c61a*%Us_aCE)%STpc5 zx-~w-PRubno82V@Wg1H@$j_MO)RRgQ`i7lKNB3#2VMo&E7;DN1$NuohRSIcFqhXgE zdzQnipC9Ku=)Y0;da1taMqlPr8uoW`U2m)3d5%&JSrkifSVWG1ts|Z`M*5x1T4CeE>Pj;t*-pH8f=e=~9Mb?kr)$Dw1MHd?y%pQgI zjvJz4e#OOUiCws23h~u$-RRtULKN{S)IB9-+mUO3eena>T?6XS9dc1TY}Pe8bXd>J z@CW5nEZS{XNPWPS`3`;Ym2b9;B(nYV--)+r)Mz~^Y@1AsYiQez`0kmuFJ4M&&!PN8 z%XLRGA)I>P^RH=NYUy^pyP=Jjs<+5Hh`v(wU1K5tk=b!K^Q_dXh;ea_7{2=5^?i@9 zuAjsM`{MF_sd9ckwNgdWb;$chb@@aj!RNIiO5l<3hX+Ksl}h#!9TJWF zRRM;kUcTh(rmt(6a)ibo?|rt=}Atp*$(RU$F zkI&DW6^roY+O=?Yx-#swz1S-KK~?dReUx#ELj1^ro{Z*K;7~aI{SU(W&cDAzS!{?kuq+Yd`Q3ez77W?^T zy@2gb<6a%Jo$$woe7{WV&x|bO&V-cHDiYV}L!)N1&Fn|KJmC+8T(qQA9^`(eO*VgT zee;oqG2C2O!U;9q!0^r~L6!eqC$WSLIZLYVg_{ZrgO1p>T^7L=s^LBaV|K#%W z5!mY8(Du%M{Nk1RmePR?QH7T2lQ9cNZA~Z18(wo(-Ks_Vqy8KmvgRPlC|cGR^Dk>N z+9Y{Aju(q28?S6qu}a?^r5^CqsrTCt+uxHPBif1B5NLm4+?}1mUsp|*Pb}sBoNn>g zsM+2s?cUH$Q6KK8rrymS;|Q{u`rYCaJ!m%Ff9dn92R zdaQ~XZz+i6ZLe!GtDeIhQ?CB}pdqrf0GXeyZEtJw3Nv)GXq5JI6Dgu3oE&*jm9l*oLGlcNw1wXWb)4A{ zEQ`jR+7Jt!qkfcsjh5mIJV}2;bVlmAton?Hyzrgq@_6s_#n6d1jg*I#rwUF{iKT!0 zH8P$Vcme+QQkL%PAZ8jH!$3|}`9e)OX$2aT zQ{VMZH=H~TftL8mElZkPq{P;K7oUGfnk+AP@N&`UUB%lTVKh{h3Hai{UHc~0eB!{R)UP{|jJNQ+8e8RI1|C^f8D;B#>F8zGIUcL2I ziz%_Qb;lCVoV5Lkqg_y9wm03{HdNAUXT4sVOcK5i3&Z?q59QX~q@%n=z=&oa2AP4?to zhgt~xsdmpJgE(j}^ak==qm|z{FGjKkP8LV6)A-4^AZTXB#ji2zcCPUsi7-&~FeOK< zwEZG!OsV{2661lEX~en~Q#Wm`7v+eQvx!jeZUl2*>N7d|=2}l@!Pcnb5-wiqzK`;8 zYUOdeV8J*TvL4kU__~p8Pp}0e5jAp&+BCgyx1%gu!!&SlI^wg2afKX!I?GA(-J&fH6oZ4F0mSR zObRc3&02%=eaQpfdgpQFdAH#mIu7~GEV>2z`NnRs4kJShBIQkLe%_56(75p#$%OZ| z#tSSD4ZiN&4SI8ZQQScKZMD{e{4DbX))!Ar>oZ!7mR~{&9tvJH#B!BiG8nJE#VTHK zc|yRDC$!uu;s#1gOUQwhtWFtKKIxFpiaeWt1glUsbgVFbx$cNCq3WPf&dbj!AGS1> z>ZQux4m>yzXeb)Ydm~=b>`X0CWR&f8kt!dSTOl9^8@y@#uykA}MlH)#<(8~Ult%Tn zg8rU&qqgrfHG9+2FU%HVwcO&FQ#))UxU5Xl#>wjXp#7HlW#=bz)l^h@=r(&v z2=_TO426WTks5n-yDi>hK}jr9m@a6I#D~2Mvv)o7VU8lG&;C(q(p48Zt2dRo?(gNh zgpFe_WJ|!1&aw5h!)uU2m-2>-wi0b522!gX*<_B;{DQHsl_^&pxMQ_a=ojv)JU(y5 zWtf#V7M`4>!Q^_5A+0BKoi|jQ9o>cO4U4~f^o5>S?b*Yus|8eJFw1SJ4GfPfm@#GB zFy>sUag0sQD)1oVQW1>1YqU zXdr3pb6S;pQw&T~oq487f;F$zJgFVZF`ArRbL)&RX|=3x=*K`zS!JWaT>JV(Zl-6i z-+_Bf6uxjVGNiRKUC4Q+{kgPNP&o7bTr`EhN%T%K&9gAhG?lq%`h%x*l#^4zzL`&= zi6;})CCg%7{H%O!LGvxB3f99~smHwgWq#)LZByRai{<2PD=JcNyZi5NvG-Bkw5#Th zp|<;i>2qkOzoUYKUF#CfcGffu}c2t&*N;t?TOBns?Tk8UovE)qvAOe z=;5t*jORX{w--6JIOp==A*u*`VC)(RN@yO&g$+ zYmf5+i2Cj>E>0+Cd}S8`{sTn$yRZyCDLn(_SHOS)MqN`y2MOR_{>W2^8smX9rZ9vg z7!HSlVF-zn7XSRu{vW!81Pm+*lOXURr2fT&=f{uzkrfkzfMGBQSX_$0k8l5so#+O} z{@EgcIOFd>e0X`hJ$(Czb|Meh#nbS-_;LJ=Kmhk|c?k%?&L0}SkLSV5no;}chD2uLJxk@Kmb-erx>A8Bs7SV29Ea#0ufg^X(S{FV}zaxAQ+PPEkLk< zAaEjJ1Wtkl34%QY!50LfCqZyf;x~~XE}r&I3Z4OQ2h58!6k8mSvbLK_-~fq zh5*hJEZLv+13qLY`|z*bW9{yS$KIW6){{Mj-z)ZL)L;AA1>=JMzWurXeXfNEwGmEw z{x2Uq{Qd(_g#X)@P8AJoJN#Gdudk4>#-HOT8zgYj1bj5HU{mM`(hfMrGQb$3?eRMm z3_XGF{SBhSA4dH-dpgF#uguQ3K|I z@&w*(O=kd^sPwy4{^u9M(+zl;AYdqh0>AqS7gz!g7lVUs!GF_$BR~8FM*l^F!2$RG zMT0^Ri2tMkuLJ~`#J~9PFBX6V{12KqVDaBHC`1DG-+Tyh04MoxSuq40h{L~W5IFol z^+6yA*#964{6Y8+SurW8|MFQ%4EVtQ2cHyxW&JOj*gyTlxgmj5MYj_$ufDxEu)ql5 p)Dr*q?>E>}4}-yh@kgg8h)XOE>4rO55hz3w0i)pIQPowW_& +#include +#include +#include + +// Number of audio samples per second +// Should match SAMPLE_RATE in nrf51_audio_rx +// Limited by the rate we can output samples in the receiver +#define SAMPLE_RATE 5000 + +// Number of 8 bit samples per packet +#define PACKET_SIZE 200 + +// Number of ADC data buffers +#define NUM_BUFFERS 2 + +// Minimum diff between smallest and largest reading in a given buffer +// before we will send that buffer. We dont transmit quiet signals or silence +#define USE_SQUELCH 0 +#define SQUELCH_THRESHOLD 2 + +// These provide data transfer between the low level ADC interrupt handler and the +// higher level packet assembly and transmission +volatile uint8_t buffers[NUM_BUFFERS][PACKET_SIZE]; +volatile uint16_t sample_index = 0; // Of the next sample to write +volatile uint8_t buffer_index = 0; // Of the bufferbeing filled +volatile bool buffer_ready[NUM_BUFFERS]; // Set when a buffer is full + +// These hold the state of the high level transmitter code +uint8_t next_tx_buffer = 0; + +// Singleton instance of the radio driver +RH_NRF51 driver; + +void setup() +{ + delay(1000); + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. + + if (!driver.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + + // Set up ADC + // Uses the builtin 1.2V bandgap reference and no prescaling + // AnalogInput2 is A0 on RedBear nrf51822 board + // Input voltage range is 0.0 to 1.2 V + NRF_ADC->CONFIG = ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos + | ADC_CONFIG_INPSEL_AnalogInputNoPrescaling << ADC_CONFIG_INPSEL_Pos + | ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos + | ADC_CONFIG_PSEL_AnalogInput2 << ADC_CONFIG_PSEL_Pos; + NRF_ADC->ENABLE = 1; + NRF_ADC->INTENSET = ADC_INTENSET_END_Msk; // Interrupt at completion of each sample + + // Set up TIMER to trigger ADC samples + // Use TIMER0 + // Timer freq before prescaling is 16MHz (VARIANT_MCK) + // We set up a 32 bit timer that restarts every 100us and trggers a new ADC sample + NRF_TIMER0->PRESCALER = 0 << TIMER_PRESCALER_PRESCALER_Pos; + NRF_TIMER0->MODE = TIMER_MODE_MODE_Timer << TIMER_BITMODE_BITMODE_Pos; + NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos; + NRF_TIMER0->CC[0] = VARIANT_MCK / SAMPLE_RATE; // Counts per cycle + // When timer count expires, its cleared and restarts + NRF_TIMER0->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk; + NRF_TIMER0->TASKS_START = 1; + + // When the timer expires, trigger an ADC conversion + NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[0]); + NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_ADC->TASKS_START); + NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk; + + // Enable the ADC interrupt, and set the priority + // ADC_IRQHandler() will be called after each sample is available + NVIC_SetPriority(ADC_IRQn, 1); + NVIC_EnableIRQ(ADC_IRQn); +} + +// Called when a new sample is available from the ADC. +// Add it to the current buffer. +// when the buffer is full, signal that and switch to the other buffer. +void handle_sample() +{ + buffers[buffer_index][sample_index++] = NRF_ADC->RESULT; + if (sample_index >= PACKET_SIZE) + { + sample_index = 0; + buffer_ready[buffer_index] = true; + buffer_index = (buffer_index + 1) % NUM_BUFFERS; + // If the next buffer is still still full, we have an overrun + if (buffer_ready[buffer_index]) + Serial.println("Overrun"); + } +} + +void loop() { + // Wait for the adc to fill the current buffer + if (buffer_ready[next_tx_buffer]) + { +#if USE_SQUELCH + // Honour squelch settings + uint8_t min_value = 255; + uint8_t max_value = 0; + uint16_t i; + for (i = 0; i < PACKET_SIZE; i++) + { + if (buffers[next_tx_buffer][i] > max_value) + max_value = buffers[next_tx_buffer][i]; + if (buffers[next_tx_buffer][i] < min_value) + min_value = buffers[next_tx_buffer][i]; + } + if (max_value - min_value > SQUELCH_THRESHOLD) +#endif + { + // OK to send this one + driver.waitPacketSent(); // Make sure the previous packet has gone + driver.send((uint8_t*)buffers[next_tx_buffer], PACKET_SIZE); + } + + // Now get ready to wait for the next buffer + buffer_ready[next_tx_buffer] = false; + next_tx_buffer = (next_tx_buffer + 1) % NUM_BUFFERS; + } +} + +// Called as an interrupt after each new ADC sample is complete. +void ADC_IRQHandler(void) +{ + NRF_ADC->EVENTS_END = 0; // Clear the end event + handle_sample(); +} + diff --git a/examples/nrf51/nrf51_client/nrf51_client.pde b/examples/nrf51/nrf51_client/nrf51_client.pde new file mode 100644 index 0000000..03e857b --- /dev/null +++ b/examples/nrf51/nrf51_client/nrf51_client.pde @@ -0,0 +1,73 @@ +// nrf51_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_NRF51 class. RH_NRF51 class does not provide for addressing or +// reliability, so you should only use RH_NRF51 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf51_server. +// Tested on RedBearLabs nRF51822 and BLE Nano kit, built with Arduino 1.6.4. +// See http://redbearlab.com/getting-started-nrf51822/ +// for how to set up your Arduino build environment +// Also tested with Sparkfun nRF52832 breakout board, witth Arduino 1.6.13 and +// Sparkfun nRF52 boards manager 0.2.3 +#include + +// Singleton instance of the radio driver +RH_NRF51 nrf51; + +void setup() +{ + delay(1000); // Wait for serial port etc to be ready + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. + if (!nrf51.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf51.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf51.setRF(RH_NRF51::DataRate2Mbps, RH_NRF51::TransmitPower0dBm)) + Serial.println("setRF failed"); + + // AES encryption can be enabled by setting the same key in the sender and receiver +// uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, +// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; +// nrf51.setEncryptionKey(key); + +// nrf51.printRegisters(); +} + + +void loop() +{ + Serial.println("Sending to nrf51_server"); + // Send a message to nrf51_server + uint8_t data[] = "Hello World!"; + nrf51.send(data, sizeof(data)); + nrf51.waitPacketSent(); + + // Now wait for a reply + uint8_t buf[RH_NRF51_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (nrf51.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (nrf51.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is nrf51_server running?"); + } + + delay(400); +} + diff --git a/examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde b/examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde new file mode 100644 index 0000000..2f9c3ca --- /dev/null +++ b/examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde @@ -0,0 +1,66 @@ +// nrf51_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_NRF51 driver to control a NRF51 radio. +// It is designed to work with the other example nrf51_reliable_datagram_server +// Tested on RedBearLabs nRF51822 and BLE Nano kit, built with Arduino 1.6.4. +// See http://redbearlab.com/getting-started-nrf51822/ +// for how to set up your Arduino build environment +// Also tested with Sparkfun nRF52832 breakout board, witth Arduino 1.6.13 and +// Sparkfun nRF52 boards manager 0.2.3 + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF51 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + delay(1000); // Wait for serial port etc to be ready + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. + + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_NRF51_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to nrf51_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is nrf51_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde b/examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde new file mode 100644 index 0000000..9e105b4 --- /dev/null +++ b/examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde @@ -0,0 +1,61 @@ +// nrf51_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_NRF51 driver to control a NRF51 radio. +// It is designed to work with the other example nrf51_reliable_datagram_client +// Tested on RedBearLabs nRF51822 and BLE Nano kit, built with Arduino 1.6.4. +// See http://redbearlab.com/getting-started-nrf51822/ +// for how to set up your Arduino build environment +// Also tested with Sparkfun nRF52832 breakout board, witth Arduino 1.6.13 and +// Sparkfun nRF52 boards manager 0.2.3 + + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF51 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + delay(1000); // Wait for serial port etc to be ready + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. + + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_NRF51_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/nrf51/nrf51_server/nrf51_server.pde b/examples/nrf51/nrf51_server/nrf51_server.pde new file mode 100644 index 0000000..f9b1f45 --- /dev/null +++ b/examples/nrf51/nrf51_server/nrf51_server.pde @@ -0,0 +1,64 @@ +// nrf51_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_NRF51 class. RH_NRF51 class does not provide for addressing or +// reliability, so you should only use RH_NRF51 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf51_client +// Tested on RedBearLabs nRF51822 and BLE Nano kit, built with Arduino 1.6.4. +// See http://redbearlab.com/getting-started-nrf51822/ +// for how to set up your Arduino build environment +// Also tested with Sparkfun nRF52832 breakout board, witth Arduino 1.6.13 and +// Sparkfun nRF52 boards manager 0.2.3 + +#include + +// Singleton instance of the radio driver +RH_NRF51 nrf51; + +void setup() +{ + delay(1000); // Wait for serial port etc to be ready + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf51.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf51.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf51.setRF(RH_NRF51::DataRate2Mbps, RH_NRF51::TransmitPower0dBm)) + Serial.println("setRF failed"); + + // AES encryption can be enabled by setting the same key in the sender and receiver +// uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, +// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; +// nrf51.setEncryptionKey(key); +} + +void loop() +{ + if (nrf51.available()) + { + // Should be a message for us now + uint8_t buf[RH_NRF51_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (nrf51.recv(buf, &len)) + { +// NRF51::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); + + // Send a reply + uint8_t data[] = "And hello back to you"; + nrf51.send(data, sizeof(data)); + nrf51.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/nrf905/nrf905_client/nrf905_client.pde b/examples/nrf905/nrf905_client/nrf905_client.pde new file mode 100644 index 0000000..6dfcee3 --- /dev/null +++ b/examples/nrf905/nrf905_client/nrf905_client.pde @@ -0,0 +1,59 @@ +// nrf905_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_NRF905 class. RH_NRF905 class does not provide for addressing or +// reliability, so you should only use RH_NRF905 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf905_server. +// Tested on Teensy3.1 with nRF905 module +// Tested on Arduino Due with nRF905 module (Caution: use the SPI headers for connecting) + +#include +#include + +// Singleton instance of the radio driver +RH_NRF905 nrf905; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf905.init()) + Serial.println("init failed"); + // Defaults after init are 433.2 MHz (channel 108), -10dBm +} + + +void loop() +{ + Serial.println("Sending to nrf905_server"); + // Send a message to nrf905_server + uint8_t data[] = "Hello World!"; + nrf905.send(data, sizeof(data)); + + nrf905.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_NRF905_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (nrf905.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (nrf905.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is nrf905_server running?"); + } + delay(400); +} + diff --git a/examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde b/examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde new file mode 100644 index 0000000..b76f13b --- /dev/null +++ b/examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde @@ -0,0 +1,60 @@ +// nrf905_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_NRF905 driver to control a NRF905 radio. +// It is designed to work with the other example nrf905_reliable_datagram_server +// Tested on Teensy3.1 with nRF905 module +// Tested on Arduino Due with nRF905 module (Caution: use the SPI headers for connecting) + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF905 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 433.2 MHz (channel 108), -10dBm +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_NRF905_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to nrf905_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is nrf905_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde b/examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde new file mode 100644 index 0000000..b76f9ee --- /dev/null +++ b/examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde @@ -0,0 +1,54 @@ +// nrf905_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_NRF905 driver to control a NRF905 radio. +// It is designed to work with the other example nrf905_reliable_datagram_client +// Tested on Teensy3.1 with nRF905 module +// Tested on Arduino Due with nRF905 module (Caution: use the SPI headers for connecting) + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_NRF905 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 433.2 MHz (channel 108), -10dBm +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_NRF905_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/nrf905/nrf905_server/nrf905_server.pde b/examples/nrf905/nrf905_server/nrf905_server.pde new file mode 100644 index 0000000..507e7f6 --- /dev/null +++ b/examples/nrf905/nrf905_server/nrf905_server.pde @@ -0,0 +1,52 @@ +// nrf905_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_NRF905 class. RH_NRF905 class does not provide for addressing or +// reliability, so you should only use RH_NRF905 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example nrf905_client +// Tested on Teensy3.1 with nRF905 module +// Tested on Arduino Due with nRF905 module (Caution: use the SPI headers for connecting) + +#include +#include + +// Singleton instance of the radio driver +RH_NRF905 nrf905; + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; // wait for serial port to connect. Needed for Leonardo only + if (!nrf905.init()) + Serial.println("init failed"); + // Defaults after init are 433.2 MHz (channel 108), -10dBm +} + +void loop() +{ + if (nrf905.available()) + { + // Should be a message for us now + uint8_t buf[RH_NRF905_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (nrf905.recv(buf, &len)) + { +// nrf905.printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); + + // Send a reply + uint8_t data[] = "And hello back to you"; + nrf905.send(data, sizeof(data)); + nrf905.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/raspi/Makefile b/examples/raspi/Makefile new file mode 100644 index 0000000..bb24a0a --- /dev/null +++ b/examples/raspi/Makefile @@ -0,0 +1,53 @@ +# Makefile +# Sample for RH_NRF24 on Raspberry Pi +# Caution: requires bcm2835 library to be already installed +# http://www.airspayce.com/mikem/bcm2835/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -DBCM2835_NO_DELAY_COMPATIBILITY +LIBS = -lbcm2835 +RADIOHEADBASE = ../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: RasPiRH + +RasPi.o: $(RADIOHEADBASE)/RHutil/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil/RasPi.cpp $(INCLUDE) + +RasPiRH.o: RasPiRH.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_NRF24.o: $(RADIOHEADBASE)/RH_NRF24.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHMesh.o: $(RADIOHEADBASE)/RHMesh.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHNRFSPIDriver.o: $(RADIOHEADBASE)/RHNRFSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RasPiRH: RasPiRH.o RH_NRF24.o RHMesh.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHNRFSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o RasPiRH + + +clean: + rm -rf *.o RasPiRH + diff --git a/examples/raspi/RasPiRH.cpp b/examples/raspi/RasPiRH.cpp new file mode 100644 index 0000000..1864620 --- /dev/null +++ b/examples/raspi/RasPiRH.cpp @@ -0,0 +1,138 @@ +// RasPiRH.cpp +// +// Example program showing how to use RH_NRF24 on Raspberry Pi +// Uses the bcm2835 library to access the GPIO pins to drive the NRF24L01 +// Requires bcm2835 library to be already installed +// http://www.airspayce.com/mikem/bcm2835/ +// Use the Makefile in this directory: +// cd example/raspi +// make +// sudo ./RasPiRH +// +// Creates a RHReliableDatagram manager and listens and prints for reliable datagrams +// sent to it on the default Channel 2. +// +// Contributed by Mike Poublon + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); +void printbuffer(uint8_t buff[], int len); + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Create an instance of a driver +// Chip enable is pin 22 +// Slave Select is pin 24 +RH_NRF24 nrf24(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24); +RHReliableDatagram manager(nrf24, SERVER_ADDRESS); + +//Flag for Ctrl-C +volatile sig_atomic_t flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + signal(SIGINT, sig_handler); + + if (!bcm2835_init()) + { + printf( "\n\nRasPiRH Tester Startup Failed\n\n" ); + return 1; + } + + printf( "\nRasPiRH Tester Startup\n\n" ); + + /* Begin Driver Only Init Code + if (!nrf24.init()) + Serial.println("init failed"); + // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm + if (!nrf24.setChannel(1)) + Serial.println("setChannel failed"); + if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) + Serial.println("setRF failed"); + End Driver Only Init Code */ + + /* Begin Reliable Datagram Init Code */ + if (!manager.init()) + { + printf( "Init failed\n" ); + } + /* End Reliable Datagram Init Code */ + + uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + + //Begin the main body of code + while (true) + { + uint8_t len = sizeof(buf); + uint8_t from, to, id, flags; + + /* Begin Driver Only code + if (nrf24.available()) + { + // Should be a message for us now + //uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (nrf24.recv(buf, &len)) + { + Serial.print("got request: "); + Serial.println((char*)buf); + Serial.println(""); + } + else + { + Serial.println("recv failed"); + } + } + End Driver Only Code*/ + + /* Begin Reliable Datagram Code */ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + } + /* End Reliable Datagram Code */ + + if (flag) + { + printf("\n---CTRL-C Caught - Exiting---\n"); + break; + } + //sleep(1); + delay(25); + } + printf( "\nRasPiRH Tester Ending\n" ); + bcm2835_close(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} + +void printbuffer(uint8_t buff[], int len) +{ + for (int i = 0; i< len; i++) + { + printf(" %2X", buff[i]); + } +} diff --git a/examples/raspi/rf95/rf95_client/Makefile b/examples/raspi/rf95/rf95_client/Makefile new file mode 100644 index 0000000..3d00dd7 --- /dev/null +++ b/examples/raspi/rf95/rf95_client/Makefile @@ -0,0 +1,43 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_client + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_client.o: rf95_client.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_client: rf95_client.o RH_RF95.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_client + + +clean: + rm -rf *.o rf95_client + diff --git a/examples/raspi/rf95/rf95_client/rf95_client.cpp b/examples/raspi/rf95/rf95_client/rf95_client.cpp new file mode 100644 index 0000000..2207895 --- /dev/null +++ b/examples/raspi/rf95/rf95_client/rf95_client.cpp @@ -0,0 +1,147 @@ +// rf95_client.cpp +// -*- mode: C++ -*- +// Example app showing how to create a simple messaging client +// with the RH_RF95 class. RH_RF95 class does not provide for addressing or +// reliability, so you should only use RH_RF95 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf95_server. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_client. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_client.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +//Client and Server Addresses +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_client startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_client startup OK.\n" ); + printf( "\nRPI GPIO settings:\n" ); + printf("CS-> GPIO %d\n", (uint8_t) RFM95_CS_PIN); + printf("IRQ-> GPIO %d\n", (uint8_t) RFM95_IRQ_PIN); +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!rf95.init()) + { + printf( "\n\nRF95 driver failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client(This) Address= %d\n", CLIENT_ADDRESS); + printf("Server Address= %d\n", SERVER_ADDRESS); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(CLIENT_ADDRESS); + rf95.setHeaderFrom(CLIENT_ADDRESS); + rf95.setHeaderTo(SERVER_ADDRESS); + /* End Manager/Driver settings code */ + + /* Begin Datagram Client Code */ + while(!flag) + { + Serial.println("Sending to rf95_server"); + // Send a message to rf95_server +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + uint8_t data[] = "Hello World!"; + rf95.send(data, sizeof(data)); + + rf95.waitPacketSent(); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + // Now wait for a reply + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (rf95.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (rf95.recv(buf, &len)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got reply: "); + Serial.println((char*)buf); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is rf95_server running?"); + } + gpioDelay(400000); + } + printf( "\nrf95_client Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_mesh_client/Makefile b/examples/raspi/rf95/rf95_mesh_client/Makefile new file mode 100644 index 0000000..7bb19eb --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_client/Makefile @@ -0,0 +1,52 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_mesh_client + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_mesh_client.o: rf95_mesh_client.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHMesh.o: $(RADIOHEADBASE)/RHMesh.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_mesh_client: rf95_mesh_client.o RH_RF95.o RHMesh.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_mesh_client + + +clean: + rm -rf *.o rf95_mesh_client + diff --git a/examples/raspi/rf95/rf95_mesh_client/rf95_mesh_client.cpp b/examples/raspi/rf95/rf95_mesh_client/rf95_mesh_client.cpp new file mode 100644 index 0000000..6047447 --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_client/rf95_mesh_client.cpp @@ -0,0 +1,151 @@ +// rf95_mesh_client.cpp +// -*- mode: C++ -*- +// Example application showing how to create a simple addressed, routed reliable messaging client +// with the RHMesh class. +// It is designed to work with the other examples rf95_mesh_server* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_mesh_client. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_mesh_client.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +#define RH_MESH_MAX_MESSAGE_LEN 50 + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(rf95, CLIENT_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_mesh_client startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_mesh_client startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nMesh Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client(This) Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1= %d\n", SERVER1_ADDRESS); + printf("Server Address 2= %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client->Server 3 is automatic in MESH.\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + /* End Manager/Driver settings code */ + + + uint8_t data[] = "Hello World!"; + // Dont put this on the stack: + uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; + + while(!flag) + { + Serial.println("Sending to manager_mesh_server3"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + + // Send a message to a rf95_mesh_server + // A route to the destination will be automatically discovered. + if (manager.sendtoWait(data, sizeof(data), SERVER3_ADDRESS) == RH_ROUTER_ERROR_NONE) + { + // It has been reliably delivered to the next node. + // Now wait for a reply from the ultimate server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 3000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf95_mesh_server1, rf95_mesh_server2 and rf95_mesh_server3 running?"); + } + } + else + Serial.println("sendtoWait failed. Are the intermediate mesh servers running?"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + gpioDelay(400000); + } + printf( "\nrf95_mesh_client Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_mesh_server1/Makefile b/examples/raspi/rf95/rf95_mesh_server1/Makefile new file mode 100644 index 0000000..8cf13f8 --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server1/Makefile @@ -0,0 +1,52 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_mesh_server1 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_mesh_server1.o: rf95_mesh_server1.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHMesh.o: $(RADIOHEADBASE)/RHMesh.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_mesh_server1: rf95_mesh_server1.o RH_RF95.o RHMesh.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_mesh_server1 + + +clean: + rm -rf *.o rf95_mesh_server1 + diff --git a/examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp b/examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp new file mode 100644 index 0000000..10a550a --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp @@ -0,0 +1,138 @@ +// rf95_mesh_server1.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf95_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_mesh_server1. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_mesh_server1.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +#define RH_MESH_MAX_MESSAGE_LEN 50 + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(rf95, SERVER1_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_mesh_server1 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_mesh_server1 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nMesh Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server(This) Address 1= %d\n", SERVER1_ADDRESS); + printf("Server Address 2= %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client->Server 3 is automatic in MESH.\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server1"; + // Dont put this on the stack: + uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + + printf( "\nrf95_mesh_server1 Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} diff --git a/examples/raspi/rf95/rf95_mesh_server2/Makefile b/examples/raspi/rf95/rf95_mesh_server2/Makefile new file mode 100644 index 0000000..c7d399b --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server2/Makefile @@ -0,0 +1,52 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_mesh_server2 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_mesh_server2.o: rf95_mesh_server2.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHMesh.o: $(RADIOHEADBASE)/RHMesh.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_mesh_server2: rf95_mesh_server2.o RH_RF95.o RHMesh.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_mesh_server2 + + +clean: + rm -rf *.o rf95_mesh_server2 + diff --git a/examples/raspi/rf95/rf95_mesh_server2/rf95_mesh_server2.cpp b/examples/raspi/rf95/rf95_mesh_server2/rf95_mesh_server2.cpp new file mode 100644 index 0000000..79d77cb --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server2/rf95_mesh_server2.cpp @@ -0,0 +1,138 @@ +// rf95_mesh_server2.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf95_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_mesh_server2. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_mesh_server2.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +#define RH_MESH_MAX_MESSAGE_LEN 50 + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(rf95, SERVER2_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_mesh_server2 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_mesh_server2 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nMesh Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1= %d\n", SERVER1_ADDRESS); + printf("Server(This) Address 2= %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client->Server 3 is automatic in MESH.\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server2"; + // Dont put this on the stack: + uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + + printf( "\nrf95_mesh_server2 Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} diff --git a/examples/raspi/rf95/rf95_mesh_server3/Makefile b/examples/raspi/rf95/rf95_mesh_server3/Makefile new file mode 100644 index 0000000..be6a45f --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server3/Makefile @@ -0,0 +1,52 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_mesh_server3 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_mesh_server3.o: rf95_mesh_server3.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHMesh.o: $(RADIOHEADBASE)/RHMesh.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_mesh_server3: rf95_mesh_server3.o RH_RF95.o RHMesh.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_mesh_server3 + + +clean: + rm -rf *.o rf95_mesh_server3 + diff --git a/examples/raspi/rf95/rf95_mesh_server3/rf95_mesh_server3.cpp b/examples/raspi/rf95/rf95_mesh_server3/rf95_mesh_server3.cpp new file mode 100644 index 0000000..0bab8dd --- /dev/null +++ b/examples/raspi/rf95/rf95_mesh_server3/rf95_mesh_server3.cpp @@ -0,0 +1,138 @@ +// rf95_mesh_server3.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf95_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_mesh_server3. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_mesh_server3.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +#define RH_MESH_MAX_MESSAGE_LEN 50 + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(rf95, SERVER3_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_mesh_server3 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_mesh_server2 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nMesh Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1= %d\n", SERVER1_ADDRESS); + printf("Server Address 2= %d\n", SERVER2_ADDRESS); + printf("Server(This) Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client->Server 3 is automatic in MESH.\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server3"; + // Dont put this on the stack: + uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + + printf( "\nrf95_mesh_server3 Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} diff --git a/examples/raspi/rf95/rf95_reliable_datagram_client/Makefile b/examples/raspi/rf95/rf95_reliable_datagram_client/Makefile new file mode 100644 index 0000000..e47970e --- /dev/null +++ b/examples/raspi/rf95/rf95_reliable_datagram_client/Makefile @@ -0,0 +1,46 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_reliable_datagram_client + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_reliable_datagram_client.o: rf95_reliable_datagram_client.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_reliable_datagram_client: rf95_reliable_datagram_client.o RH_RF95.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_reliable_datagram_client + + +clean: + rm -rf *.o rf95_reliable_datagram_client + diff --git a/examples/raspi/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.cpp b/examples/raspi/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.cpp new file mode 100644 index 0000000..879bb29 --- /dev/null +++ b/examples/raspi/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.cpp @@ -0,0 +1,141 @@ +// rf95_reliable_datagram_client.cpp +// -*- mode: C++ -*- +// Example app showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio. +// It is designed to work with the other example rf95_reliable_datagram_server. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_reliable_datagram_client. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_reliable_datagram_client.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +//Client and Server Addresses +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(rf95, CLIENT_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_reliable_datagram_client startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_reliable_datagram_client startup OK.\n" ); + printf( "\nRPI GPIO settings:\n" ); + printf("CS-> GPIO %d\n", (uint8_t) RFM95_CS_PIN); + printf("IRQ-> GPIO %d\n", (uint8_t) RFM95_IRQ_PIN); +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!rf95.init()) + { + printf( "\n\nRF95 Driver Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client(This) Address= %d\n", CLIENT_ADDRESS); + printf("Server Address= %d\n", SERVER_ADDRESS); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(CLIENT_ADDRESS); + rf95.setHeaderFrom(CLIENT_ADDRESS); + rf95.setHeaderTo(SERVER_ADDRESS); + /* End Manager/Driver settings code */ + + /* Begin Datagram Client Code */ + uint8_t data[] = "Hello World!"; + // Dont put this on the stack: + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + + while(!flag) + { + Serial.println("Sending to rf95_reliable_datagram_server"); + // Send a message to manager_server +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf95_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + gpioDelay(500000); + } + printf( "\nrf95_reliable_datagram_client Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} diff --git a/examples/raspi/rf95/rf95_reliable_datagram_server/Makefile b/examples/raspi/rf95/rf95_reliable_datagram_server/Makefile new file mode 100644 index 0000000..22747d4 --- /dev/null +++ b/examples/raspi/rf95/rf95_reliable_datagram_server/Makefile @@ -0,0 +1,46 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_reliable_datagram_server + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_reliable_datagram_server.o: rf95_reliable_datagram_server.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_reliable_datagram_server: rf95_reliable_datagram_server.o RH_RF95.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_reliable_datagram_server + + +clean: + rm -rf *.o rf95_reliable_datagram_server + diff --git a/examples/raspi/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.cpp b/examples/raspi/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.cpp new file mode 100644 index 0000000..070df72 --- /dev/null +++ b/examples/raspi/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.cpp @@ -0,0 +1,136 @@ +// rf95_reliable_datagram_server.cpp +// -*- mode: C++ -*- +// Example app showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio. +// It is designed to work with the other example rf95_reliable_datagram_client. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_reliable_datagram_server. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_reliable_datagram_server.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +//Client and Server Addresses +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(rf95, SERVER_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_reliable_datagram_server startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_reliable_datagram_server startup OK.\n" ); + printf( "\nRPI GPIO settings:\n" ); + printf("CS-> GPIO %d\n", (uint8_t) RFM95_CS_PIN); + printf("IRQ-> GPIO %d\n", (uint8_t) RFM95_IRQ_PIN); +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!rf95.init()) + { + printf( "\n\nRF95 Driver Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server(This) Address= %d\n", SERVER_ADDRESS); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(SERVER_ADDRESS); + rf95.setHeaderFrom(SERVER_ADDRESS); + /* End Manager/Driver settings code */ + + /* Begin Reliable Datagram Server Code */ + uint8_t data[] = "And hello back to you"; + // Dont put this on the stack: + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + + while(!flag) + { + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + } + printf( "\nrf95_reliable_datagram_server Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_router_client/Makefile b/examples/raspi/rf95/rf95_router_client/Makefile new file mode 100644 index 0000000..088bf3a --- /dev/null +++ b/examples/raspi/rf95/rf95_router_client/Makefile @@ -0,0 +1,49 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_router_client + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_router_client.o: rf95_router_client.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_router_client: rf95_router_client.o RH_RF95.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_router_client + + +clean: + rm -rf *.o rf95_router_client + diff --git a/examples/raspi/rf95/rf95_router_client/rf95_router_client.cpp b/examples/raspi/rf95/rf95_router_client/rf95_router_client.cpp new file mode 100644 index 0000000..b7b122e --- /dev/null +++ b/examples/raspi/rf95/rf95_router_client/rf95_router_client.cpp @@ -0,0 +1,155 @@ +// rf95_router_client.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging client +// with the RHRouter class. +// It is designed to work with the other examples rf95_router_server*. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_router_client. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_router_client.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Create an instance of a driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(rf95, CLIENT_ADDRESS); + + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_router_client startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_router_client startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nRouter Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client(This) Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1= %d\n", SERVER1_ADDRESS); + printf("Server Address 2= %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client-> Server 1-> Server 2-> Server 3\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + + // Manually define the routes for this network + manager.addRouteTo(SERVER1_ADDRESS, SERVER1_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER3_ADDRESS); + /* End Manager/Driver settings code */ + + uint8_t data[] = "Hello World!"; + // Dont put this on the stack: + uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + + while(!flag) + { + Serial.println("Sending to rf95_router_server3"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + + // Send a message to a rf95_router_server + // It will be routed by the intermediate + // nodes to the destination node, accorinding to the + // routing tables in each node + if (manager.sendtoWait(data, sizeof(data), SERVER3_ADDRESS) == RH_ROUTER_ERROR_NONE) + { + // It has been reliably delivered to the next node. + // Now wait for a reply from the ultimate server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 3000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf95_router_server1, rf95_router_server2 and rf95_router_server3 running?"); + } + } + else + Serial.println("sendtoWait failed. Are the intermediate router servers running?"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + gpioDelay(500000); + } + printf( "\nrf95_router_client Tester Ending\n" ); + gpioTerminate(); + return 0; + +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_router_server1/Makefile b/examples/raspi/rf95/rf95_router_server1/Makefile new file mode 100644 index 0000000..e42ecfa --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server1/Makefile @@ -0,0 +1,49 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_router_server1 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_router_server1.o: rf95_router_server1.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_router_server1: rf95_router_server1.o RH_RF95.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_router_server1 + + +clean: + rm -rf *.o rf95_router_server1 + diff --git a/examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp b/examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp new file mode 100644 index 0000000..037c456 --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp @@ -0,0 +1,143 @@ +// rf95_router_server1.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf95_router_client. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_router_server1. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_router_server1.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Create an instance of a driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(rf95, SERVER1_ADDRESS); + + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_router_server1 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_router_server1 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nRouter Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server(This) Address 1= %d\n", SERVER1_ADDRESS); + printf("Server Address 2= %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client-> Server 1-> Server 2-> Server 3\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(SERVER1_ADDRESS); + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server1"; + // Dont put this on the stack: + uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + printf( "\nrf95_router_server1 Tester Ending\n" ); + gpioTerminate(); + return 0; + +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_router_server2/Makefile b/examples/raspi/rf95/rf95_router_server2/Makefile new file mode 100644 index 0000000..1ce5362 --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server2/Makefile @@ -0,0 +1,49 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_router_server2 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_router_server2.o: rf95_router_server2.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_router_server2: rf95_router_server2.o RH_RF95.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_router_server2 + + +clean: + rm -rf *.o rf95_router_server2 + diff --git a/examples/raspi/rf95/rf95_router_server2/rf95_router_server2.cpp b/examples/raspi/rf95/rf95_router_server2/rf95_router_server2.cpp new file mode 100644 index 0000000..fbecd30 --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server2/rf95_router_server2.cpp @@ -0,0 +1,143 @@ +// rf95_router_server2.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf95_router_client. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_router_server2. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_router_server2.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Create an instance of a driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(rf95, SERVER2_ADDRESS); + + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_router_server2 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_router_server2 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nRouter Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1 = %d\n", SERVER1_ADDRESS); + printf("Server(This) Address 2 = %d\n", SERVER2_ADDRESS); + printf("Server Address 3= %d\n", SERVER3_ADDRESS); + printf("Route: Client-> Server 1-> Server 2-> Server 3\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(SERVER2_ADDRESS); + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server2"; + // Dont put this on the stack: + uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + printf( "\nrf95_router_server2 Tester Ending\n" ); + gpioTerminate(); + return 0; + +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_router_server3/Makefile b/examples/raspi/rf95/rf95_router_server3/Makefile new file mode 100644 index 0000000..517c4ad --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server3/Makefile @@ -0,0 +1,49 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_router_server3 + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_router_server3.o: rf95_router_server3.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_router_server3: rf95_router_server3.o RH_RF95.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_router_server3 + + +clean: + rm -rf *.o rf95_router_server3 + diff --git a/examples/raspi/rf95/rf95_router_server3/rf95_router_server3.cpp b/examples/raspi/rf95/rf95_router_server3/rf95_router_server3.cpp new file mode 100644 index 0000000..c221acf --- /dev/null +++ b/examples/raspi/rf95/rf95_router_server3/rf95_router_server3.cpp @@ -0,0 +1,143 @@ +// rf95_router_server3.cpp +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf95_router_client. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_router_server3. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_router_server3.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Create an instance of a driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(rf95, SERVER3_ADDRESS); + + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_router_server3 startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_router_server3 startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nRouter Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server Address 1 = %d\n", SERVER1_ADDRESS); + printf("Server Address 2 = %d\n", SERVER2_ADDRESS); + printf("Server(This) Address 3 = %d\n", SERVER3_ADDRESS); + printf("Route: Client-> Server 1-> Server 2-> Server 3\n"); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(SERVER3_ADDRESS); + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); + /* End Manager/Driver settings code */ + + uint8_t data[] = "And hello back to you from server3"; + // Dont put this on the stack: + uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + + while(!flag) + { + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + } + printf( "\nrf95_router_server3 Tester Ending\n" ); + gpioTerminate(); + return 0; + +} + +void sig_handler(int sig) +{ + flag=1; +} + diff --git a/examples/raspi/rf95/rf95_router_test/Makefile b/examples/raspi/rf95/rf95_router_test/Makefile new file mode 100644 index 0000000..84b93d8 --- /dev/null +++ b/examples/raspi/rf95/rf95_router_test/Makefile @@ -0,0 +1,49 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_router_test + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_router_test.o: rf95_router_test.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHRouter.o: $(RADIOHEADBASE)/RHRouter.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHReliableDatagram.o: $(RADIOHEADBASE)/RHReliableDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_router_test: rf95_router_test.o RH_RF95.o RHRouter.o RHReliableDatagram.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_router_test + + +clean: + rm -rf *.o rf95_router_test + diff --git a/examples/raspi/rf95/rf95_router_test/rf95_router_test.cpp b/examples/raspi/rf95/rf95_router_test/rf95_router_test.cpp new file mode 100644 index 0000000..cdc2aef --- /dev/null +++ b/examples/raspi/rf95/rf95_router_test/rf95_router_test.cpp @@ -0,0 +1,183 @@ +// rf95_router_test.cpp +// -*- mode: C++ -*- +// +// Test code used during library development, showing how +// to do various things, and how to call various functions +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_router_test. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(10/6/2019) Contributed by Brody M. Based off rf22_router_tester.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include +#include + +//Function Definitions +void sig_handler(int sig); +void test_tx(void); +void test_routes(void); + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +//uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +#define CLIENT_ADDRESS 1 +#define ROUTER_ADDRESS 2 +#define SERVER_ADDRESS 3 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(rf95, CLIENT_ADDRESS); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_router_tester startup Failed.\n" ); + return 1; + } + + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_router_tester startup OK.\n" ); + +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("\nINFO: LED on GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!manager.init()) + { + printf( "\n\nRouter Manager Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client(This) Address= %d\n", CLIENT_ADDRESS); + printf("Router Address = %d\n", ROUTER_ADDRESS); + printf("Server Address = %d\n", SERVER_ADDRESS); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + /* End Manager/Driver settings code */ + + while(!flag) + { + Serial.println("Running test function..."); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif + // test_routes(); + test_tx(); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + gpioDelay(500000); + } + printf( "\nrf95_router_test Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} + +void test_routes() +{ + manager.clearRoutingTable(); +// manager.printRoutingTable(); + manager.addRouteTo(1, 101); + manager.addRouteTo(2, 102); + manager.addRouteTo(3, 103); + RHRouter::RoutingTableEntry* e; + e = manager.getRouteTo(0); + if (e) // Should fail + Serial.println("getRouteTo 0 failed"); + + e = manager.getRouteTo(1); + if (!e) + Serial.println("getRouteTo 1 failed"); + if (e->dest != 1) + Serial.println("getRouteTo 2 failed"); + if (e->next_hop != 101) + Serial.println("getRouteTo 3 failed"); + if (e->state != RHRouter::Valid) + Serial.println("getRouteTo 4 failed"); + + e = manager.getRouteTo(2); + if (!e) + Serial.println("getRouteTo 5 failed"); + if (e->dest != 2) + Serial.println("getRouteTo 6 failed"); + if (e->next_hop != 102) + Serial.println("getRouteTo 7 failed"); + if (e->state != RHRouter::Valid) + Serial.println("getRouteTo 8 failed"); + + if (!manager.deleteRouteTo(1)) + Serial.println("deleteRouteTo 1 failed"); + // Route to 1 should now be gone + e = manager.getRouteTo(1); + if (e) + Serial.println("deleteRouteTo 2 failed"); + + Serial.println("-------------------"); + +// manager.printRoutingTable(); + delay(500); + +} + +void test_tx() +{ + manager.addRouteTo(SERVER_ADDRESS, ROUTER_ADDRESS); + uint8_t errorcode; + errorcode = manager.sendtoWait(data, sizeof(data), 100); // Should fail with no route + if (errorcode != RH_ROUTER_ERROR_NO_ROUTE) + Serial.println("sendtoWait 1 failed"); + errorcode = manager.sendtoWait(data, 255, 10); // Should fail too big + if (errorcode != RH_ROUTER_ERROR_INVALID_LENGTH) + Serial.println("sendtoWait 2 failed"); + errorcode = manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS); // Should fail after timeouts to 110 + if (errorcode != RH_ROUTER_ERROR_UNABLE_TO_DELIVER) + Serial.println("sendtoWait 3 failed"); + Serial.println("-------------------"); + delay(500); +} + diff --git a/examples/raspi/rf95/rf95_server/Makefile b/examples/raspi/rf95/rf95_server/Makefile new file mode 100644 index 0000000..bcf8866 --- /dev/null +++ b/examples/raspi/rf95/rf95_server/Makefile @@ -0,0 +1,43 @@ +# Makefile +# Example for RH_RF95 on Raspberry Pi +# Requires pigpio to be installed: http://abyz.me.uk/rpi/pigpio/ + +CC = g++ +CFLAGS = -DRASPBERRY_PI -pthread +LIBS = -lpigpio -lrt +RADIOHEADBASE = ../../../.. +INCLUDE = -I$(RADIOHEADBASE) + +all: rf95_server + +RasPi.o: $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp + $(CC) $(CFLAGS) -c $(RADIOHEADBASE)/RHutil_pigpio/RasPi.cpp $(INCLUDE) + +rf95_server.o: rf95_server.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RH_RF95.o: $(RADIOHEADBASE)/RH_RF95.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHDatagram.o: $(RADIOHEADBASE)/RHDatagram.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHHardwareSPI.o: $(RADIOHEADBASE)/RHHardwareSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHSPIDriver.o: $(RADIOHEADBASE)/RHSPIDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericDriver.o: $(RADIOHEADBASE)/RHGenericDriver.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +RHGenericSPI.o: $(RADIOHEADBASE)/RHGenericSPI.cpp + $(CC) $(CFLAGS) -c $(INCLUDE) $< + +rf95_server: rf95_server.o RH_RF95.o RHDatagram.o RasPi.o RHHardwareSPI.o RHSPIDriver.o RHGenericDriver.o RHGenericSPI.o + $(CC) $^ $(LIBS) -o rf95_server + + +clean: + rm -rf *.o rf95_server + diff --git a/examples/raspi/rf95/rf95_server/rf95_server.cpp b/examples/raspi/rf95/rf95_server/rf95_server.cpp new file mode 100644 index 0000000..939d75a --- /dev/null +++ b/examples/raspi/rf95/rf95_server/rf95_server.cpp @@ -0,0 +1,134 @@ +// rf95_server.cpp +// -*- mode: C++ -*- +// Example app showing how to create a simple messageing server +// with the RH_RF95 class. RH_RF95 class does not provide for addressing or +// reliability, so you should only use RH_RF95 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf95_client. +// +// Requires Pigpio GPIO library. Install by downloading and compiling from +// http://abyz.me.uk/rpi/pigpio/, or install via command line with +// "sudo apt install pigpio". To use, run "make" at the command line in +// the folder where this source code resides. Then execute application with +// sudo ./rf95_server. +// Tested on Raspberry Pi Zero and Zero W with LoRaWan/TTN RPI Zero Shield +// by ElectronicTricks. Although this application builds and executes on +// Raspberry Pi 3, there seems to be missed messages and hangs. +// Strategically adding delays does seem to help in some cases. + +//(9/20/2019) Contributed by Brody M. Based off rf22_server.pde. +// Raspberry Pi mods influenced by nrf24 example by Mike Poublon, +// and Charles-Henri Hallard (https://github.com/hallard/RadioHead) + +#include +#include +#include +#include + +#include + +//Function Definitions +void sig_handler(int sig); + +//Pin Definitions +#define RFM95_CS_PIN 8 +#define RFM95_IRQ_PIN 25 +#define RFM95_LED 4 + +//Client and Server Addresses +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +//RFM95 Configuration +#define RFM95_FREQUENCY 915.00 +#define RFM95_TXPOWER 14 + +// Singleton instance of the radio driver +RH_RF95 rf95(RFM95_CS_PIN, RFM95_IRQ_PIN); + +//Flag for Ctrl-C +int flag = 0; + +//Main Function +int main (int argc, const char* argv[] ) +{ + if (gpioInitialise()<0) + { + printf( "\n\nRPI rf95_server startup Failed.\n" ); + return 1; + } + gpioSetSignalFunc(2, sig_handler); //2 is SIGINT. Ctrl+C will cause signal. + + printf( "\nRPI rf95_server startup OK.\n" ); + printf( "\nRPI GPIO settings:\n" ); + printf("CS-> GPIO %d\n", (uint8_t) RFM95_CS_PIN); + printf("IRQ-> GPIO %d\n", (uint8_t) RFM95_IRQ_PIN); +#ifdef RFM95_LED + gpioSetMode(RFM95_LED, PI_OUTPUT); + printf("LED-> GPIO %d\n", (uint8_t) RFM95_LED); + gpioWrite(RFM95_LED, PI_ON); + gpioDelay(500000); + gpioWrite(RFM95_LED, PI_OFF); +#endif + + if (!rf95.init()) + { + printf( "\n\nRF95 Driver Failed to initialize.\n\n" ); + return 1; + } + + /* Begin Manager/Driver settings code */ + printf("\nRFM 95 Settings:\n"); + printf("Frequency= %d MHz\n", (uint16_t) RFM95_FREQUENCY); + printf("Power= %d\n", (uint8_t) RFM95_TXPOWER); + printf("Client Address= %d\n", CLIENT_ADDRESS); + printf("Server(This) Address= %d\n", SERVER_ADDRESS); + rf95.setTxPower(RFM95_TXPOWER, false); + rf95.setFrequency(RFM95_FREQUENCY); + rf95.setThisAddress(SERVER_ADDRESS); + /* End Manager/Driver settings code */ + + /* Begin Datagram Server Code */ + while(!flag) + { + if (rf95.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (rf95.recv(buf, &len)) + { +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_ON); +#endif +// RF95::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(rf95.lastRssi(), DEC); + + //Send a reply + uint8_t data[] = "And hello back to you"; + rf95.send(data, sizeof(data)); + rf95.waitPacketSent(); + Serial.println("Sent a reply"); +#ifdef RFM95_LED + gpioWrite(RFM95_LED, PI_OFF); +#endif + } + else + { + Serial.println("recv failed"); + } + } + } + /* End Datagram Server Code */ + printf( "\nrf95_server Tester Ending\n" ); + gpioTerminate(); + return 0; +} + +void sig_handler(int sig) +{ + flag=1; +} diff --git a/examples/rf22/rf22_client/rf22_client.pde b/examples/rf22/rf22_client/rf22_client.pde new file mode 100644 index 0000000..0f1ea5a --- /dev/null +++ b/examples/rf22/rf22_client/rf22_client.pde @@ -0,0 +1,57 @@ +// rf22_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_RF22 class. RH_RF22 class does not provide for addressing or +// reliability, so you should only use RH_RF22 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf22_server +// Tested on Duemilanove, Uno with Sparkfun RFM22 wireless shield +// Tested on Flymaple with sparkfun RFM22 wireless shield +// Tested on ChiKit Uno32 with sparkfun RFM22 wireless shield + +#include +#include + +// Singleton instance of the radio driver +RH_RF22 rf22; + +void setup() +{ + Serial.begin(9600); + if (!rf22.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +void loop() +{ + Serial.println("Sending to rf22_server"); + // Send a message to rf22_server + uint8_t data[] = "Hello World!"; + rf22.send(data, sizeof(data)); + + rf22.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_RF22_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (rf22.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (rf22.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is rf22_server running?"); + } + delay(400); +} + diff --git a/examples/rf22/rf22_mesh_client/rf22_mesh_client.pde b/examples/rf22/rf22_mesh_client/rf22_mesh_client.pde new file mode 100644 index 0000000..dd8e472 --- /dev/null +++ b/examples/rf22/rf22_mesh_client/rf22_mesh_client.pde @@ -0,0 +1,68 @@ +// rf22_mesh_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging client +// with the RHMesh class. +// It is designed to work with the other examples rf22_mesh_server* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +#define RH_MESH_MAX_MESSAGE_LEN 50 + +#include +#include +#include + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to manager_mesh_server3"); + + // Send a message to a rf22_mesh_server + // A route to the destination will be automatically discovered. + if (manager.sendtoWait(data, sizeof(data), SERVER3_ADDRESS) == RH_ROUTER_ERROR_NONE) + { + // It has been reliably delivered to the next node. + // Now wait for a reply from the ultimate server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 3000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf22_mesh_server1, rf22_mesh_server2 and rf22_mesh_server3 running?"); + } + } + else + Serial.println("sendtoWait failed. Are the intermediate mesh servers running?"); +} + diff --git a/examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde b/examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde new file mode 100644 index 0000000..90ded81 --- /dev/null +++ b/examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde @@ -0,0 +1,56 @@ +// rf22_mesh_server1.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf22_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +#define RH_MESH_MAX_MESSAGE_LEN 50 + +#include +#include +#include + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(driver, SERVER1_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("RF22 init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "And hello back to you from server1"; +// Dont put this on the stack: +uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde b/examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde new file mode 100644 index 0000000..4132000 --- /dev/null +++ b/examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde @@ -0,0 +1,56 @@ +// rf22_mesh_server1.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf22_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +#define RH_MESH_MAX_MESSAGE_LEN 50 + +#include +#include +#include + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(driver, SERVER2_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("RF22 init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "And hello back to you from server2"; +// Dont put this on the stack: +uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde b/examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde new file mode 100644 index 0000000..7da1912 --- /dev/null +++ b/examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde @@ -0,0 +1,56 @@ +// rf22_mesh_server3.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHMesh class. +// It is designed to work with the other examples rf22_mesh_* +// Hint: you can simulate other network topologies by setting the +// RH_TEST_NETWORK define in RHRouter.h + +// Mesh has much greater memory requirements, and you may need to limit the +// max message length to prevent wierd crashes +#define RH_MESH_MAX_MESSAGE_LEN 50 + +#include +#include +#include + +// In this small artifical network of 4 nodes, +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHMesh manager(driver, SERVER3_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("RF22 init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "And hello back to you from server3"; +// Dont put this on the stack: +uint8_t buf[RH_MESH_MAX_MESSAGE_LEN]; +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde b/examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde new file mode 100644 index 0000000..387eabb --- /dev/null +++ b/examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde @@ -0,0 +1,62 @@ +// rf22_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_RF22 driver to control a RF22 radio. +// It is designed to work with the other example rf22_reliable_datagram_server +// Tested on Duemilanove, Uno with Sparkfun RFM22 wireless shield +// Tested on Flymaple with sparkfun RFM22 wireless shield +// Tested on ChiKit Uno32 with sparkfun RFM22 wireless shield + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF22 driver; +//RH_RF22 driver(5, 4); // ESP8266 + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_RF22_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to rf22_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf22_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde b/examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde new file mode 100644 index 0000000..897d4a9 --- /dev/null +++ b/examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde @@ -0,0 +1,56 @@ +// rf22_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_RF22 driver to control a RF22 radio. +// It is designed to work with the other example rf22_reliable_datagram_client +// Tested on Duemilanove, Uno with Sparkfun RFM22 wireless shield +// Tested on Flymaple with sparkfun RFM22 wireless shield +// Tested on ChiKit Uno32 with sparkfun RFM22 wireless shield + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF22 driver; +//RH_RF22 driver(5, 4); // ESP8266 + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_RF22_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/rf22/rf22_router_client/rf22_router_client.pde b/examples/rf22/rf22_router_client/rf22_router_client.pde new file mode 100644 index 0000000..f9959c2 --- /dev/null +++ b/examples/rf22/rf22_router_client/rf22_router_client.pde @@ -0,0 +1,72 @@ +// rf22_router_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging client +// with the RHRouter class. +// It is designed to work with the other examples rf22_router_server* + +#include +#include +#include + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + + // Manually define the routes for this network + manager.addRouteTo(SERVER1_ADDRESS, SERVER1_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER3_ADDRESS); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to rf22_router_server3"); + + // Send a message to a rf22_router_server + // It will be routed by the intermediate + // nodes to the destination node, accorinding to the + // routing tables in each node + if (manager.sendtoWait(data, sizeof(data), SERVER3_ADDRESS) == RH_ROUTER_ERROR_NONE) + { + // It has been reliably delivered to the next node. + // Now wait for a reply from the ultimate server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 3000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf22_router_server1, rf22_router_server2 and rf22_router_server3 running?"); + } + } + else + Serial.println("sendtoWait failed. Are the intermediate router servers running?"); +} + diff --git a/examples/rf22/rf22_router_server1/rf22_router_server1.pde b/examples/rf22/rf22_router_server1/rf22_router_server1.pde new file mode 100644 index 0000000..e7e4ffd --- /dev/null +++ b/examples/rf22/rf22_router_server1/rf22_router_server1.pde @@ -0,0 +1,59 @@ +// rf22_router_server1.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf22_router_client + +#include +#include +#include + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(driver, SERVER1_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); +} + +uint8_t data[] = "And hello back to you from server1"; +// Dont put this on the stack: +uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_router_server2/rf22_router_server2.pde b/examples/rf22/rf22_router_server2/rf22_router_server2.pde new file mode 100644 index 0000000..c9e86cb --- /dev/null +++ b/examples/rf22/rf22_router_server2/rf22_router_server2.pde @@ -0,0 +1,59 @@ +// rf22_router_server2.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf22_router_client + +#include +#include +#include + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(driver, SERVER2_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); +} + +uint8_t data[] = "And hello back to you from server2"; +// Dont put this on the stack: +uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_router_server3/rf22_router_server3.pde b/examples/rf22/rf22_router_server3/rf22_router_server3.pde new file mode 100644 index 0000000..9a9f1e5 --- /dev/null +++ b/examples/rf22/rf22_router_server3/rf22_router_server3.pde @@ -0,0 +1,59 @@ +// rf22_router_server3.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, routed reliable messaging server +// with the RHRouter class. +// It is designed to work with the other example rf22_router_client + +#include +#include +#include + +// In this small artifical network of 4 nodes, +// messages are routed via intermediate nodes to their destination +// node. All nodes can act as routers +// CLIENT_ADDRESS <-> SERVER1_ADDRESS <-> SERVER2_ADDRESS<->SERVER3_ADDRESS +#define CLIENT_ADDRESS 1 +#define SERVER1_ADDRESS 2 +#define SERVER2_ADDRESS 3 +#define SERVER3_ADDRESS 4 + +// Singleton instance of the radio +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(driver, SERVER3_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + + // Manually define the routes for this network + manager.addRouteTo(CLIENT_ADDRESS, CLIENT_ADDRESS); + manager.addRouteTo(SERVER2_ADDRESS, SERVER2_ADDRESS); + manager.addRouteTo(SERVER3_ADDRESS, SERVER2_ADDRESS); +} + +uint8_t data[] = "And hello back to you from server3"; +// Dont put this on the stack: +uint8_t buf[RH_ROUTER_MAX_MESSAGE_LEN]; + +void loop() +{ + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (manager.sendtoWait(data, sizeof(data), from) != RH_ROUTER_ERROR_NONE) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/rf22/rf22_router_test/rf22_router_test.pde b/examples/rf22/rf22_router_test/rf22_router_test.pde new file mode 100644 index 0000000..f0ffb45 --- /dev/null +++ b/examples/rf22/rf22_router_test/rf22_router_test.pde @@ -0,0 +1,102 @@ +// rf22_router_test.pde +// -*- mode: C++ -*- +// +// Test code used during library development, showing how +// to do various things, and how to call various functions + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define ROUTER_ADDRESS 2 +#define SERVER_ADDRESS 3 + +// Singleton instance of the radio driver +RH_RF22 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHRouter manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("RF22 init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +void test_routes() +{ + manager.clearRoutingTable(); +// manager.printRoutingTable(); + manager.addRouteTo(1, 101); + manager.addRouteTo(2, 102); + manager.addRouteTo(3, 103); + RHRouter::RoutingTableEntry* e; + e = manager.getRouteTo(0); + if (e) // Should fail + Serial.println("getRouteTo 0 failed"); + + e = manager.getRouteTo(1); + if (!e) + Serial.println("getRouteTo 1 failed"); + if (e->dest != 1) + Serial.println("getRouteTo 2 failed"); + if (e->next_hop != 101) + Serial.println("getRouteTo 3 failed"); + if (e->state != RHRouter::Valid) + Serial.println("getRouteTo 4 failed"); + + e = manager.getRouteTo(2); + if (!e) + Serial.println("getRouteTo 5 failed"); + if (e->dest != 2) + Serial.println("getRouteTo 6 failed"); + if (e->next_hop != 102) + Serial.println("getRouteTo 7 failed"); + if (e->state != RHRouter::Valid) + Serial.println("getRouteTo 8 failed"); + + if (!manager.deleteRouteTo(1)) + Serial.println("deleteRouteTo 1 failed"); + // Route to 1 should now be gone + e = manager.getRouteTo(1); + if (e) + Serial.println("deleteRouteTo 2 failed"); + + Serial.println("-------------------"); + +// manager.printRoutingTable(); + delay(500); + +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +//uint8_t buf[RH_RF22_MAX_MESSAGE_LEN]; + +void test_tx() +{ + manager.addRouteTo(SERVER_ADDRESS, ROUTER_ADDRESS); + uint8_t errorcode; + errorcode = manager.sendtoWait(data, sizeof(data), 100); // Should fail with no route + if (errorcode != RH_ROUTER_ERROR_NO_ROUTE) + Serial.println("sendtoWait 1 failed"); + errorcode = manager.sendtoWait(data, 255, 10); // Should fail too big + if (errorcode != RH_ROUTER_ERROR_INVALID_LENGTH) + Serial.println("sendtoWait 2 failed"); + errorcode = manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS); // Should fail after timeouts to 110 + if (errorcode != RH_ROUTER_ERROR_UNABLE_TO_DELIVER) + Serial.println("sendtoWait 3 failed"); + Serial.println("-------------------"); + delay(500); +} + +void loop() +{ +// test_routes(); + test_tx(); +} + + diff --git a/examples/rf22/rf22_server/rf22_server.pde b/examples/rf22/rf22_server/rf22_server.pde new file mode 100644 index 0000000..6d09a2f --- /dev/null +++ b/examples/rf22/rf22_server/rf22_server.pde @@ -0,0 +1,53 @@ +// rf22_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_RF22 class. RH_RF22 class does not provide for addressing or +// reliability, so you should only use RH_RF22 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf22_client +// Tested on Duemilanove, Uno with Sparkfun RFM22 wireless shield +// Tested on Flymaple with sparkfun RFM22 wireless shield +// Tested on ChiKit Uno32 with sparkfun RFM22 wireless shield + +#include +#include + +// Singleton instance of the radio driver +RH_RF22 rf22; + +void setup() +{ + Serial.begin(9600); + if (!rf22.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 +} + +void loop() +{ + if (rf22.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF22_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (rf22.recv(buf, &len)) + { +// RF22::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(rf22.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + rf22.send(data, sizeof(data)); + rf22.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/rf24/rf24_client/rf24_client.pde b/examples/rf24/rf24_client/rf24_client.pde new file mode 100644 index 0000000..a623c1a --- /dev/null +++ b/examples/rf24/rf24_client/rf24_client.pde @@ -0,0 +1,62 @@ +// rf24_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_RF24 class. RH_RF24 class does not provide for addressing or +// reliability, so you should only use RH_RF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf24_server. +// Tested on Anarduino Mini http://www.anarduino.com/mini/ with RFM24W and RFM26W + +#include +#include + +// Singleton instance of the radio driver +RH_RF24 rf24; + +void setup() +{ + Serial.begin(9600); + if (!rf24.init()) + Serial.println("init failed"); + // The default radio config is for 30MHz Xtal, 434MHz base freq 2GFSK 5kbps 10kHz deviation + // power setting 0x10 + // If you want a different frequency mand or modulation scheme, you must generate a new + // radio config file as per the RH_RF24 module documentation and recompile + // You can change a few other things programatically: + //rf24.setFrequency(435.0); // Only within the same frequency band + //rf24.setTxPower(0x7f); +} + + +void loop() +{ + Serial.println("Sending to rf24_server"); + // Send a message to rf24_server + uint8_t data[] = "Hello World!"; + rf24.send(data, sizeof(data)); + + rf24.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_RF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (rf24.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (rf24.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is rf24_server running?"); + } + delay(400); +} + diff --git a/examples/rf24/rf24_lowpower_client/rf24_lowpower_client.pde b/examples/rf24/rf24_lowpower_client/rf24_lowpower_client.pde new file mode 100644 index 0000000..718b260 --- /dev/null +++ b/examples/rf24/rf24_lowpower_client/rf24_lowpower_client.pde @@ -0,0 +1,80 @@ +// rf24_lowpower_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple message transmitter +// which sleeps between transmissions (every 8 secs) to reduce power consumnption +// It uses the watchdog timer and the CPU sleep mode and the radio sleep mode +// to reduce quiescent power to 1.7mA +// Tested on Anarduino Mini http://www.anarduino.com/mini/ with RFM24W and RFM26W + +#include +#include +#include +#include + +// Singleton instance of the radio driver +RH_RF24 rf24; + +// Watchdog timer interrupt handler +ISR(WDT_vect) +{ + // Dont need to do anything, just override the default vector which causes a reset +} + +// Go into sleep mode until WDT interrupt +void sleep() +{ + // Select the sleep mode we want. This is the lowest power + // that can wake with WDT interrupt + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_mode(); // Sleep here and wake on WDT interrupt every 8 secs +} + +void setup() +{ + Serial.begin(9600); + if (!rf24.init()) + Serial.println("init failed"); + // The default radio config is for 30MHz Xtal, 434MHz base freq 2GFSK 5kbps 10kHz deviation + // power setting 0x10 + // If you want a different frequency mand or modulation scheme, you must generate a new + // radio config file as per the RH_RF24 module documentation and recompile + // You can change a few other things programatically: + //rf24.setFrequency(435.0); // Only within the same frequency band + //rf24.setTxPower(0x7f); + + // Set the watchdog timer to interrupt every 8 secs + noInterrupts(); + // Set the watchdog reset bit in the MCU status register to 0. + MCUSR &= ~(1< +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF24 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // The default radio config is for 30MHz Xtal, 434MHz base freq 2GFSK 5kbps 10kHz deviation + // power setting 0x10 + // If you want a different frequency mand or modulation scheme, you must generate a new + // radio config file as per the RH_RF24 module documentation and recompile + // You can change a few other things programatically: + //driver.setFrequency(435.0); // Only within the same frequency band + //driver.setTxPower(0x7f); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_RF24_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to rf24_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf24_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde b/examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde new file mode 100644 index 0000000..50f0522 --- /dev/null +++ b/examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde @@ -0,0 +1,59 @@ +// rf24_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_RF24 driver to control a RF24 radio. +// It is designed to work with the other example rf24_reliable_datagram_client +// Tested on Anarduino Mini http://www.anarduino.com/mini/ with RFM24W and RFM26W + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF24 driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // The default radio config is for 30MHz Xtal, 434MHz base freq 2GFSK 5kbps 10kHz deviation + // power setting 0x10 + // If you want a different frequency mand or modulation scheme, you must generate a new + // radio config file as per the RH_RF24 module documentation and recompile + // You can change a few other things programatically: + //driver.setFrequency(435.0); // Only within the same frequency band + //driver.setTxPower(0x7f); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_RF24_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/rf24/rf24_server/rf24_server.pde b/examples/rf24/rf24_server/rf24_server.pde new file mode 100644 index 0000000..ad03c56 --- /dev/null +++ b/examples/rf24/rf24_server/rf24_server.pde @@ -0,0 +1,57 @@ +// rf24_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_RF24 class. RH_RF24 class does not provide for addressing or +// reliability, so you should only use RH_RF24 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf24_client +// Tested on Anarduino Mini http://www.anarduino.com/mini/ with RFM24W and RFM26W + +#include +#include + +// Singleton instance of the radio driver +RH_RF24 rf24; + +void setup() +{ + Serial.begin(9600); + if (!rf24.init()) + Serial.println("init failed"); + // The default radio config is for 30MHz Xtal, 434MHz base freq 2GFSK 5kbps 10kHz deviation + // power setting 0x10 + // If you want a different frequency mand or modulation scheme, you must generate a new + // radio config file as per the RH_RF24 module documentation and recompile + // You can change a few other things programatically: + //rf24.setFrequency(435.0); // Only within the same frequency band + //rf24.setTxPower(0x7f); +} + +void loop() +{ + if (rf24.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF24_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (rf24.recv(buf, &len)) + { +// RF24::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println((uint8_t)rf24.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + rf24.send(data, sizeof(data)); + rf24.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/rf69/rf69_client/rf69_client.pde b/examples/rf69/rf69_client/rf69_client.pde new file mode 100644 index 0000000..64aa927 --- /dev/null +++ b/examples/rf69/rf69_client/rf69_client.pde @@ -0,0 +1,77 @@ +// rf69_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_RF69 class. RH_RF69 class does not provide for addressing or +// reliability, so you should only use RH_RF69 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf69_server. +// Demonstrates the use of AES encryption, setting the frequency and modem +// configuration +// Tested on Moteino with RFM69 http://lowpowerlab.com/moteino/ +// Tested on miniWireless with RFM69 www.anarduino.com/miniwireless +// Tested on Teensy 3.1 with RF69 on PJRC breakout board + +#include +#include + +// Singleton instance of the radio driver +RH_RF69 rf69; +//RH_RF69 rf69(15, 16); // For RF69 on PJRC breakout board with Teensy 3.1 +//RH_RF69 rf69(4, 2); // For MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +//RH_RF69 rf69(8, 7); // Adafruit Feather 32u4 + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; + if (!rf69.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + // No encryption + if (!rf69.setFrequency(433.0)) + Serial.println("setFrequency failed"); + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + //rf69.setTxPower(14, true); + + // The encryption key has to be the same as the one in the server + uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + rf69.setEncryptionKey(key); +} + + +void loop() +{ + Serial.println("Sending to rf69_server"); + // Send a message to rf69_server + uint8_t data[] = "Hello World!"; + rf69.send(data, sizeof(data)); + + rf69.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (rf69.waitAvailableTimeout(500)) + { + // Should be a reply message for us now + if (rf69.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is rf69_server running?"); + } + delay(400); +} + diff --git a/examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde b/examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde new file mode 100644 index 0000000..7dba21d --- /dev/null +++ b/examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde @@ -0,0 +1,70 @@ +// rf69_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_RF69 driver to control a RF69 radio. +// It is designed to work with the other example rf69_reliable_datagram_server +// Tested on Moteino with RFM69 http://lowpowerlab.com/moteino/ +// Tested on miniWireless with RFM69 www.anarduino.com/miniwireless +// Tested on Teensy 3.1 with RF69 on PJRC breakout board + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF69 driver; +//RH_RF69 driver(15, 16); // For RF69 on PJRC breakout board with Teensy 3.1 +//RH_RF69 driver(4, 2); // For MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +//RH_RF69 driver(8, 7); // Adafruit Feather 32u4 + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + //driver.setTxPower(14, true); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to rf69_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf69_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde b/examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde new file mode 100644 index 0000000..dd9654e --- /dev/null +++ b/examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde @@ -0,0 +1,64 @@ +// rf69_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_RF69 driver to control a RF69 radio. +// It is designed to work with the other example rf69_reliable_datagram_client +// Tested on Moteino with RFM69 http://lowpowerlab.com/moteino/ +// Tested on miniWireless with RFM69 www.anarduino.com/miniwireless +// Tested on Teensy 3.1 with RF69 on PJRC breakout board + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF69 driver; +//RH_RF69 driver(15, 16); // For RF69 on PJRC breakout board with Teensy 3.1 +//RH_RF69 rf69(4, 2); // For MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +//RH_RF69 driver(8, 7); // Adafruit Feather 32u4 + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + //driver.setTxPower(14, true); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/rf69/rf69_server/rf69_server.pde b/examples/rf69/rf69_server/rf69_server.pde new file mode 100644 index 0000000..726f03d --- /dev/null +++ b/examples/rf69/rf69_server/rf69_server.pde @@ -0,0 +1,81 @@ +// rf69_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_RF69 class. RH_RF69 class does not provide for addressing or +// reliability, so you should only use RH_RF69 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf69_client +// Demonstrates the use of AES encryption, setting the frequency and modem +// configuration. +// Tested on Moteino with RFM69 http://lowpowerlab.com/moteino/ +// Tested on miniWireless with RFM69 www.anarduino.com/miniwireless +// Tested on Teensy 3.1 with RF69 on PJRC breakout board + +#include +#include + +// Singleton instance of the radio driver +RH_RF69 rf69; +//RH_RF69 rf69(15, 16); // For RF69 on PJRC breakout board with Teensy 3.1 +//RH_RF69 rf69(4, 2); // For MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +//RH_RF69 rf69(8, 7); // Adafruit Feather 32u4 + +void setup() +{ + Serial.begin(9600); + while (!Serial) + ; + if (!rf69.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + // No encryption + if (!rf69.setFrequency(433.0)) + Serial.println("setFrequency failed"); + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + //rf69.setTxPower(14, true); + + // The encryption key has to be the same as the one in the client + uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + rf69.setEncryptionKey(key); + +#if 0 + // For compat with RFM69 Struct_send + rf69.setModemConfig(RH_RF69::GFSK_Rb250Fd250); + rf69.setPreambleLength(3); + uint8_t syncwords[] = { 0x2d, 0x64 }; + rf69.setSyncWords(syncwords, sizeof(syncwords)); + rf69.setEncryptionKey((uint8_t*)"thisIsEncryptKey"); +#endif +} + +void loop() +{ + if (rf69.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (rf69.recv(buf, &len)) + { +// RH_RF69::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(rf69.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + rf69.send(data, sizeof(data)); + rf69.waitPacketSent(); + Serial.println("Sent a reply"); + } + else + { + Serial.println("recv failed"); + } + } +} + diff --git a/examples/rf95/rf95_client/rf95_client.pde b/examples/rf95/rf95_client/rf95_client.pde new file mode 100644 index 0000000..c2dd867 --- /dev/null +++ b/examples/rf95/rf95_client/rf95_client.pde @@ -0,0 +1,83 @@ +// rf95_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing client +// with the RH_RF95 class. RH_RF95 class does not provide for addressing or +// reliability, so you should only use RH_RF95 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf95_server +// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with +// the RFM95W, Adafruit Feather M0 with RFM95 + +#include +#include + +// Singleton instance of the radio driver +RH_RF95 rf95; +//RH_RF95 rf95(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W +//RH_RF95 rf95(8, 3); // Adafruit Feather M0 with RFM95 + +// Need this on Arduino Zero with SerialUSB port (eg RocketScream Mini Ultra Pro) +//#define Serial SerialUSB + +void setup() +{ + // Rocket Scream Mini Ultra Pro with the RFM95W only: + // Ensure serial flash is not interfering with radio communication on SPI bus +// pinMode(4, OUTPUT); +// digitalWrite(4, HIGH); + + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + if (!rf95.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + // You can change the modulation parameters with eg + // rf95.setModemConfig(RH_RF95::Bw500Cr45Sf128); + + // The default transmitter power is 13dBm, using PA_BOOST. + // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then + // you can set transmitter powers from 2 to 20 dBm: +// rf95.setTxPower(20, false); + // If you are using Modtronix inAir4 or inAir9, or any other module which uses the + // transmitter RFO pins and not the PA_BOOST pins + // then you can configure the power transmitter power for 0 to 15 dBm and with useRFO true. + // Failure to do that will result in extremely low transmit powers. +// rf95.setTxPower(14, true); +} + +void loop() +{ + Serial.println("Sending to rf95_server"); + // Send a message to rf95_server + uint8_t data[] = "Hello World!"; + rf95.send(data, sizeof(data)); + + rf95.waitPacketSent(); + // Now wait for a reply + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + + if (rf95.waitAvailableTimeout(3000)) + { + // Should be a reply message for us now + if (rf95.recv(buf, &len)) + { + Serial.print("got reply: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(rf95.lastRssi(), DEC); + } + else + { + Serial.println("recv failed"); + } + } + else + { + Serial.println("No reply, is rf95_server running?"); + } + delay(400); +} + + diff --git a/examples/rf95/rf95_encrypted_client/rf95_encrypted_client.pde b/examples/rf95/rf95_encrypted_client/rf95_encrypted_client.pde new file mode 100644 index 0000000..467a81f --- /dev/null +++ b/examples/rf95/rf95_encrypted_client/rf95_encrypted_client.pde @@ -0,0 +1,47 @@ +// LoRa Simple Hello World Client with encrypted communications +// In order for this to compile you MUST uncomment the #define RH_ENABLE_ENCRYPTION_MODULE line +// at the bottom of RadioHead.h, AND you MUST have installed the Crypto directory from arduinolibs: +// http://rweather.github.io/arduinolibs/index.html +// Philippe.Rochat'at'gmail.com +// 06.07.2017 + +#include +#include +#include + +RH_RF95 rf95; // Instanciate a LoRa driver +Speck myCipher; // Instanciate a Speck block ciphering +RHEncryptedDriver myDriver(rf95, myCipher); // Instantiate the driver with those two + +float frequency = 868.0; // Change the frequency here. +unsigned char encryptkey[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; // The very secret key +char HWMessage[] = "Hello World ! I'm happy if you can read me"; +uint8_t HWMessageLen; + +void setup() +{ + HWMessageLen = strlen(HWMessage); + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + Serial.println("LoRa Simple_Encrypted Client"); + if (!rf95.init()) + Serial.println("LoRa init failed"); + // Setup ISM frequency + rf95.setFrequency(frequency); + // Setup Power,dBm + rf95.setTxPower(13); + myCipher.setKey(encryptkey, sizeof(encryptkey)); + Serial.println("Waiting for radio to setup"); + delay(1000); + Serial.println("Setup completed"); +} + +void loop() +{ + uint8_t data[HWMessageLen+1] = {0}; + for(uint8_t i = 0; i<= HWMessageLen; i++) data[i] = (uint8_t)HWMessage[i]; + myDriver.send(data, sizeof(data)); // Send out ID + Sensor data to LoRa gateway + Serial.print("Sent: "); + Serial.println((char *)&data); + delay(4000); +} diff --git a/examples/rf95/rf95_encrypted_server/rf95_encrypted_server.pde b/examples/rf95/rf95_encrypted_server/rf95_encrypted_server.pde new file mode 100644 index 0000000..445b858 --- /dev/null +++ b/examples/rf95/rf95_encrypted_server/rf95_encrypted_server.pde @@ -0,0 +1,48 @@ +// LoRa simple server with encrypted communications +// In order for this to compile you MUST uncomment the #define RH_ENABLE_ENCRYPTION_MODULE line +// at the bottom of RadioHead.h, AND you MUST have installed the Crypto directory from arduinolibs: +// http://rweather.github.io/arduinolibs/index.html +// Philippe.Rochat'at'gmail.com +// 06.07.2017 + +#include +#include +#include + +RH_RF95 rf95; // Instanciate a LoRa driver +Speck myCipher; // Instanciate a Speck block ciphering +RHEncryptedDriver myDriver(rf95, myCipher); // Instantiate the driver with those two + +float frequency = 868.0; // Change the frequency here. +unsigned char encryptkey[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; // The very secret key + +void setup() { + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + Serial.println("LoRa Simple_Encrypted Server"); + if (!rf95.init()) + Serial.println("LoRa init failed"); + // Setup ISM frequency + rf95.setFrequency(frequency); + // Setup Power,dBm + rf95.setTxPower(13); + myCipher.setKey(encryptkey, 16); + delay(4000); + Serial.println("Setup completed"); +} + +void loop() { + if (myDriver.available()) { + // Should be a message for us now + uint8_t buf[myDriver.maxMessageLength()]; + uint8_t len = sizeof(buf); + if (myDriver.recv(buf, &len)) { + Serial.print("Received: "); + Serial.println((char *)&buf); + } + else + { + Serial.println("recv failed"); + } + } +} diff --git a/examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde b/examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde new file mode 100644 index 0000000..c934df6 --- /dev/null +++ b/examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde @@ -0,0 +1,84 @@ +// rf95_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio. +// It is designed to work with the other example rf95_reliable_datagram_server +// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with the RFM95W + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF95 driver; +//RH_RF95 driver(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +// Need this on Arduino Zero with SerialUSB port (eg RocketScream Mini Ultra Pro) +//#define Serial SerialUSB + +void setup() +{ + // Rocket Scream Mini Ultra Pro with the RFM95W only: + // Ensure serial flash is not interfering with radio communication on SPI bus +// pinMode(4, OUTPUT); +// digitalWrite(4, HIGH); + + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + // The default transmitter power is 13dBm, using PA_BOOST. + // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then + // you can set transmitter powers from 2 to 20 dBm: +// driver.setTxPower(20, false); + // If you are using Modtronix inAir4 or inAir9, or any other module which uses the + // transmitter RFO pins and not the PA_BOOST pins + // then you can configure the power transmitter power for 0 to 15 dBm and with useRFO true. + // Failure to do that will result in extremely low transmit powers. +// driver.setTxPower(14, true); + + // You can optionally require this module to wait until Channel Activity + // Detection shows no activity on the channel before transmitting by setting + // the CAD timeout to non-zero: +// driver.setCADTimeout(10000); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to rf95_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is rf95_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde b/examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde new file mode 100644 index 0000000..8594418 --- /dev/null +++ b/examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde @@ -0,0 +1,78 @@ +// rf95_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio. +// It is designed to work with the other example rf95_reliable_datagram_client +// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with the RFM95W + +#include +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_RF95 driver; +//RH_RF95 driver(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +// Need this on Arduino Zero with SerialUSB port (eg RocketScream Mini Ultra Pro) +//#define Serial SerialUSB + +void setup() +{ + // Rocket Scream Mini Ultra Pro with the RFM95W only: + // Ensure serial flash is not interfering with radio communication on SPI bus +// pinMode(4, OUTPUT); +// digitalWrite(4, HIGH); + + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + // The default transmitter power is 13dBm, using PA_BOOST. + // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then + // you can set transmitter powers from 2 to 20 dBm: +// driver.setTxPower(20, false); + // If you are using Modtronix inAir4 or inAir9, or any other module which uses the + // transmitter RFO pins and not the PA_BOOST pins + // then you can configure the power transmitter power for 0 to 15 dBm and with useRFO true. + // Failure to do that will result in extremely low transmit powers. +// driver.setTxPower(14, true); + + // You can optionally require this module to wait until Channel Activity + // Detection shows no activity on the channel before transmitting by setting + // the CAD timeout to non-zero: +// driver.setCADTimeout(10000); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + +void loop() +{ + if (manager.available()) + { + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } + } +} + diff --git a/examples/rf95/rf95_server/rf95_server.pde b/examples/rf95/rf95_server/rf95_server.pde new file mode 100644 index 0000000..4eabec0 --- /dev/null +++ b/examples/rf95/rf95_server/rf95_server.pde @@ -0,0 +1,82 @@ +// rf95_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple messageing server +// with the RH_RF95 class. RH_RF95 class does not provide for addressing or +// reliability, so you should only use RH_RF95 if you do not need the higher +// level messaging abilities. +// It is designed to work with the other example rf95_client +// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with +// the RFM95W, Adafruit Feather M0 with RFM95 + +#include +#include + +// Singleton instance of the radio driver +RH_RF95 rf95; +//RH_RF95 rf95(5, 2); // Rocket Scream Mini Ultra Pro with the RFM95W +//RH_RF95 rf95(8, 3); // Adafruit Feather M0 with RFM95 + +// Need this on Arduino Zero with SerialUSB port (eg RocketScream Mini Ultra Pro) +//#define Serial SerialUSB + +int led = 9; + +void setup() +{ + // Rocket Scream Mini Ultra Pro with the RFM95W only: + // Ensure serial flash is not interfering with radio communication on SPI bus +// pinMode(4, OUTPUT); +// digitalWrite(4, HIGH); + + pinMode(led, OUTPUT); + Serial.begin(9600); + while (!Serial) ; // Wait for serial port to be available + if (!rf95.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on + + // You can change the modulation parameters with eg + // rf95.setModemConfig(RH_RF95::Bw500Cr45Sf128); + +// The default transmitter power is 13dBm, using PA_BOOST. + // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then + // you can set transmitter powers from 2 to 20 dBm: +// driver.setTxPower(20, false); + // If you are using Modtronix inAir4 or inAir9, or any other module which uses the + // transmitter RFO pins and not the PA_BOOST pins + // then you can configure the power transmitter power for 0 to 15 dBm and with useRFO true. + // Failure to do that will result in extremely low transmit powers. +// driver.setTxPower(14, true); +} + +void loop() +{ + if (rf95.available()) + { + // Should be a message for us now + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; + uint8_t len = sizeof(buf); + if (rf95.recv(buf, &len)) + { + digitalWrite(led, HIGH); +// RH_RF95::printBuffer("request: ", buf, len); + Serial.print("got request: "); + Serial.println((char*)buf); +// Serial.print("RSSI: "); +// Serial.println(rf95.lastRssi(), DEC); + + // Send a reply + uint8_t data[] = "And hello back to you"; + rf95.send(data, sizeof(data)); + rf95.waitPacketSent(); + Serial.println("Sent a reply"); + digitalWrite(led, LOW); + } + else + { + Serial.println("recv failed"); + } + } +} + + diff --git a/examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde b/examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde new file mode 100644 index 0000000..af3c888 --- /dev/null +++ b/examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde @@ -0,0 +1,82 @@ +// serial_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_Serial driver to +// communicate using packets over a serial port (or a radio connected to a +// serial port, such as the 3DR Telemetry radio V1 and others). +// It is designed to work with the other example serial_reliable_datagram_server +// Tested on Arduino Mega and ChipKit Uno32 (normal Arduinos only have one +// serial port and so it not possible to test on them and still have debug +// output) +// Tested with Arduino Mega, Teensy 3.1, Moteino, Arduino Due +// Also works on Linux and OSX. Build and test with: +// tools/simBuild examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde +// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB1 ./serial_reliable_datagram_client + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + #include + // On Unix we connect to a physical serial port + // You can override this with RH_HARDWARESERIAL_DEVICE_NAME environment variable + HardwareSerial hardwareserial("/dev/ttyUSB0"); + RH_Serial driver(hardwareserial); + +#else + // On arduino etc, use a predefined local serial port + // eg Serial1 on a Mega + #include + // Singleton instance of the Serial driver, configured + // to use the port Serial1. Caution: on Uno32, Serial1 is on pins 39 (Rx) and + // 40 (Tx) + RH_Serial driver(Serial1); +#endif + + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + // Configure the port RH_Serial will use: + driver.serial().begin(9600); + if (!manager.init()) + Serial.println("init failed"); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_SERIAL_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to serial_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is serial_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde b/examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde new file mode 100644 index 0000000..ded56fd --- /dev/null +++ b/examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde @@ -0,0 +1,75 @@ +// serial_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_Serial driver to +// communicate using packets over a serial port (or a radio connected to a +// serial port, such as the 3DR Telemetry radio V1 and others). +// It is designed to work with the other example serial_reliable_datagram_client +// Tested on Arduino Mega and ChipKit Uno32 (normal Arduinos only have one +// serial port and so it not possible to test on them and still have debug +// output) +// Tested with Arduino Mega, Teensy 3.1, Moteino, Arduino Due +// Also works on Linux an OSX. Build and test with: +// tools/simBuild examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde +// RH_HARDWARESERIAL_DEVICE_NAME=/dev/ttyUSB0 ./serial_reliable_datagram_server + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +#if (RH_PLATFORM == RH_PLATFORM_UNIX) + #include + // On Unix we connect to a physical serial port + // You can override this with RH_HARDWARESERIAL_DEVICE_NAME environment variable + HardwareSerial hardwareserial("/dev/ttyUSB0"); + RH_Serial driver(hardwareserial); + +#else + // On arduino etc, use a predefined local serial port + // eg Serial1 on a Mega + #include + // Singleton instance of the Serial driver, configured + // to use the port Serial1. Caution: on Uno32, Serial1 is on pins 39 (Rx) and + // 40 (Tx) + RH_Serial driver(Serial1); +#endif + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + // Configure the port RH_Serial will use: + driver.serial().begin(9600); + if (!manager.init()) + Serial.println("init failed"); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_SERIAL_MAX_MESSAGE_LEN]; + +void loop() +{ + + // Wait for a message addressed to us from the client + manager.waitAvailable(); + + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } +} + diff --git a/examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde b/examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde new file mode 100644 index 0000000..2d77598 --- /dev/null +++ b/examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde @@ -0,0 +1,67 @@ +// simulator_reliable_datagram_client.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging client +// with the RHReliableDatagram class, using the RH_SIMULATOR driver to control a SIMULATOR radio. +// It is designed to work with the other example simulator_reliable_datagram_server +// Tested on Linux +// Build with +// cd whatever/RadioHead +// tools/simBuild examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde +// Run with ./simulator_reliable_datagram_client +// Make sure you also have the 'Luminiferous Ether' simulator tools/etherSimulator.pl running + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_TCP driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, CLIENT_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + + // Maybe set this address from teh command line + if (_simulator_argc >= 2) + manager.setThisAddress(atoi(_simulator_argv[1])); +} + +uint8_t data[] = "Hello World!"; +// Dont put this on the stack: +uint8_t buf[RH_TCP_MAX_MESSAGE_LEN]; + +void loop() +{ + Serial.println("Sending to simulator_reliable_datagram_server"); + + // Send a message to manager_server + if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) + { + // Now wait for a reply from the server + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) + { + Serial.print("got reply from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + } + else + { + Serial.println("No reply, is simulator_reliable_datagram_server running?"); + } + } + else + Serial.println("sendtoWait failed"); + delay(500); +} + diff --git a/examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde b/examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde new file mode 100644 index 0000000..74a020b --- /dev/null +++ b/examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde @@ -0,0 +1,59 @@ +// simulator_reliable_datagram_server.pde +// -*- mode: C++ -*- +// Example sketch showing how to create a simple addressed, reliable messaging server +// with the RHReliableDatagram class, using the RH_SIMULATOR driver to control a SIMULATOR radio. +// It is designed to work with the other example simulator_reliable_datagram_client +// Tested on Linux +// Build with +// cd whatever/RadioHead +// tools/simBuild examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde +// Run with ./simulator_reliable_datagram_server +// Make sure you also have the 'Luminiferous Ether' simulator tools/etherSimulator.pl running + +#include +#include + +#define CLIENT_ADDRESS 1 +#define SERVER_ADDRESS 2 + +// Singleton instance of the radio driver +RH_TCP driver; + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram manager(driver, SERVER_ADDRESS); + +void setup() +{ + Serial.begin(9600); + if (!manager.init()) + Serial.println("init failed"); + // Defaults after init are 434.0MHz, 0.05MHz AFC pull-in, modulation FSK_Rb2_4Fd36 + manager.setRetries(0); // Client will ping us if no ack received +// manager.setTimeout(50); +} + +uint8_t data[] = "And hello back to you"; +// Dont put this on the stack: +uint8_t buf[RH_TCP_MAX_MESSAGE_LEN]; + +void loop() +{ + // Wait for a message addressed to us from the client + manager.waitAvailable(); + + // Wait for a message addressed to us from the client + uint8_t len = sizeof(buf); + uint8_t from; + if (manager.recvfromAck(buf, &len, &from)) + { + Serial.print("got request from : 0x"); + Serial.print(from, HEX); + Serial.print(": "); + Serial.println((char*)buf); + + // Send a reply back to the originator client + if (!manager.sendtoWait(data, sizeof(data), from)) + Serial.println("sendtoWait failed"); + } +} + diff --git a/project.cfg b/project.cfg new file mode 100644 index 0000000..7aa2ec1 --- /dev/null +++ b/project.cfg @@ -0,0 +1,2331 @@ +# Doxyfile 1.8.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = RadioHead + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = doc + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /