Full Code of hallard/RadioHead for AI

master 818d1d2f7b07 cached
138 files
1.1 MB
332.5k tokens
130 symbols
1 requests
Download .txt
Showing preview only (1,208K chars total). Download the full file or copy to clipboard to get everything.
Repository: hallard/RadioHead
Branch: master
Commit: 818d1d2f7b07
Files: 138
Total size: 1.1 MB

Directory structure:
gitextract_jb8sxj2z/

├── .gitattributes
├── .gitignore
├── LICENSE
├── MANIFEST
├── README.md
├── RHCRC.cpp
├── RHCRC.h
├── RHDatagram.cpp
├── RHDatagram.h
├── RHGenericDriver.cpp
├── RHGenericDriver.h
├── RHGenericSPI.cpp
├── RHGenericSPI.h
├── RHHardwareSPI.cpp
├── RHHardwareSPI.h
├── RHMesh.cpp
├── RHMesh.h
├── RHNRFSPIDriver.cpp
├── RHNRFSPIDriver.h
├── RHReliableDatagram.cpp
├── RHReliableDatagram.h
├── RHRouter.cpp
├── RHRouter.h
├── RHSPIDriver.cpp
├── RHSPIDriver.h
├── RHSoftwareSPI.cpp
├── RHSoftwareSPI.h
├── RHTcpProtocol.h
├── RH_ASK.cpp
├── RH_ASK.h
├── RH_CC110.cpp
├── RH_CC110.h
├── RH_MRF89.cpp
├── RH_MRF89.h
├── RH_NRF24.cpp
├── RH_NRF24.h
├── RH_NRF51.cpp
├── RH_NRF51.h
├── RH_NRF905.cpp
├── RH_NRF905.h
├── RH_RF22.cpp
├── RH_RF22.h
├── RH_RF24.cpp
├── RH_RF24.h
├── RH_RF69.cpp
├── RH_RF69.h
├── RH_RF95.cpp
├── RH_RF95.h
├── RH_Serial.cpp
├── RH_Serial.h
├── RH_TCP.cpp
├── RH_TCP.h
├── RHutil/
│   ├── HardwareSerial.cpp
│   ├── HardwareSerial.h
│   ├── RasPi.cpp
│   ├── RasPi.h
│   ├── atomic.h
│   └── simulator.h
├── RadioHead.h
├── STM32ArduinoCompat/
│   ├── HardwareSPI.cpp
│   ├── HardwareSPI.h
│   ├── HardwareSerial.cpp
│   ├── HardwareSerial.h
│   ├── README
│   ├── wirish.cpp
│   └── wirish.h
├── examples/
│   ├── ask/
│   │   ├── ask_receiver/
│   │   │   └── ask_receiver.pde
│   │   ├── ask_reliable_datagram_client/
│   │   │   └── ask_reliable_datagram_client.pde
│   │   ├── ask_reliable_datagram_server/
│   │   │   └── ask_reliable_datagram_server.pde
│   │   └── ask_transmitter/
│   │       └── ask_transmitter.pde
│   ├── cc110/
│   │   ├── cc110_client/
│   │   │   └── cc110_client.pde
│   │   └── cc110_server/
│   │       └── cc110_server.pde
│   ├── mrf89/
│   │   ├── mrf89_client/
│   │   │   └── mrf89_client.pde
│   │   └── mrf89_server/
│   │       └── mrf89_server.pde
│   ├── nrf24/
│   │   ├── nrf24_client/
│   │   │   └── nrf24_client.pde
│   │   ├── nrf24_reliable_datagram_client/
│   │   │   └── nrf24_reliable_datagram_client.pde
│   │   ├── nrf24_reliable_datagram_server/
│   │   │   └── nrf24_reliable_datagram_server.pde
│   │   └── nrf24_server/
│   │       └── nrf24_server.pde
│   ├── nrf51/
│   │   ├── nrf51_audio_rx/
│   │   │   └── nrf51_audio_rx.pde
│   │   ├── nrf51_audio_tx/
│   │   │   └── nrf51_audio_tx.pde
│   │   ├── nrf51_client/
│   │   │   └── nrf51_client.pde
│   │   ├── nrf51_reliable_datagram_client/
│   │   │   └── nrf51_reliable_datagram_client.pde
│   │   ├── nrf51_reliable_datagram_server/
│   │   │   └── nrf51_reliable_datagram_server.pde
│   │   └── nrf51_server/
│   │       └── nrf51_server.pde
│   ├── nrf905/
│   │   ├── nrf905_client/
│   │   │   └── nrf905_client.pde
│   │   ├── nrf905_reliable_datagram_client/
│   │   │   └── nrf905_reliable_datagram_client.pde
│   │   ├── nrf905_reliable_datagram_server/
│   │   │   └── nrf905_reliable_datagram_server.pde
│   │   └── nrf905_server/
│   │       └── nrf905_server.pde
│   ├── raspi/
│   │   ├── RasPiBoards.h
│   │   ├── irq_test/
│   │   │   ├── Makefile
│   │   │   └── irq_test.c
│   │   ├── multi_server/
│   │   │   ├── Makefile
│   │   │   └── multi_server.cpp
│   │   ├── nrf24/
│   │   │   ├── Makefile
│   │   │   └── RasPiRH.cpp
│   │   ├── rf69/
│   │   │   ├── Makefile
│   │   │   ├── rf69_client.cpp
│   │   │   └── rf69_server.cpp
│   │   ├── rf95/
│   │   │   ├── Makefile
│   │   │   ├── rf95_client.cpp
│   │   │   └── rf95_server.cpp
│   │   └── spi_scan/
│   │       ├── Makefile
│   │       └── spi_scan.c
│   ├── rf22/
│   │   ├── rf22_client/
│   │   │   └── rf22_client.pde
│   │   ├── rf22_mesh_client/
│   │   │   └── rf22_mesh_client.pde
│   │   ├── rf22_mesh_server1/
│   │   │   └── rf22_mesh_server1.pde
│   │   ├── rf22_mesh_server2/
│   │   │   └── rf22_mesh_server2.pde
│   │   ├── rf22_mesh_server3/
│   │   │   └── rf22_mesh_server3.pde
│   │   ├── rf22_reliable_datagram_client/
│   │   │   └── rf22_reliable_datagram_client.pde
│   │   ├── rf22_reliable_datagram_server/
│   │   │   └── rf22_reliable_datagram_server.pde
│   │   ├── rf22_router_client/
│   │   │   └── rf22_router_client.pde
│   │   ├── rf22_router_server1/
│   │   │   └── rf22_router_server1.pde
│   │   ├── rf22_router_server2/
│   │   │   └── rf22_router_server2.pde
│   │   ├── rf22_router_server3/
│   │   │   └── rf22_router_server3.pde
│   │   ├── rf22_router_test/
│   │   │   └── rf22_router_test.pde
│   │   └── rf22_server/
│   │       └── rf22_server.pde
│   ├── rf24/
│   │   ├── rf24_client/
│   │   │   └── rf24_client.pde
│   │   ├── rf24_reliable_datagram_client/
│   │   │   └── rf24_reliable_datagram_client.pde
│   │   ├── rf24_reliable_datagram_server/
│   │   │   └── rf24_reliable_datagram_server.pde
│   │   └── rf24_server/
│   │       └── rf24_server.pde
│   ├── rf69/
│   │   ├── rf69_client/
│   │   │   └── rf69_client.pde
│   │   ├── rf69_reliable_datagram_client/
│   │   │   └── rf69_reliable_datagram_client.pde
│   │   ├── rf69_reliable_datagram_server/
│   │   │   └── rf69_reliable_datagram_server.pde
│   │   └── rf69_server/
│   │       └── rf69_server.pde
│   ├── rf95/
│   │   ├── rf95_client/
│   │   │   └── rf95_client.pde
│   │   ├── rf95_reliable_datagram_client/
│   │   │   └── rf95_reliable_datagram_client.pde
│   │   ├── rf95_reliable_datagram_server/
│   │   │   └── rf95_reliable_datagram_server.pde
│   │   └── rf95_server/
│   │       └── rf95_server.pde
│   ├── serial/
│   │   ├── serial_reliable_datagram_client/
│   │   │   └── serial_reliable_datagram_client.pde
│   │   └── serial_reliable_datagram_server/
│   │       └── serial_reliable_datagram_server.pde
│   └── simulator/
│       ├── simulator_reliable_datagram_client/
│       │   └── simulator_reliable_datagram_client.pde
│       └── simulator_reliable_datagram_server/
│           └── simulator_reliable_datagram_server.pde
├── project.cfg
├── radio_config_Si4460.h
└── tools/
    ├── chain.conf
    ├── etherSimulator.pl
    ├── simBuild
    └── simMain.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain


================================================
FILE: .gitignore
================================================

*.o

#################
## Eclipse
#################

*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# CDT-specific
.cproject

# PDT-specific
.buildpath


#################
## Visual Studio
#################

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.sln.docstates

# Build results

[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile

# Visual Studio profiler
*.psess
*.vsp
*.vspx

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
*.ncrunch*
.*crunch*.local.xml

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.Publish.xml
*.pubxml
*.publishproj

# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/

# Windows Azure Build Output
csx
*.build.csdef

# Windows Store app package directory
AppPackages/

# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
App_Data/*.mdf
App_Data/*.ldf

#############
## Windows detritus
#############

# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Mac crap
.DS_Store


#############
## Python
#############

*.py[cod]

# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox

#Translations
*.mo

#Mr Developer
.mr.developer.cfg


================================================
FILE: LICENSE
================================================
This software is Copyright (C) 2008 Mike McCauley. Use is subject to license
conditions. The main licensing options available are GPL V2 or Commercial:

Open Source Licensing GPL V2

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 2 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.


================================================
FILE: MANIFEST
================================================
RadioHead/LICENSE
RadioHead/MANIFEST
RadioHead/project.cfg
RadioHead/RadioHead.h
RadioHead/RH_ASK.cpp
RadioHead/RH_ASK.h
RadioHead/RHCRC.cpp
RadioHead/RHCRC.h
RadioHead/RHDatagram.cpp
RadioHead/RHDatagram.h
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_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/radio_config_Si4460.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/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/rf95/rf95_client/rf95_client.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/rf95/rf95_server/rf95_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_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_reliable_datagram_client/nrf24_reliable_datagram_client.pde
RadioHead/examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde
RadioHead/examples/nrf24/nrf24_server/nrf24_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/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


================================================
FILE: README.md
================================================
RadioHead Packet Radio library for embedded microprocessors
===========================================================

###Version 1.67

This is a fork of the original 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.

**Please read the full documentation and licensing from the original author [site][3]**

### features added with this fork
=================================

**Compatible with boards**    

[LoRasPI][10], [Raspberry PI Lora Gateway][12], [Dragino Lora GPS HAT][13]

<img src="https://raw.githubusercontent.com/hallard/LoRasPI/master/images/LoRasPI-on-Pi.jpg" height="25%" width="25%" alt="LoRasPI">&nbsp;
<img src="https://raw.githubusercontent.com/hallard/RPI-Lora-Gateway/master/images/RPI-Lora-Gateway-mounted.jpg" height="25%" width="25%" alt="Raspberry PI Lora Gateway/Node">&nbsp;
<img src="http://wiki.dragino.com/images/d/d6/Lora_GPS_HAT.png" height="25%" width="25%" alt="Raspberry PI Lora Gateway/Node">   

- Added moteino modem setting on RF69 to be compatible with lowpowerlab RF69 configuration library
- Added possibility to work with no IRQ connected for RF69 and RF95
  - for example to get one more GPIO free 
  - on Raspberry Pi, we do not have `attachInterrupt()` like with bcm2835 library
- Added samples for multiples Raspberry Pi boards with RF69 and RF95 modules such as 
  - [LoRasPI][10], simple RFM9x or RFM69HCW shield
  - [iC880A or Linklabs Raspberry PI shield][11] with RFM9x or RFM69HCW onboard 
  - [Raspberry PI Lora Gateway][12] with multiple RFM9x or RFM69HCW shield
  - [Dragino Lora shield][13]
  - Sample code are in [rf95][21], [rf69][20], [nrf24][22] and [multi_server][23], note that old sample NRF24 sample has been moved to nrf24 folder for consistency.
- Added 2 samples test tools (for Raspberry PI) do detect RF69 and RF95 modules and check IRQ rising edge
  - [spi_scan][9] sample code, scan and try to detect connected modules
  - [irq_test][8] sample code, check a rising edge on a GPIO

Sample code for Raspberry PI is located under [RadioHead/examples/raspi][7] folder.

### Installation on Raspberry PI
================================

Clone repository
```shell
git clone https://github.com/hallard/RadioHead
```

To avoid system hangs/instability starting with kernel 4.14, disable all GPIO kernel interrupts by adding this line to your `/boot/config.txt`:
```
dtoverlay=gpio-no-irq
```
This works around an issue with the design of the bcm2835 library and how it handles rising/falling edge detection events, but has some downsides as well.  For more information, see [this issue][30] and [this discussion][31].

**Connection and pins definition**

Boards pins (Chip Select, IRQ line, Reset and LED) definition are set in the new [RadioHead/examples/raspi/RasPiBoards.h][24] file. In your code, you need to define board used and then, include the file definition like this
```cpp
// LoRasPi board 
#define BOARD_LORASPI

// Now we include RasPi_Boards.h so this will expose defined 
// constants with CS/IRQ/RESET/on board LED pins definition
#include "../RasPiBoards.h"

// Your code start here
#ifdef RF_RST_PIN
// Blah blah do reset line
#endif

```

Then in your code you'll have exposed RF_CS_PIN, RF_IRQ_PIN, RF_RST_PIN and RF_LED_PIN and you'll be able to do some `#ifdef RF_LED_LIN` for example. See [rf95_client][25] sample code.

So you have 3 options to define the pins you want 

- The board you have is already defined so just need to define it your source code (as explained above)
- You can add your board into [RasPiBoards.h][24] and then define it your source code as above
- You can manually define pins in your code and remove the board definition and `#include "../RasPiBoards.h"`

To go further with examples :

go to example folder here spi_scan
```shell
cd RadioHead/examples/raspi/spi_scan
```
Build executable
```shell
root@pi03(rw):~/RadioHead/examples/raspi/spi_scan# make
g++ -DRASPBERRY_PI -DBCM2835_NO_DELAY_COMPATIBILITY -c -I../../.. spi_scan.c
g++ spi_scan.o -lbcm2835  -o spi_scan
root@pi03(rw):~/RadioHead/examples/raspi/spi_scan
```
And run 
```shell
root@pi03(rw):~/RadioHead/examples/raspi/spi_scan# ./spi_scan
Checking register(0x42) with CS=GPIO06 => Nothing!
Checking register(0x10) with CS=GPIO06 => Nothing!
Checking register(0x42) with CS=GPIO08 => SX1276 RF95/96 (V=0x12)
Checking register(0x10) with CS=GPIO08 => Nothing!
Checking register(0x42) with CS=GPIO07 => Nothing!
Checking register(0x10) with CS=GPIO07 => Nothing!
Checking register(0x42) with CS=GPIO26 => Nothing!
Checking register(0x10) with CS=GPIO26 => Nothing!
```
And voila! with [LoRasPi][10] board RFM95 dedected on SPI with GPIO8 (CE0)


If I'm doing same test with [PI Lora Gateway][12] with 2 RFM95 (one 433MHz and one 868MHz) and one RFMHW69 433MHz on board like this    

<img src="https://raw.githubusercontent.com/hallard/RPI-Lora-Gateway/master/images/RPI-Lora-Gateway-mounted.jpg" height="40%" width="40%" alt="Raspberry PI Lora Gateway/Node">   

Here are the results when trying to detect the onboard modules:

```shell
root@pi01(rw):~/RadioHead/examples/raspi/spi_scan# ./spi_scan
Checking register(0x42) with CS=GPIO06 => Nothing!
Checking register(0x10) with CS=GPIO06 => Nothing!
Checking register(0x42) with CS=GPIO08 => SX1276 RF95/96 (V=0x12)
Checking register(0x10) with CS=GPIO08 => Nothing!
Checking register(0x42) with CS=GPIO07 => SX1276 RF95/96 (V=0x12)
Checking register(0x10) with CS=GPIO07 => Nothing!
Checking register(0x42) with CS=GPIO26 => Unknown (V=0x01)
Checking register(0x10) with CS=GPIO26 => SX1231 RFM69 (V=0x24)
```

Voila! 3 modules are seen, now let's try listenning packets with PI Lora [Gateway][12].

My setup has another Raspberry Pi with RFM95 868MHZ [LoRasPI][10] shield running [`rf95_client`][25] sample and some [ULPnode][6] prototypes always running with on board RFM69 configured as Group ID 69 on 433MHz. I don't have a Lora 433MHz sender running so we won't receive anything on this one.

Here the results starting from scratch

**Client side**    

<img src="https://raw.githubusercontent.com/hallard/RadioHead/master/examples/raspi/pictures/rf95_client.png" alt="RF95 client">    

**multi server side**    

<img src="https://raw.githubusercontent.com/hallard/RadioHead/master/examples/raspi/pictures/multi_server.png" alt="RF95 client">   

It works! 

### Difference with original Author repo
========================================

Due to easier maintenance to keep in sync with original author lib, I've got 2 repo:    

- My master one (this one) https://github.com/hallard/RadioHead that is the one you need if you want to use my projects or lib added features.
-  The one above has been forked to https://github.com/ch2i/RadioHead where I put the original version released by the author.

Like this, I can do Pull Request from [ch2i][4] to [hallard][1] to add new features added by the author to my version. This mean that this [one][4] is just a github copy version of the latest original done by Mike, I don't do any change on this one. I know it's not the best way, but I didn't found a better solution for now, if you have better idea, just let me know.

[1]: https://github.com/hallard/RadioHead 
[2]: https://hallard.me
[3]: http://www.airspayce.com/mikem/arduino/RadioHead/
[4]: http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.67.zip
[5]: https://github.com/ch2i/RadioHead 
[6]: http://hallard.me/category/ulpnode/ 
[7]: https://github.com/hallard/RadioHead/tree/master/examples/raspi
[8]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/irq_test
[9]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/spi_scan

[10]: https://github.com/hallard/LoRasPI
[11]: https://github.com/ch2i/iC880A-Raspberry-PI
[12]: https://github.com/hallard/RPI-Lora-Gateway
[13]: https://github.com/dragino/Lora

[20]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/rf69
[21]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/rf95
[22]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/nrf24
[23]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/multi_server
[24]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/RasPiBoards.h
[25]: https://github.com/hallard/RadioHead/tree/master/examples/raspi/rf95/rf95_client.cpp

[30]: https://github.com/raspberrypi/linux/issues/2550
[31]: https://groups.google.com/forum/#!topic/bcm2835/Y3D1mmp6vew


================================================
FILE: RHCRC.cpp
================================================
/* 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 <RHCRC.h>

#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;
}




================================================
FILE: RHCRC.h
================================================
// 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 <RadioHead.h>

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


================================================
FILE: RHDatagram.cpp
================================================
// RHDatagram.cpp
//
// Copyright (C) 2011 Mike McCauley
// $Id: RHDatagram.cpp,v 1.6 2014/05/23 02:20:17 mikem Exp $

#include <RHDatagram.h>

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();
}





================================================
FILE: RHDatagram.h
================================================
// 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 <RHGenericDriver.h>

// 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 <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:<br>
/// \b TO The node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted)<br>
/// \b FROM The node address of the sending node<br>
/// \b ID A message ID, distinct (over short time scales) for each message sent by a particilar node<br>
/// \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.<br>
///
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


================================================
FILE: RHGenericDriver.cpp
================================================
// RHGenericDriver.cpp
//
// Copyright (C) 2014 Mike McCauley
// $Id: RHGenericDriver.cpp,v 1.20 2017/01/12 23:58:00 mikem Exp $

#include <RHGenericDriver.h>

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;
         delay(random(1, 10) * 100); // Should these values be configurable? Macros?
    }

    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;
}

int8_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)
{
    uint8_t i;

#ifdef RH_HAVE_SERIAL
    Serial.println(prompt);
    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_ARDUINO) && defined(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


================================================
FILE: RHGenericDriver.h
================================================
// RHGenericDriver.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2014 Mike McCauley
// $Id: RHGenericDriver.h,v 1.18 2017/01/12 23:58:00 mikem Exp $

#ifndef RHGenericDriver_h
#define RHGenericDriver_h

#include <RadioHead.h>

// 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 <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();

    /// 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.
    int8_t        lastRssi();

    /// Returns the operating mode of the library.
    /// \return the current mode, one of RF69_MODE_*
    RHMode          mode();

    /// Sets the operating mode of the transport.
    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.
    uint16_t       rxBad();

    /// Returns the count of the number of 
    /// good received packets
    /// \return The number of good packets received.
    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
    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 int8_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;
    unsigned int        _cad_timeout;
    
private:

};


#endif 


================================================
FILE: RHGenericSPI.cpp
================================================
// 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.h>

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;
}



================================================
FILE: RHGenericSPI.h
================================================
// RHGenericSPI.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// Contributed by Joanna Rutkowska
// $Id: RHGenericSPI.h,v 1.7 2014/04/14 08:37:11 mikem Exp $

#ifndef RHGenericSPI_h
#define RHGenericSPI_h

#include <RadioHead.h>

/////////////////////////////////////////////////////////////////////
/// \class RHGenericSPI RHGenericSPI.h <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;

    /// 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);

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


================================================
FILE: RHHardwareSPI.cpp
================================================
// RHHardwareSPI.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// Contributed by Joanna Rutkowska
// $Id: RHHardwareSPI.cpp,v 1.16 2016/07/07 00:02:53 mikem Exp $

#include <RHHardwareSPI.h>

// Declare a single default instance of the hardware SPI interface class
RHHardwareSPI hardware_spi;

#ifdef RH_HAVE_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);
#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)
 // 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);
}

void RHHardwareSPI::attachInterrupt() 
{
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
    SPI.attachInterrupt();
#endif
}

void RHHardwareSPI::detachInterrupt() 
{
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
    SPI.detachInterrupt();
#endif
}
    
void RHHardwareSPI::begin() 
{
    // 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)
    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)
    // 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))
    // Arduino Due in 1.5.5 has its own BitOrder :-(
    // So too does Arduino Zero
    ::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
    Serial.println("HERE");
    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);
#else
 #warning RHHardwareSPI does not support this platform yet. Consider adding it and contributing a patch.
#endif
}

void RHHardwareSPI::end() 
{
    return SPI.end();
}

#endif



================================================
FILE: RHHardwareSPI.h
================================================
// RHHardwareSPI.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// Contributed by Joanna Rutkowska
// $Id: RHHardwareSPI.h,v 1.10 2017/01/12 23:58:00 mikem Exp $

#ifndef RHHardwareSPI_h
#define RHHardwareSPI_h

#include <RHGenericSPI.h>

/////////////////////////////////////////////////////////////////////
/// \class RHHardwareSPI RHHardwareSPI.h <RHHardwareSPI.h>
/// \brief Encapsulate a hardware SPI bus interface
///
/// This concrete subclass of GenericSPIClass encapsulates the standard Arduino hardware and other
/// hardware SPI interfaces.
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);

    // 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
};

// Built in default instance
extern RHHardwareSPI hardware_spi;

#endif


================================================
FILE: RHMesh.cpp
================================================
// 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.9 2015/08/13 02:45:47 mikem Exp $

#include <RHMesh.h>

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 tmpMessageLen = sizeof(_tmpMessage);
    uint8_t _source;
    uint8_t _dest;
    uint8_t _id;
    uint8_t _flags;
    if (RHRouter::recvfromAck(_tmpMessage, &tmpMessageLen, &_source, &_dest, &_id, &_flags))
    {
	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;
	    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
	    
	    // Hasnt been past us yet, record routes back to the earlier nodes
	    addRouteTo(_source, headerFrom()); // The originator
	    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)
	    {
		// 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)
{  
    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;
}





================================================
FILE: RHMesh.h
================================================
// RHMesh.h
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// $Id: RHMesh.h,v 1.15 2015/08/13 02:45:47 mikem Exp $

#ifndef RHMesh_h
#define RHMesh_h

#include <RHRouter.h>

// 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 <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.g
	uint8_t             dest;    ///< The address of the destination node whose route is being sought
	uint8_t             route[RH_MESH_MAX_MESSAGE_LEN - 1]; ///< 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
    /// (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);

    /// 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
    /// (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);

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



================================================
FILE: RHNRFSPIDriver.cpp
================================================
// RHNRFSPIDriver.cpp
//
// Copyright (C) 2014 Mike McCauley
// $Id: RHNRFSPIDriver.cpp,v 1.3 2015/12/16 04:55:33 mikem Exp $

#include <RHNRFSPIDriver.h>

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;
    ATOMIC_BLOCK_START;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(command);
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return status;
}

uint8_t RHNRFSPIDriver::spiRead(uint8_t reg)
{
    uint8_t val;
    ATOMIC_BLOCK_START;
    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);
    ATOMIC_BLOCK_END;
    return val;
}

uint8_t RHNRFSPIDriver::spiWrite(uint8_t reg, uint8_t val)
{
    uint8_t status = 0;
    ATOMIC_BLOCK_START;
    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);
    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;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(reg); // Send the start address
    while (len--)
	*dest++ = _spi.transfer(0);
    digitalWrite(_slaveSelectPin, HIGH);
    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;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(reg); // Send the start address
    while (len--)
	_spi.transfer(*src++);
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return status;
}

void RHNRFSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin)
{
    _slaveSelectPin = slaveSelectPin;
}




================================================
FILE: RHNRFSPIDriver.h
================================================
// RHNRFSPIDriver.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2014 Mike McCauley
// $Id: RHNRFSPIDriver.h,v 1.3 2015/12/16 04:55:33 mikem Exp $

#ifndef RHNRFSPIDriver_h
#define RHNRFSPIDriver_h

#include <RHGenericDriver.h>
#include <RHHardwareSPI.h>

class RHGenericSPI;

/////////////////////////////////////////////////////////////////////
/// \class RHNRFSPIDriver RHNRFSPIDriver.h <RHNRFSPIDriver.h>
/// \brief Base class for a RadioHead driver 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 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);

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


================================================
FILE: RHReliableDatagram.cpp
================================================
// 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.16 2017/01/12 23:58:00 mikem Exp $

#include <RHReliableDatagram.h>

////////////////////////////////////////////////////////////////////
// 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);
	setHeaderFlags(RH_FLAGS_NONE, RH_FLAGS_ACK); // Clear the ACK flag
	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 for this node, not an ACK
	    if (_to != RH_BROADCAST_ADDRESS)
	    {
		// Its not a broadcast, so ACK it
		// Acknowledge message with ACK set in flags and ID set to received ID
		acknowledge(_id, _from);
	    }
	    // If we have not seen this message before, then we are interested in it
	    if (_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();
}



================================================
FILE: RHReliableDatagram.h
================================================
// RHReliableDatagram.h
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// $Id: RHReliableDatagram.h,v 1.17 2016/04/04 01:40:12 mikem Exp $

#ifndef RHReliableDatagram_h
#define RHReliableDatagram_h

#include <RHDatagram.h>

// The acknowledgement bit in the FLAGS
// The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved
// for application layer use.
#define RH_FLAGS_ACK 0x80

/// 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 <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
    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



================================================
FILE: RHRouter.cpp
================================================
// 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.7 2015/08/13 02:45:47 mikem Exp $

#include <RHRouter.h>

RHRouter::RoutedMessage RHRouter::_tmpMessage;

////////////////////////////////////////////////////////////////////
// Constructors
RHRouter::RHRouter(RHGenericDriver& driver, uint8_t thisAddress) 
    : RHReliableDatagram(driver, thisAddress)
{
    _max_hops = RH_DEFAULT_MAX_HOPS;
    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::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
}

////////////////////////////////////////////////////////////////////
bool RHRouter::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
{  
    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;
	    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?
	    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)
{  
    unsigned long starttime = millis();
    int32_t timeLeft;
    while ((timeLeft = timeout - (millis() - starttime)) > 0)
    {
	if (waitAvailableTimeout(timeLeft))
	{
	    if (recvfromAck(buf, len, source, dest, id, flags))
		return true;
	}
	YIELD;
    }
    return false;
}



================================================
FILE: RHRouter.h
================================================
// RHRouter.h
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2011 Mike McCauley
// $Id: RHRouter.h,v 1.9 2014/08/10 20:55:17 mikem Exp $

#ifndef RHRouter_h
#define RHRouter_h

#include <RHReliableDatagram.h>

// 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
// Duemilanova. Size of 50 works with the sample router programs on Duemilanova.
#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 <RHRouter.h>
/// \brief RHReliableDatagram subclass for sending addressed, optionally acknowledged datagrams
/// multi-hop routed across a network.
///
/// Manager class that extends RHReliableDatagram to define addressed messages
/// That are reliably transmitted and routed across a network. 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 filed in teh header is incremented. If a message is received for routing to another node 
/// which has exceed the routers max_hops, the message wioll 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 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
    /// (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);

    /// 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
    /// (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);

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;

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



================================================
FILE: RHSPIDriver.cpp
================================================
// RHSPIDriver.cpp
//
// Copyright (C) 2014 Mike McCauley
// $Id: RHSPIDriver.cpp,v 1.10 2015/12/16 04:55:33 mikem Exp $

#include <RHSPIDriver.h>

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
    pinMode(_slaveSelectPin, OUTPUT);
    digitalWrite(_slaveSelectPin, HIGH);

    delay(100);
    return true;
}

uint8_t RHSPIDriver::spiRead(uint8_t reg)
{
    uint8_t val;
    RPI_CE0_CE1_FIX;
    ATOMIC_BLOCK_START;
    digitalWrite(_slaveSelectPin, LOW);
    _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
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return val;
}

uint8_t RHSPIDriver::spiWrite(uint8_t reg, uint8_t val)
{
    uint8_t status = 0;
    RPI_CE0_CE1_FIX;
    ATOMIC_BLOCK_START;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the address with the write mask on
    _spi.transfer(val); // New value follows
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return status;
}

uint8_t RHSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len)
{
    uint8_t status = 0;
    RPI_CE0_CE1_FIX;
    ATOMIC_BLOCK_START;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(reg & ~RH_SPI_WRITE_MASK); // Send the start address with the write mask off
    while (len--)
	*dest++ = _spi.transfer(0);
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return status;
}

uint8_t RHSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len)
{
    uint8_t status = 0;
    RPI_CE0_CE1_FIX;
    ATOMIC_BLOCK_START;
    digitalWrite(_slaveSelectPin, LOW);
    status = _spi.transfer(reg | RH_SPI_WRITE_MASK); // Send the start address with the write mask on
    while (len--)
	_spi.transfer(*src++);
    digitalWrite(_slaveSelectPin, HIGH);
    ATOMIC_BLOCK_END;
    return status;
}

void RHSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin)
{
    _slaveSelectPin = slaveSelectPin;
}
 

================================================
FILE: RHSPIDriver.h
================================================
// RHSPIDriver.h
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2014 Mike McCauley
// $Id: RHSPIDriver.h,v 1.10 2015/12/16 04:55:33 mikem Exp $

#ifndef RHSPIDriver_h
#define RHSPIDriver_h

#include <RHGenericDriver.h>
#include <RHHardwareSPI.h>

// This is the bit in the SPI address that marks it as a write
#define RH_SPI_WRITE_MASK 0x80

#if (RH_PLATFORM == RH_PLATFORM_RASPI)
#define RPI_CE0_CE1_FIX { \
          if (_slaveSelectPin!=7) {   \
            bcm2835_gpio_fsel(7,BCM2835_GPIO_FSEL_OUTP); \
            bcm2835_gpio_write(7,HIGH); \
          }                           \
          if (_slaveSelectPin!=8) {   \
            bcm2835_gpio_fsel(8,BCM2835_GPIO_FSEL_OUTP); \
            bcm2835_gpio_write(8,HIGH); \
          }                           \
        }
#else
#define RPI_CE0_CE1_FIX {}
#endif


class RHGenericSPI;

/////////////////////////////////////////////////////////////////////
/// \class RHSPIDriver RHSPIDriver.h <RHSPIDriver.h>
/// \brief Base class for a 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);

protected:
    /// Reference to the RHGenericSPI instance to use to transfer 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


================================================
FILE: RHSoftwareSPI.cpp
================================================
// SoftwareSPI.cpp
// Author: Chris Lapa (chris@lapa.com.au)
// Copyright (C) 2014 Chris Lapa
// Contributed by Chris Lapa

#include <RHSoftwareSPI.h>

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");
    }
}



================================================
FILE: RHSoftwareSPI.h
================================================
// 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 <RHGenericSPI.h>

/////////////////////////////////////////////////////////////////////
/// \class RHSoftwareSPI RHSoftwareSPI.h <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.
///
/// \par Usage
///
/// Usage varies slightly depending on what driver you are using.
///
/// For RF22, for example:
/// \code
/// #include <RHSoftwareSPI.h>
/// 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 _bitOrder;
    uint8_t _delayCounts;
    uint8_t _clockPolarity;
    uint8_t _clockPhase;
};

#endif


================================================
FILE: RHTcpProtocol.h
================================================
// 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


================================================
FILE: RH_ASK.cpp
================================================
// RH_ASK.cpp
//
// Copyright (C) 2014 Mike McCauley
// $Id: RH_ASK.cpp,v 1.20 2017/01/12 23:58:00 mikem Exp $

#include <RH_ASK.h>
#include <RHCRC.h>

#if (RH_PLATFORM == RH_PLATFORM_STM32) // Maple etc
HardwareTimer timer(MAPLE_TIMER);

#endif

#if (RH_PLATFORM == RH_PLATFORM_ESP8266)
    // interrupt handler and related code must be in RAM on ESP8266,
    // according to issue #46.
    #define INTERRUPT_ATTR ICACHE_RAM_ATTR
#else
    #define INTERRUPT_ATTR
#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),
    _pttInverted(pttInverted),
    _rxInverted(false)
{
    // 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<<RH_ASK_PTT_PIN); 
    RH_ASK_TX_DDR   |=  (1<<RH_ASK_TX_PIN);
    RH_ASK_RX_DDR   &= ~(1<<RH_ASK_RX_PIN);
 #else
    RH_ASK_TX_DDR   |=  (1<<RH_ASK_TX_PIN);
    RH_ASK_RX_DDR   &= ~(1<<RH_ASK_RX_PIN);
 #endif
#else
    // Set up digital IO pins for arduino
    pinMode(_txPin, OUTPUT);
    pinMode(_rxPin, INPUT);
    pinMode(_pttPin, OUTPUT);
#endif

    // Ready to go
    setModeIdle();
    timerSetup();

    return true;
}

// Put these prescaler structs in PROGMEM, not on the stack
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
 #if defined(RH_ASK_ARDUINO_USE_TIMER2)
 // Timer 2 has different prescalers
 PROGMEM static const uint16_t prescalers[] = {0, 1, 8, 32, 64, 128, 256, 3333}; 
 #else
 PROGMEM static const uint16_t prescalers[] = {0, 1, 8, 64, 256, 1024, 3333}; 
 #endif
 #define NUM_PRESCALERS (sizeof(prescalers) / sizeof( uint16_t))
#endif

// Common function for setting timer ticks @ prescaler values for speed
// Returns prescaler index into {0, 1, 8, 64, 256, 1024} array
// and sets nticks to compare-match value if lower than max_ticks
// returns 0 & nticks = 0 on fault
uint8_t RH_ASK::timerCalc(uint16_t speed, uint16_t max_ticks, uint16_t *nticks)
{
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
    // Clock divider (prescaler) values - 0/3333: error flag
    uint8_t prescaler;     // index into array & return bit value
    unsigned long ulticks; // calculate by ntick overflow

    // Div-by-zero protection
    if (speed == 0)
    {
        // signal fault
        *nticks = 0;
        return 0;
    }

    // test increasing prescaler (divisor), decreasing ulticks until no overflow
    // 1/Fraction of second needed to xmit one bit
    unsigned long inv_bit_time = ((unsigned long)speed) * 8;
    for (prescaler=1; prescaler < NUM_PRESCALERS; prescaler += 1)
    {
	// Integer arithmetic courtesy Jim Remington
	// 1/Amount of time per CPU clock tick (in seconds)
	uint16_t prescalerValue;
	memcpy_P(&prescalerValue, &prescalers[prescaler], sizeof(uint16_t));
        unsigned long inv_clock_time = F_CPU / ((unsigned long)prescalerValue);
        // number of prescaled ticks needed to handle bit time @ speed
        ulticks = inv_clock_time / inv_bit_time;

        // Test if ulticks fits in nticks bitwidth (with 1-tick safety margin)
        if ((ulticks > 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_ARDUINO) // Arduino specific
    uint16_t nticks; // number of prescaled ticks needed
    uint8_t prescaler; // Bit values for CS0[2:0]

 #ifdef 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
    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 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_STM32) // Maple etc
    // Pause the timer while we're configuring it
    timer.pause();
    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
    void interrupt(); // defined below
    timer.attachCompare1Interrupt(interrupt);
    
    // Refresh the timer's count, prescale, and overflow
    timer.refresh();
    
    // Start the timer counting
    timer.resume();

#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 INTERRUPT_ATTR esp8266_timer_interrupt_handler(); // Forward declarat
    // 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);
#endif

}

void INTERRUPT_ATTR RH_ASK::setModeIdle()
{
    if (_mode != RHModeIdle)
    {
	// Disable the transmitter hardware
	writePtt(LOW);
	writeTx(LOW);
	_mode = RHModeIdle;
    }
}

void 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_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 INTERRUPT_ATTR RH_ASK::readRx()
{
    bool value;
#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
    value = ((RH_ASK_RX_PORT & (1<<RH_ASK_RX_PIN)) ? 1 : 0);
#else
    value = digitalRead(_rxPin);
#endif
    return value ^ _rxInverted;
}

// Write the TX output pin, taking into account platform type.
void INTERRUPT_ATTR RH_ASK::writeTx(bool value)
{
#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
    ((value) ? (RH_ASK_TX_PORT |= (1<<RH_ASK_TX_PIN)) : (RH_ASK_TX_PORT &= ~(1<<RH_ASK_TX_PIN)));
#else
    digitalWrite(_txPin, value);
#endif
}

// Write the PTT output pin, taking into account platform type and inversion.
void INTERRUPT_ATTR RH_ASK::writePtt(bool value)
{
#if (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
 #if RH_ASK_PTT_PIN 
    ((value) ? (RH_ASK_PTT_PORT |= (1<<RH_ASK_PTT_PIN)) : (RH_ASK_PTT_PORT &= ~(1<<RH_ASK_PTT_PIN)));
 #else
    ((value) ? (RH_ASK_TX_PORT |= (1<<RH_ASK_TX_PIN)) : (RH_ASK_TX_PORT &= ~(1<<RH_ASK_TX_PIN)));
 #endif
#else
    digitalWrite(_pttPin, value ^ _pttInverted);
#endif
}

uint8_t RH_ASK::maxMessageLength()
{
    return RH_ASK_MAX_MESSAGE_LEN;
}

#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) 
 #if defined(RH_PLATFORM_ATTINY)
  #define RH_ASK_TIMER_VECTOR TIM0_COMPA_vect
 #else // Assume Arduino Uno (328p or similar)
  #if defined(RH_ASK_ARDUINO_USE_TIMER2)
   #define RH_ASK_TIMER_VECTOR TIMER2_COMPA_vect
  #else
   #define RH_ASK_TIMER_VECTOR TIMER1_COMPA_vect
  #endif
 #endif 
#elif (RH_ASK_PLATFORM == RH_ASK_PLATFORM_GENERIC_AVR8)
 #define __COMB(a,b,c) (a##b##c)
 #define _COMB(a,b,c) __COMB(a,b,c)
 #define RH_ASK_TIMER_VECTOR _COMB(TIMER,RH_ASK_TIMER_INDEX,_COMPA_vect)
#endif

#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(CORE_TEENSY)	
void TIMER1_COMPA_vect(void)
{
    thisASKDriver->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) || (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 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();
}

#endif

// Convert a 6 bit encoded symbol into its 4 bit decoded equivalent
uint8_t 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 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 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 INTERRUPT_ATTR RH_ASK::handleTimerInterrupt()
{
    if (_mode == RHModeRx)
	receiveTimer(); // Receiving
    else if (_mode == RHModeTx)
        transmitTimer(); // Transmitting
}



================================================
FILE: RH_ASK.h
================================================
// RH_ASK.h
//
// Copyright (C) 2014 Mike McCauley
// $Id: RH_ASK.h,v 1.16 2016/07/07 00:02:53 mikem Exp $

#ifndef RH_ASK_h
#define RH_ASK_h

#include <RHGenericDriver.h>

// 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 <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
/// - Transmitters: 
///  - TX-C1 (433.92MHz)
///  - RFM85 from HopeRF http://www.hoperfusa.com/details.jsp?pid=127
/// - 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
Download .txt
gitextract_jb8sxj2z/

├── .gitattributes
├── .gitignore
├── LICENSE
├── MANIFEST
├── README.md
├── RHCRC.cpp
├── RHCRC.h
├── RHDatagram.cpp
├── RHDatagram.h
├── RHGenericDriver.cpp
├── RHGenericDriver.h
├── RHGenericSPI.cpp
├── RHGenericSPI.h
├── RHHardwareSPI.cpp
├── RHHardwareSPI.h
├── RHMesh.cpp
├── RHMesh.h
├── RHNRFSPIDriver.cpp
├── RHNRFSPIDriver.h
├── RHReliableDatagram.cpp
├── RHReliableDatagram.h
├── RHRouter.cpp
├── RHRouter.h
├── RHSPIDriver.cpp
├── RHSPIDriver.h
├── RHSoftwareSPI.cpp
├── RHSoftwareSPI.h
├── RHTcpProtocol.h
├── RH_ASK.cpp
├── RH_ASK.h
├── RH_CC110.cpp
├── RH_CC110.h
├── RH_MRF89.cpp
├── RH_MRF89.h
├── RH_NRF24.cpp
├── RH_NRF24.h
├── RH_NRF51.cpp
├── RH_NRF51.h
├── RH_NRF905.cpp
├── RH_NRF905.h
├── RH_RF22.cpp
├── RH_RF22.h
├── RH_RF24.cpp
├── RH_RF24.h
├── RH_RF69.cpp
├── RH_RF69.h
├── RH_RF95.cpp
├── RH_RF95.h
├── RH_Serial.cpp
├── RH_Serial.h
├── RH_TCP.cpp
├── RH_TCP.h
├── RHutil/
│   ├── HardwareSerial.cpp
│   ├── HardwareSerial.h
│   ├── RasPi.cpp
│   ├── RasPi.h
│   ├── atomic.h
│   └── simulator.h
├── RadioHead.h
├── STM32ArduinoCompat/
│   ├── HardwareSPI.cpp
│   ├── HardwareSPI.h
│   ├── HardwareSerial.cpp
│   ├── HardwareSerial.h
│   ├── README
│   ├── wirish.cpp
│   └── wirish.h
├── examples/
│   ├── ask/
│   │   ├── ask_receiver/
│   │   │   └── ask_receiver.pde
│   │   ├── ask_reliable_datagram_client/
│   │   │   └── ask_reliable_datagram_client.pde
│   │   ├── ask_reliable_datagram_server/
│   │   │   └── ask_reliable_datagram_server.pde
│   │   └── ask_transmitter/
│   │       └── ask_transmitter.pde
│   ├── cc110/
│   │   ├── cc110_client/
│   │   │   └── cc110_client.pde
│   │   └── cc110_server/
│   │       └── cc110_server.pde
│   ├── mrf89/
│   │   ├── mrf89_client/
│   │   │   └── mrf89_client.pde
│   │   └── mrf89_server/
│   │       └── mrf89_server.pde
│   ├── nrf24/
│   │   ├── nrf24_client/
│   │   │   └── nrf24_client.pde
│   │   ├── nrf24_reliable_datagram_client/
│   │   │   └── nrf24_reliable_datagram_client.pde
│   │   ├── nrf24_reliable_datagram_server/
│   │   │   └── nrf24_reliable_datagram_server.pde
│   │   └── nrf24_server/
│   │       └── nrf24_server.pde
│   ├── nrf51/
│   │   ├── nrf51_audio_rx/
│   │   │   └── nrf51_audio_rx.pde
│   │   ├── nrf51_audio_tx/
│   │   │   └── nrf51_audio_tx.pde
│   │   ├── nrf51_client/
│   │   │   └── nrf51_client.pde
│   │   ├── nrf51_reliable_datagram_client/
│   │   │   └── nrf51_reliable_datagram_client.pde
│   │   ├── nrf51_reliable_datagram_server/
│   │   │   └── nrf51_reliable_datagram_server.pde
│   │   └── nrf51_server/
│   │       └── nrf51_server.pde
│   ├── nrf905/
│   │   ├── nrf905_client/
│   │   │   └── nrf905_client.pde
│   │   ├── nrf905_reliable_datagram_client/
│   │   │   └── nrf905_reliable_datagram_client.pde
│   │   ├── nrf905_reliable_datagram_server/
│   │   │   └── nrf905_reliable_datagram_server.pde
│   │   └── nrf905_server/
│   │       └── nrf905_server.pde
│   ├── raspi/
│   │   ├── RasPiBoards.h
│   │   ├── irq_test/
│   │   │   ├── Makefile
│   │   │   └── irq_test.c
│   │   ├── multi_server/
│   │   │   ├── Makefile
│   │   │   └── multi_server.cpp
│   │   ├── nrf24/
│   │   │   ├── Makefile
│   │   │   └── RasPiRH.cpp
│   │   ├── rf69/
│   │   │   ├── Makefile
│   │   │   ├── rf69_client.cpp
│   │   │   └── rf69_server.cpp
│   │   ├── rf95/
│   │   │   ├── Makefile
│   │   │   ├── rf95_client.cpp
│   │   │   └── rf95_server.cpp
│   │   └── spi_scan/
│   │       ├── Makefile
│   │       └── spi_scan.c
│   ├── rf22/
│   │   ├── rf22_client/
│   │   │   └── rf22_client.pde
│   │   ├── rf22_mesh_client/
│   │   │   └── rf22_mesh_client.pde
│   │   ├── rf22_mesh_server1/
│   │   │   └── rf22_mesh_server1.pde
│   │   ├── rf22_mesh_server2/
│   │   │   └── rf22_mesh_server2.pde
│   │   ├── rf22_mesh_server3/
│   │   │   └── rf22_mesh_server3.pde
│   │   ├── rf22_reliable_datagram_client/
│   │   │   └── rf22_reliable_datagram_client.pde
│   │   ├── rf22_reliable_datagram_server/
│   │   │   └── rf22_reliable_datagram_server.pde
│   │   ├── rf22_router_client/
│   │   │   └── rf22_router_client.pde
│   │   ├── rf22_router_server1/
│   │   │   └── rf22_router_server1.pde
│   │   ├── rf22_router_server2/
│   │   │   └── rf22_router_server2.pde
│   │   ├── rf22_router_server3/
│   │   │   └── rf22_router_server3.pde
│   │   ├── rf22_router_test/
│   │   │   └── rf22_router_test.pde
│   │   └── rf22_server/
│   │       └── rf22_server.pde
│   ├── rf24/
│   │   ├── rf24_client/
│   │   │   └── rf24_client.pde
│   │   ├── rf24_reliable_datagram_client/
│   │   │   └── rf24_reliable_datagram_client.pde
│   │   ├── rf24_reliable_datagram_server/
│   │   │   └── rf24_reliable_datagram_server.pde
│   │   └── rf24_server/
│   │       └── rf24_server.pde
│   ├── rf69/
│   │   ├── rf69_client/
│   │   │   └── rf69_client.pde
│   │   ├── rf69_reliable_datagram_client/
│   │   │   └── rf69_reliable_datagram_client.pde
│   │   ├── rf69_reliable_datagram_server/
│   │   │   └── rf69_reliable_datagram_server.pde
│   │   └── rf69_server/
│   │       └── rf69_server.pde
│   ├── rf95/
│   │   ├── rf95_client/
│   │   │   └── rf95_client.pde
│   │   ├── rf95_reliable_datagram_client/
│   │   │   └── rf95_reliable_datagram_client.pde
│   │   ├── rf95_reliable_datagram_server/
│   │   │   └── rf95_reliable_datagram_server.pde
│   │   └── rf95_server/
│   │       └── rf95_server.pde
│   ├── serial/
│   │   ├── serial_reliable_datagram_client/
│   │   │   └── serial_reliable_datagram_client.pde
│   │   └── serial_reliable_datagram_server/
│   │       └── serial_reliable_datagram_server.pde
│   └── simulator/
│       ├── simulator_reliable_datagram_client/
│       │   └── simulator_reliable_datagram_client.pde
│       └── simulator_reliable_datagram_server/
│           └── simulator_reliable_datagram_server.pde
├── project.cfg
├── radio_config_Si4460.h
└── tools/
    ├── chain.conf
    ├── etherSimulator.pl
    ├── simBuild
    └── simMain.cpp
Download .txt
SYMBOL INDEX (130 symbols across 48 files)

FILE: RHCRC.cpp
  function RHcrc16_update (line 48) | uint16_t RHcrc16_update(uint16_t crc, uint8_t a)
  function RHcrc_xmodem_update (line 63) | uint16_t RHcrc_xmodem_update (uint16_t crc, uint8_t data)
  function RHcrc_ccitt_update (line 79) | uint16_t RHcrc_ccitt_update (uint16_t crc, uint8_t data)
  function RHcrc_ibutton_update (line 88) | uint8_t RHcrc_ibutton_update(uint8_t crc, uint8_t data)

FILE: RHDatagram.h
  function class (line 45) | class RHDatagram

FILE: RHGenericDriver.cpp
  function __cxa_pure_virtual (line 214) | void __cxa_pure_virtual()

FILE: RHGenericDriver.h
  function class (line 41) | class RHGenericDriver

FILE: RHGenericSPI.h
  type Frequency (line 55) | typedef enum
  type BitOrder (line 70) | typedef enum
  function virtual (line 95) | virtual void attachInterrupt() {}
  function virtual (line 99) | virtual void detachInterrupt() {}

FILE: RHHardwareSPI.h
  function class (line 18) | class RHHardwareSPI : public RHGenericSPI

FILE: RHMesh.h
  function class (line 115) | class RHMesh : public RHRouter

FILE: RHNRFSPIDriver.h
  function class (line 33) | class RHNRFSPIDriver : public RHGenericDriver

FILE: RHReliableDatagram.h
  function class (line 78) | class RHReliableDatagram : public RHDatagram

FILE: RHRouter.h
  function class (line 132) | class RHRouter : public RHReliableDatagram

FILE: RHSPIDriver.h
  function class (line 54) | class RHSPIDriver : public RHGenericDriver

FILE: RHSoftwareSPI.h
  function class (line 35) | class RHSoftwareSPI : public RHGenericSPI

FILE: RHTcpProtocol.h
  type RHTcpMessage (line 30) | typedef struct
  type RHTcpTypeMessage (line 37) | typedef struct
  type RHTcpThisAddress (line 45) | typedef struct
  type RHTcpPacket (line 53) | typedef struct

FILE: RH_ASK.cpp
  function TIMER1_COMPA_vect (line 592) | void TIMER1_COMPA_vect(void)
  function TC3_Handler (line 599) | void TC3_Handler()
  function TC1_Handler (line 609) | void TC1_Handler()
  function ISR (line 620) | ISR(RH_ASK_TIMER_VECTOR)
  function interrupt (line 627) | void interrupt()
  function TimerInterruptHandler (line 633) | void TimerInterruptHandler()
  function Timer_A_int (line 639) | Timer_A_int(void)
  function chipkit_timer_interrupt_handler (line 646) | uint32_t chipkit_timer_interrupt_handler(uint32_t currentTime)
  function timerInterrupt (line 656) | timerInterrupt(void)

FILE: RH_ASK.h
  function class (line 241) | class RH_ASK : public RHGenericDriver

FILE: RH_CC110.h
  function class (line 592) | class RH_CC110 : public RHNRFSPIDriver

FILE: RH_MRF89.h
  function class (line 377) | class RH_MRF89 : public RHNRFSPIDriver

FILE: RH_NRF24.h
  function class (line 417) | class RH_NRF24 : public RHNRFSPIDriver

FILE: RH_NRF51.h
  function class (line 126) | class RH_NRF51 : public RHGenericDriver

FILE: RH_NRF905.h
  function class (line 234) | class RH_NRF905 : public RHNRFSPIDriver

FILE: RH_RF22.h
  function class (line 849) | class RH_RF22 : public RHSPIDriver

FILE: RH_RF24.h
  function class (line 647) | class RH_RF24 : public RHSPIDriver

FILE: RH_RF69.h
  function class (line 664) | class RH_RF69 : public RHSPIDriver

FILE: RH_RF95.h
  function class (line 549) | class RH_RF95 : public RHSPIDriver

FILE: RH_Serial.cpp
  function HardwareSerial (line 20) | HardwareSerial& RH_Serial::serial()

FILE: RH_Serial.h
  function class (line 141) | class RH_Serial : public RHGenericDriver

FILE: RH_TCP.cpp
  type addrinfo (line 39) | struct addrinfo
  type addrinfo (line 40) | struct addrinfo
  type sockaddr_storage (line 42) | struct sockaddr_storage
  type addrinfo (line 45) | struct addrinfo
  type timeval (line 225) | struct timeval

FILE: RH_TCP.h
  function class (line 67) | class RH_TCP : public RHGenericDriver

FILE: RHutil/HardwareSerial.cpp
  type termios (line 172) | struct termios
  type timeval (line 231) | struct timeval

FILE: RHutil/HardwareSerial.h
  function class (line 29) | class HardwareSerial

FILE: RHutil/RasPi.cpp
  function byte (line 68) | byte SPIClass::transfer(byte _data)
  function pinMode (line 81) | void pinMode(unsigned char pin, unsigned char mode)
  function digitalWrite (line 96) | void digitalWrite(unsigned char pin, unsigned char value)
  function digitalRead (line 104) | unsigned char digitalRead(unsigned char pin) {
  function millis (line 111) | unsigned long millis()
  function delay (line 124) | void delay (unsigned long ms)
  function random (line 133) | long random(long min, long max)
  function printbuffer (line 142) | void printbuffer(uint8_t buff[], int len)

FILE: RHutil/RasPi.h
  type byte (line 17) | typedef unsigned char byte;
  function class (line 40) | class SPIClass
  function class (line 55) | class SerialSimulator

FILE: RHutil/atomic.h
  function __get_primask (line 22) | static __inline__ uint32_t __get_primask(void) \
  function __set_primask (line 27) | static __inline__ void __set_primask(uint32_t setval) \
  function __iSeiRetVal (line 31) | static __inline__ uint32_t __iSeiRetVal(void) \
  function __iCliRetVal (line 35) | static __inline__ uint32_t __iCliRetVal(void) \
  function __iSeiParam (line 39) | static __inline__ void    __iSeiParam(const uint32_t *__s) \
  function __iCliParam (line 43) | static __inline__ void    __iCliParam(const uint32_t *__s) \
  function __iRestore (line 47) | static __inline__ void    __iRestore(const  uint32_t *__s) \

FILE: RHutil/simulator.h
  function class (line 29) | class SerialSimulator

FILE: STM32ArduinoCompat/HardwareSPI.h
  type SPIFrequency (line 9) | typedef enum SPIFrequency {
  function class (line 24) | class HardwareSPI

FILE: STM32ArduinoCompat/HardwareSerial.cpp
  function USART1_IRQHandler (line 252) | void USART1_IRQHandler(void)
  function USART2_IRQHandler (line 267) | void USART2_IRQHandler(void)
  function USART3_IRQHandler (line 283) | void USART3_IRQHandler(void)
  function UART4_IRQHandler (line 299) | void UART4_IRQHandler(void)
  function UART5_IRQHandler (line 315) | void UART5_IRQHandler(void)
  function USART6_IRQHandler (line 331) | void USART6_IRQHandler(void)

FILE: STM32ArduinoCompat/HardwareSerial.h
  function class (line 17) | class RingBuffer
  function class (line 36) | class HardwareSerial

FILE: STM32ArduinoCompat/wirish.cpp
  function SysTickConfig (line 153) | void SysTickConfig()
  function SysTick_Handler (line 171) | void SysTick_Handler(void)
  function EXTI0_IRQHandler (line 177) | void EXTI0_IRQHandler(void)
  function EXTI1_IRQHandler (line 186) | void EXTI1_IRQHandler(void)
  function EXTI2_IRQHandler (line 195) | void EXTI2_IRQHandler(void)
  function EXTI3_IRQHandler (line 204) | void EXTI3_IRQHandler(void)
  function EXTI4_IRQHandler (line 213) | void EXTI4_IRQHandler(void)
  function EXTI9_5_IRQHandler (line 222) | void EXTI9_5_IRQHandler(void)
  function EXTI15_10_IRQHandler (line 255) | void EXTI15_10_IRQHandler(void)
  function main (line 300) | int main(int argc, char** argv)
  function pinMode (line 310) | void pinMode(uint8_t pin, WiringPinMode mode)
  function digitalWrite (line 330) | void digitalWrite(uint8_t pin, uint8_t val)
  function digitalRead (line 340) | uint8_t digitalRead(uint8_t pin)
  function attachInterrupt (line 347) | void attachInterrupt(uint8_t pin, void (*handler)(void), int mode)
  function delay (line 383) | void delay(unsigned long ms)
  function millis (line 391) | unsigned long millis()
  function random (line 396) | long random(long from, long to)
  function random (line 401) | long random(long to)
  function _init (line 409) | void _init() {}
  function _fini (line 410) | void _fini() {}

FILE: STM32ArduinoCompat/wirish.h
  type WiringPinMode (line 15) | typedef enum WiringPinMode {
  function class (line 97) | class SerialUSBClass

FILE: examples/raspi/irq_test/irq_test.c
  function main (line 20) | int main(int argc, char **argv)

FILE: examples/raspi/multi_server/multi_server.cpp
  type Mod_idx (line 48) | enum Mod_idx { IDX_MOD1 = 0, IDX_MOD2 = 1, IDX_MOD3 = 2 }
  function sig_handler (line 86) | void sig_handler(int sig)
  function getReceivedData (line 99) | void getReceivedData( uint8_t index)
  function initRadioModule (line 140) | bool initRadioModule( uint8_t index) {
  function main (line 247) | int main (int argc, const char* argv[] )

FILE: examples/raspi/nrf24/RasPiRH.cpp
  function main (line 42) | int main (int argc, const char* argv[] )
  function sig_handler (line 127) | void sig_handler(int sig)
  function printbuffer (line 132) | void printbuffer(uint8_t buff[], int len)

FILE: examples/raspi/rf69/rf69_client.cpp
  function sig_handler (line 58) | void sig_handler(int sig)
  function main (line 65) | int main (int argc, const char* argv[] )

FILE: examples/raspi/rf69/rf69_server.cpp
  function sig_handler (line 58) | void sig_handler(int sig)
  function main (line 65) | int main (int argc, const char* argv[] )

FILE: examples/raspi/rf95/rf95_client.cpp
  function sig_handler (line 58) | void sig_handler(int sig)
  function main (line 65) | int main (int argc, const char* argv[] )

FILE: examples/raspi/rf95/rf95_server.cpp
  function sig_handler (line 57) | void sig_handler(int sig)
  function main (line 64) | int main (int argc, const char* argv[] )

FILE: examples/raspi/spi_scan/spi_scan.c
  function readRegister (line 27) | uint8_t readRegister(uint8_t cs_pin, uint8_t addr)
  function getModuleName (line 39) | void getModuleName(uint8_t version)
  function readModuleVersion (line 57) | void readModuleVersion(uint8_t cs_pin)
  function main (line 70) | int main(int argc, char **argv)

FILE: tools/simMain.cpp
  function time_in_millis (line 28) | unsigned long time_in_millis()
  function main (line 37) | int main(int argc, char** argv)
  function delay (line 50) | void delay(unsigned long ms)
  function millis (line 56) | unsigned long millis()
  function random (line 61) | long random(long from, long to)
  function random (line 66) | long random(long to)
Condensed preview — 138 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,225K chars).
[
  {
    "path": ".gitattributes",
    "chars": 378,
    "preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n\n# St"
  },
  {
    "path": ".gitignore",
    "chars": 2664,
    "preview": "\n*.o\n\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.n"
  },
  {
    "path": "LICENSE",
    "chars": 841,
    "preview": "This software is Copyright (C) 2008 Mike McCauley. Use is subject to license\nconditions. The main licensing options avai"
  },
  {
    "path": "MANIFEST",
    "chars": 5566,
    "preview": "RadioHead/LICENSE\nRadioHead/MANIFEST\nRadioHead/project.cfg\nRadioHead/RadioHead.h\nRadioHead/RH_ASK.cpp\nRadioHead/RH_ASK.h"
  },
  {
    "path": "README.md",
    "chars": 8606,
    "preview": "RadioHead Packet Radio library for embedded microprocessors\n===========================================================\n"
  },
  {
    "path": "RHCRC.cpp",
    "chars": 2937,
    "preview": "/* Copyright (c) 2002, 2003, 2004  Marek Michalkiewicz\n   Copyright (c) 2005, 2007 Joerg Wunsch\n   All rights reserved.\n"
  },
  {
    "path": "RHCRC.h",
    "chars": 549,
    "preview": "// RHCRC.h\n//\n// Definitions for RadioHead compatible CRC outines.\n//\n// These routines originally derived from Arduino "
  },
  {
    "path": "RHDatagram.cpp",
    "chars": 2265,
    "preview": "// RHDatagram.cpp\n//\n// Copyright (C) 2011 Mike McCauley\n// $Id: RHDatagram.cpp,v 1.6 2014/05/23 02:20:17 mikem Exp $\n\n#"
  },
  {
    "path": "RHDatagram.h",
    "chars": 7590,
    "preview": "// RHDatagram.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// $Id: RHDatagram.h,"
  },
  {
    "path": "RHGenericDriver.cpp",
    "chars": 4137,
    "preview": "// RHGenericDriver.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHGenericDriver.cpp,v 1.20 2017/01/12 23:58:00 mik"
  },
  {
    "path": "RHGenericDriver.h",
    "chars": 15008,
    "preview": "// RHGenericDriver.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHGeneri"
  },
  {
    "path": "RHGenericSPI.cpp",
    "chars": 651,
    "preview": "// RHGenericSPI.cpp\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// Contributed by"
  },
  {
    "path": "RHGenericSPI.h",
    "chars": 5922,
    "preview": "// RHGenericSPI.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// Contributed by J"
  },
  {
    "path": "RHHardwareSPI.cpp",
    "chars": 9310,
    "preview": "// RHHardwareSPI.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// Contributed by "
  },
  {
    "path": "RHHardwareSPI.h",
    "chars": 2651,
    "preview": "// RHHardwareSPI.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// Contributed by "
  },
  {
    "path": "RHMesh.cpp",
    "chars": 9014,
    "preview": "// RHMesh.cpp\n//\n// Define addressed datagram\n// \n// Part of the Arduino RH library for operating with HopeRF RH compati"
  },
  {
    "path": "RHMesh.h",
    "chars": 15289,
    "preview": "// RHMesh.h\n//\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// $Id: RHMesh.h,v 1.1"
  },
  {
    "path": "RHNRFSPIDriver.cpp",
    "chars": 2860,
    "preview": "// RHNRFSPIDriver.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHNRFSPIDriver.cpp,v 1.3 2015/12/16 04:55:33 mikem "
  },
  {
    "path": "RHNRFSPIDriver.h",
    "chars": 4598,
    "preview": "// RHNRFSPIDriver.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHNRFSPID"
  },
  {
    "path": "RHReliableDatagram.cpp",
    "chars": 5588,
    "preview": "// RHReliableDatagram.cpp\n//\n// Define addressed datagram\n// \n// Part of the Arduino RH library for operating with HopeR"
  },
  {
    "path": "RHReliableDatagram.h",
    "chars": 10890,
    "preview": "// RHReliableDatagram.h\n//\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH"
  },
  {
    "path": "RHRouter.cpp",
    "chars": 9081,
    "preview": "// RHRouter.cpp\n//\n// Define addressed datagram\n// \n// Part of the Arduino RH library for operating with HopeRF RH compa"
  },
  {
    "path": "RHRouter.h",
    "chars": 17593,
    "preview": "// RHRouter.h\n//\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// $Id: RHRouter.h,v"
  },
  {
    "path": "RHSPIDriver.cpp",
    "chars": 2400,
    "preview": "// RHSPIDriver.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHSPIDriver.cpp,v 1.10 2015/12/16 04:55:33 mikem Exp $"
  },
  {
    "path": "RHSPIDriver.h",
    "chars": 5047,
    "preview": "// RHSPIDriver.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RHSPIDriver."
  },
  {
    "path": "RHSoftwareSPI.cpp",
    "chars": 3334,
    "preview": "// SoftwareSPI.cpp\n// Author: Chris Lapa (chris@lapa.com.au)\n// Copyright (C) 2014 Chris Lapa\n// Contributed by Chris La"
  },
  {
    "path": "RHSoftwareSPI.h",
    "chars": 3193,
    "preview": "// SoftwareSPI.h\n// Author: Chris Lapa (chris@lapa.com.au)\n// Copyright (C) 2014 Chris Lapa\n// Contributed by Chris Lapa"
  },
  {
    "path": "RHTcpProtocol.h",
    "chars": 2480,
    "preview": "// RH_TcpProtocol.h\n// Author: Mike McCauley (mikem@aierspayce.com)\n// Definition of protocol messages sent and received"
  },
  {
    "path": "RH_ASK.cpp",
    "chars": 26538,
    "preview": "// RH_ASK.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_ASK.cpp,v 1.20 2017/01/12 23:58:00 mikem Exp $\n\n#include"
  },
  {
    "path": "RH_ASK.h",
    "chars": 19287,
    "preview": "// RH_ASK.h\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_ASK.h,v 1.16 2016/07/07 00:02:53 mikem Exp $\n\n#ifndef RH_A"
  },
  {
    "path": "RH_CC110.cpp",
    "chars": 16586,
    "preview": "// RH_CC110.cpp\n//\n// Driver for Texas Instruments CC110L transceiver.\n//\n// Copyright (C) 2016 Mike McCauley\n// $Id: RH"
  },
  {
    "path": "RH_CC110.h",
    "chars": 44221,
    "preview": "// RH_CC110.h\n//\n// Definitions for Texas Instruments CC110L transceiver.\n// http://www.ti.com/lit/ds/symlink/cc110l.pdf"
  },
  {
    "path": "RH_MRF89.cpp",
    "chars": 18743,
    "preview": "// RH_MRF89.cpp\n//\n// Copyright (C) 2015 Mike McCauley\n// $Id: RH_MRF89.cpp,v 1.8 2017/01/12 23:58:00 mikem Exp $\n\n#incl"
  },
  {
    "path": "RH_MRF89.h",
    "chars": 28109,
    "preview": "// RH_MRF89.h\n//\n// Definitions for Microchip MRF89XA family radios radios per:\n// http://ww1.microchip.com/downloads/en"
  },
  {
    "path": "RH_NRF24.cpp",
    "chars": 9556,
    "preview": "// NRF24.cpp\n//\n// Copyright (C) 2012 Mike McCauley\n// $Id: RH_NRF24.cpp,v 1.23 2017/01/12 23:58:00 mikem Exp $\n\n#includ"
  },
  {
    "path": "RH_NRF24.h",
    "chars": 30769,
    "preview": "// RH_NRF24.h\n// Author: Mike McCauley\n// Copyright (C) 2012 Mike McCauley\n// $Id: RH_NRF24.h,v 1.19 2016/07/07 00:02:53"
  },
  {
    "path": "RH_NRF51.cpp",
    "chars": 11496,
    "preview": "// NRF51.cpp\n//\n// Per: nRF51_Series_Reference_manual v3.0.pdf\n// Copyright (C) 2012 Mike McCauley\n// $Id: RH_NRF51.cpp,"
  },
  {
    "path": "RH_NRF51.h",
    "chars": 13147,
    "preview": "// RH_NRF51.h\n// Author: Mike McCauley\n// Copyright (C) 2015 Mike McCauley\n// $Id: RH_NRF51.h,v 1.4 2017/01/12 23:58:00 "
  },
  {
    "path": "RH_NRF905.cpp",
    "chars": 6773,
    "preview": "// RH_NRF905.cpp\n//\n// Copyright (C) 2012 Mike McCauley\n// $Id: RH_NRF905.cpp,v 1.7 2017/01/12 23:58:00 mikem Exp $\n\n#in"
  },
  {
    "path": "RH_NRF905.h",
    "chars": 19266,
    "preview": "// RH_NRF905.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_NRF905.h,v "
  },
  {
    "path": "RH_RF22.cpp",
    "chars": 25003,
    "preview": "// RH_RF22.cpp\n//\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH_RF22.cpp,v 1.27 2017/01/12 23:58:00 mikem Exp $\n\n#inclu"
  },
  {
    "path": "RH_RF22.h",
    "chars": 69296,
    "preview": "// RH_RF22.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH_RF22.h,v 1.31"
  },
  {
    "path": "RH_RF24.cpp",
    "chars": 45658,
    "preview": "// RH_RF24.cpp\n//\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH_RF24.cpp,v 1.18 2017/01/12 23:58:00 mikem Exp $\n\n#inclu"
  },
  {
    "path": "RH_RF24.h",
    "chars": 62075,
    "preview": "// RH_RF24.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_RF24.h,v 1.15"
  },
  {
    "path": "RH_RF69.cpp",
    "chars": 21878,
    "preview": "// RH_RF69.cpp\n//\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH_RF69.cpp,v 1.27 2017/01/12 23:58:00 mikem Exp $\n\n#inclu"
  },
  {
    "path": "RH_RF69.h",
    "chars": 49104,
    "preview": "// RH_RF69.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_RF69.h,v 1.33"
  },
  {
    "path": "RH_RF95.cpp",
    "chars": 15263,
    "preview": "// RH_RF95.cpp\n//\n// Copyright (C) 2011 Mike McCauley\n// $Id: RH_RF95.cpp,v 1.12 2017/01/12 23:58:00 mikem Exp $\n\n#inclu"
  },
  {
    "path": "RH_RF95.h",
    "chars": 40341,
    "preview": "// RH_RF95.h\n//\n// Definitions for HopeRF LoRa radios per:\n// http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf\n// htt"
  },
  {
    "path": "RH_Serial.cpp",
    "chars": 4897,
    "preview": "// RH_Serial.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_Serial.cpp,v 1.13 2017/01/12 23:58:00 mikem Exp $\n\n#i"
  },
  {
    "path": "RH_Serial.h",
    "chars": 11128,
    "preview": "// RH_Serial.h\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_Serial.h,v 1.11 2016/04/04 01:40:12 mikem Exp $\n\n// Wor"
  },
  {
    "path": "RH_TCP.cpp",
    "chars": 7539,
    "preview": "// RH_TCP.cpp\n//\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_TCP.cpp,v 1.6 2017/01/12 23:58:00 mikem Exp $\n\n#include "
  },
  {
    "path": "RH_TCP.h",
    "chars": 8128,
    "preview": "// RH_TCP.h\n// Author: Mike McCauley (mikem@aierspayce.com)\n// Copyright (C) 2014 Mike McCauley\n// $Id: RH_TCP.h,v 1.4 2"
  },
  {
    "path": "RHutil/HardwareSerial.cpp",
    "chars": 5441,
    "preview": "// HardwareSerial.cpp\n//\n// Copyright (C) 2015 Mike McCauley\n// $Id: HardwareSerial.cpp,v 1.3 2015/08/13 02:45:47 mikem "
  },
  {
    "path": "RHutil/HardwareSerial.h",
    "chars": 3961,
    "preview": "// HardwareSerial.h\n// Author: Mike McCauley (mikem@airspayce.com)\n// Copyright (C) 2015 Mike McCauley\n// $Id: HardwareS"
  },
  {
    "path": "RHutil/RasPi.cpp",
    "chars": 4527,
    "preview": "// RasPi.cpp\n//\n// Routines for implementing RadioHead on Raspberry Pi\n// using BCM2835 library for GPIO\n//\n// Contribut"
  },
  {
    "path": "RHutil/RasPi.h",
    "chars": 1845,
    "preview": "// RasPi.h\n//\n// Routines for implementing RadioHead on Raspberry Pi\n// using BCM2835 library for GPIO\n// Contributed by"
  },
  {
    "path": "RHutil/atomic.h",
    "chars": 2428,
    "preview": "/*\n* This is port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3 \n* v1.0\n* Mark Pendrith, Nov 27, 2012.\n*"
  },
  {
    "path": "RHutil/simulator.h",
    "chars": 1850,
    "preview": "// simulator.h\n// Lets Arduino RadioHead sketches run within a simulator on Linux as a single process\n// Copyright (C) 2"
  },
  {
    "path": "RadioHead.h",
    "chars": 57699,
    "preview": "// RadioHead.h\n// Author: Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY\n// Copyright (C) 2014 M"
  },
  {
    "path": "STM32ArduinoCompat/HardwareSPI.cpp",
    "chars": 5754,
    "preview": "// ArduinoCompat/HardwareSPI.cpp\n//\n// Interface between Arduino-like SPI interface and STM32F4 Discovery and similar\n//"
  },
  {
    "path": "STM32ArduinoCompat/HardwareSPI.h",
    "chars": 934,
    "preview": "// ArduinoCompat/HardwareSPI.h\n// STM32 implementattion of Arduino compatible SPI class\n\n#ifndef _HardwareSPI_h\n#define "
  },
  {
    "path": "STM32ArduinoCompat/HardwareSerial.cpp",
    "chars": 10800,
    "preview": "// ArduinoCompat/HardwareSerial.cpp\n//\n// Author: mikem@airspayce.com\n\n#include <RadioHead.h>\n#if (RH_PLATFORM == RH_PLA"
  },
  {
    "path": "STM32ArduinoCompat/HardwareSerial.h",
    "chars": 2437,
    "preview": "// ArduinoCompat/HardwareSerial.h\n// STM32 implementation of Arduino compatible serial class\n\n#include <RadioHead.h>\n#if"
  },
  {
    "path": "STM32ArduinoCompat/README",
    "chars": 297,
    "preview": "This directory contains some files to allow RadioHead to be built on STM32F4\nDiscovery boards, using the native STM Firm"
  },
  {
    "path": "STM32ArduinoCompat/wirish.cpp",
    "chars": 15617,
    "preview": "// ArduinoCompat/wirish.cpp\n//\n// Arduino-like API for STM32F4 Discovery and similar\n// using STM32F4xx_DSP_StdPeriph_Li"
  },
  {
    "path": "STM32ArduinoCompat/wirish.h",
    "chars": 5122,
    "preview": "// ArduinoCompat/wirish.h\n\n#ifndef _wirish_h\n#define _wirish_h\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib."
  },
  {
    "path": "examples/ask/ask_receiver/ask_receiver.pde",
    "chars": 764,
    "preview": "// ask_receiver.pde\n// -*- mode: C++ -*-\n// Simple example of how to use RadioHead to receive messages\n// with a simple "
  },
  {
    "path": "examples/ask/ask_reliable_datagram_client/ask_reliable_datagram_client.pde",
    "chars": 1594,
    "preview": "// ask_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, rel"
  },
  {
    "path": "examples/ask/ask_reliable_datagram_server/ask_reliable_datagram_server.pde",
    "chars": 1460,
    "preview": "// ask_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, rel"
  },
  {
    "path": "examples/ask/ask_transmitter/ask_transmitter.pde",
    "chars": 655,
    "preview": "// ask_transmitter.pde\n// -*- mode: C++ -*-\n// Simple example of how to use RadioHead to transmit messages\n// with a sim"
  },
  {
    "path": "examples/cc110/cc110_client/cc110_client.pde",
    "chars": 2267,
    "preview": "// cc110_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the "
  },
  {
    "path": "examples/cc110/cc110_server/cc110_server.pde",
    "chars": 2193,
    "preview": "// cc110_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the "
  },
  {
    "path": "examples/mrf89/mrf89_client/mrf89_client.pde",
    "chars": 1827,
    "preview": "// mrf89_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the "
  },
  {
    "path": "examples/mrf89/mrf89_server/mrf89_server.pde",
    "chars": 1818,
    "preview": "// mrf89_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the "
  },
  {
    "path": "examples/nrf24/nrf24_client/nrf24_client.pde",
    "chars": 1921,
    "preview": "// nrf24_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the "
  },
  {
    "path": "examples/nrf24/nrf24_reliable_datagram_client/nrf24_reliable_datagram_client.pde",
    "chars": 1877,
    "preview": "// nrf24_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, r"
  },
  {
    "path": "examples/nrf24/nrf24_reliable_datagram_server/nrf24_reliable_datagram_server.pde",
    "chars": 1739,
    "preview": "// nrf24_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, r"
  },
  {
    "path": "examples/nrf24/nrf24_server/nrf24_server.pde",
    "chars": 1846,
    "preview": "// nrf24_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the "
  },
  {
    "path": "examples/nrf51/nrf51_audio_rx/nrf51_audio_rx.pde",
    "chars": 3566,
    "preview": "// nrf51_audio_rx.pde\n// Sample sketch for nRF51822 and RadioHead\n//\n// Plays audio samples received in the radio receiv"
  },
  {
    "path": "examples/nrf51/nrf51_audio_tx/nrf51_audio_tx.pde",
    "chars": 4948,
    "preview": "// nrf51_audio_tx.pde\n// Sample sketch for nRF51822 and RadioHead\n//\n// Reads audio samples from an electret microphone\n"
  },
  {
    "path": "examples/nrf51/nrf51_client/nrf51_client.pde",
    "chars": 2137,
    "preview": "// nrf51_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the "
  },
  {
    "path": "examples/nrf51/nrf51_reliable_datagram_client/nrf51_reliable_datagram_client.pde",
    "chars": 1962,
    "preview": "// nrf51_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, r"
  },
  {
    "path": "examples/nrf51/nrf51_reliable_datagram_server/nrf51_reliable_datagram_server.pde",
    "chars": 1823,
    "preview": "// nrf51_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, r"
  },
  {
    "path": "examples/nrf51/nrf51_server/nrf51_server.pde",
    "chars": 2059,
    "preview": "// nrf51_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the "
  },
  {
    "path": "examples/nrf905/nrf905_client/nrf905_client.pde",
    "chars": 1496,
    "preview": "// nrf905_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the"
  },
  {
    "path": "examples/nrf905/nrf905_reliable_datagram_client/nrf905_reliable_datagram_client.pde",
    "chars": 1694,
    "preview": "// nrf905_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, "
  },
  {
    "path": "examples/nrf905/nrf905_reliable_datagram_server/nrf905_reliable_datagram_server.pde",
    "chars": 1554,
    "preview": "// nrf905_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, "
  },
  {
    "path": "examples/nrf905/nrf905_server/nrf905_server.pde",
    "chars": 1418,
    "preview": "// nrf905_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the"
  },
  {
    "path": "examples/raspi/RasPiBoards.h",
    "chars": 3345,
    "preview": "// defined pins definition of different hardware boards used\n// Contributed by Charles-Henri Hallard http://hallard.me\n\n"
  },
  {
    "path": "examples/raspi/irq_test/Makefile",
    "chars": 518,
    "preview": "# Makefile\n# Sample for spi irq leve testing for Hope RF modules on Raspberry Pi\n# Caution: requires bcm2835 library to "
  },
  {
    "path": "examples/raspi/irq_test/irq_test.c",
    "chars": 1304,
    "preview": "// irq_test.cpp\n//\n// Example program showing how to use multiple module RH_RF69/RH_RF95 on Raspberry Pi\n// Requires bcm"
  },
  {
    "path": "examples/raspi/multi_server/Makefile",
    "chars": 1266,
    "preview": "# Makefile\n# Sample for multi modules server on Raspberry Pi\n# Caution: requires bcm2835 library to be already installed"
  },
  {
    "path": "examples/raspi/multi_server/multi_server.cpp",
    "chars": 11383,
    "preview": "// multi_server.cpp\n//\n// Example program showing how to use multiple module RH_RF69/RH_RF95 on Raspberry Pi\n// Requires"
  },
  {
    "path": "examples/raspi/nrf24/Makefile",
    "chars": 1604,
    "preview": "# Makefile\n# Sample for RH_NRF24 on Raspberry Pi\n# Caution: requires bcm2835 library to be already installed\n# http://ww"
  },
  {
    "path": "examples/raspi/nrf24/RasPiRH.cpp",
    "chars": 3156,
    "preview": "// RasPiRH.cpp\n//\n// Example program showing how to use RH_NRF24 on Raspberry Pi\n// Uses the bcm2835 library to access t"
  },
  {
    "path": "examples/raspi/rf69/Makefile",
    "chars": 1500,
    "preview": "# Makefile\n# Sample for RH_RF69 (client and server) on Raspberry Pi\n# Caution: requires bcm2835 library to be already in"
  },
  {
    "path": "examples/raspi/rf69/rf69_client.cpp",
    "chars": 6072,
    "preview": "// rf69_client.cpp\n//\n// Example program showing how to use RH_RF69 on Raspberry Pi\n// Uses the bcm2835 library to acces"
  },
  {
    "path": "examples/raspi/rf69/rf69_server.cpp",
    "chars": 5959,
    "preview": "// rf69_server.cpp\n//\n// Example program showing how to use RH_RF69 on Raspberry Pi\n// Uses the bcm2835 library to acces"
  },
  {
    "path": "examples/raspi/rf95/Makefile",
    "chars": 1500,
    "preview": "# Makefile\n# Sample for RH_RF95 (client and server) on Raspberry Pi\n# Caution: requires bcm2835 library to be already in"
  },
  {
    "path": "examples/raspi/rf95/rf95_client.cpp",
    "chars": 6218,
    "preview": "// rf95_client.cpp\n//\n// Example program showing how to use RH_RF95 on Raspberry Pi\n// Uses the bcm2835 library to acces"
  },
  {
    "path": "examples/raspi/rf95/rf95_server.cpp",
    "chars": 6164,
    "preview": "// rf95_server.cpp\n//\n// Example program showing how to use RH_RF95 on Raspberry Pi\n// Uses the bcm2835 library to acces"
  },
  {
    "path": "examples/raspi/spi_scan/Makefile",
    "chars": 501,
    "preview": "# Makefile\n# Sample for spi scan Hope RF module on Raspberry Pi\n# Caution: requires bcm2835 library to be already instal"
  },
  {
    "path": "examples/raspi/spi_scan/spi_scan.c",
    "chars": 3057,
    "preview": "// spi_scan.cpp\n//\n// Example program showing how to detect multiple module RH_RF69/RH_RF95 on Raspberry Pi\n// Requires "
  },
  {
    "path": "examples/rf22/rf22_client/rf22_client.pde",
    "chars": 1452,
    "preview": "// rf22_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the R"
  },
  {
    "path": "examples/rf22/rf22_mesh_client/rf22_mesh_client.pde",
    "chars": 2043,
    "preview": "// rf22_mesh_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed reliable"
  },
  {
    "path": "examples/rf22/rf22_mesh_server1/rf22_mesh_server1.pde",
    "chars": 1614,
    "preview": "// rf22_mesh_server1.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed reliabl"
  },
  {
    "path": "examples/rf22/rf22_mesh_server2/rf22_mesh_server2.pde",
    "chars": 1614,
    "preview": "// rf22_mesh_server1.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed reliabl"
  },
  {
    "path": "examples/rf22/rf22_mesh_server3/rf22_mesh_server3.pde",
    "chars": 1614,
    "preview": "// rf22_mesh_server3.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed reliabl"
  },
  {
    "path": "examples/rf22/rf22_reliable_datagram_client/rf22_reliable_datagram_client.pde",
    "chars": 1754,
    "preview": "// rf22_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf22/rf22_reliable_datagram_server/rf22_reliable_datagram_server.pde",
    "chars": 1618,
    "preview": "// rf22_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf22/rf22_router_client/rf22_router_client.pde",
    "chars": 2265,
    "preview": "// rf22_router_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed reliab"
  },
  {
    "path": "examples/rf22/rf22_router_server1/rf22_router_server1.pde",
    "chars": 1755,
    "preview": "// rf22_router_server1.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed relia"
  },
  {
    "path": "examples/rf22/rf22_router_server2/rf22_router_server2.pde",
    "chars": 1755,
    "preview": "// rf22_router_server2.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed relia"
  },
  {
    "path": "examples/rf22/rf22_router_server3/rf22_router_server3.pde",
    "chars": 1755,
    "preview": "// rf22_router_server3.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, routed relia"
  },
  {
    "path": "examples/rf22/rf22_router_test/rf22_router_test.pde",
    "chars": 2731,
    "preview": "// rf22_router_test.pde\n// -*- mode: C++ -*-\n//\n// Test code used during library development, showing how\n// to do vario"
  },
  {
    "path": "examples/rf22/rf22_server/rf22_server.pde",
    "chars": 1461,
    "preview": "// rf22_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the R"
  },
  {
    "path": "examples/rf24/rf24_client/rf24_client.pde",
    "chars": 1420,
    "preview": "// rf24_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the R"
  },
  {
    "path": "examples/rf24/rf24_reliable_datagram_client/rf24_reliable_datagram_client.pde",
    "chars": 1640,
    "preview": "// rf24_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf24/rf24_reliable_datagram_server/rf24_reliable_datagram_server.pde",
    "chars": 1504,
    "preview": "// rf24_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf24/rf24_server/rf24_server.pde",
    "chars": 1435,
    "preview": "// rf24_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the R"
  },
  {
    "path": "examples/rf69/rf69_client/rf69_client.pde",
    "chars": 2232,
    "preview": "// rf69_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the R"
  },
  {
    "path": "examples/rf69/rf69_reliable_datagram_client/rf69_reliable_datagram_client.pde",
    "chars": 2115,
    "preview": "// rf69_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf69/rf69_reliable_datagram_server/rf69_reliable_datagram_server.pde",
    "chars": 1977,
    "preview": "// rf69_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf69/rf69_server/rf69_server.pde",
    "chars": 2517,
    "preview": "// rf69_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the R"
  },
  {
    "path": "examples/rf95/rf95_client/rf95_client.pde",
    "chars": 2548,
    "preview": "// rf95_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing client\n// with the R"
  },
  {
    "path": "examples/rf95/rf95_reliable_datagram_client/rf95_reliable_datagram_client.pde",
    "chars": 2897,
    "preview": "// rf95_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf95/rf95_reliable_datagram_server/rf95_reliable_datagram_server.pde",
    "chars": 2763,
    "preview": "// rf95_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, re"
  },
  {
    "path": "examples/rf95/rf95_server/rf95_server.pde",
    "chars": 2583,
    "preview": "// rf95_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple messageing server\n// with the R"
  },
  {
    "path": "examples/serial/serial_reliable_datagram_client/serial_reliable_datagram_client.pde",
    "chars": 2652,
    "preview": "// serial_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, "
  },
  {
    "path": "examples/serial/serial_reliable_datagram_server/serial_reliable_datagram_server.pde",
    "chars": 2483,
    "preview": "// serial_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addressed, "
  },
  {
    "path": "examples/simulator/simulator_reliable_datagram_client/simulator_reliable_datagram_client.pde",
    "chars": 2025,
    "preview": "// simulator_reliable_datagram_client.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addresse"
  },
  {
    "path": "examples/simulator/simulator_reliable_datagram_server/simulator_reliable_datagram_server.pde",
    "chars": 1880,
    "preview": "// simulator_reliable_datagram_server.pde\n// -*- mode: C++ -*-\n// Example sketch showing how to create a simple addresse"
  },
  {
    "path": "project.cfg",
    "chars": 101671,
    "preview": "# Doxyfile 1.8.8\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) "
  },
  {
    "path": "radio_config_Si4460.h",
    "chars": 31812,
    "preview": "/*! @file radio_config.h\r\n * @brief This file contains the automatically generated\r\n * configurations.\r\n *\r\n * @n WDS GU"
  },
  {
    "path": "tools/chain.conf",
    "chars": 413,
    "preview": "# chain.conf\n# config file for etherSimulator.pl\n# Specify the probability of correct delivery between nodea and nodeb ("
  },
  {
    "path": "tools/etherSimulator.pl",
    "chars": 6062,
    "preview": "#!/usr/bin/perl\n#\n# etherSimulator.pl\n# Simulates the luminiferous ether for RH_Simulator.\n# Connects multiple instances"
  },
  {
    "path": "tools/simBuild",
    "chars": 450,
    "preview": "#!/bin/bash\n#\n# simBuild\n# build a RadioHead example sketch for running as a simulated process\n# on Linux.\n#\n# usage: si"
  },
  {
    "path": "tools/simMain.cpp",
    "chars": 1572,
    "preview": "// main.cpp\n// Lets Arduino RadioHead sketches run within a simulator on Linux as a single process\n// Copyright (C) 2014"
  }
]

About this extraction

This page contains the full source code of the hallard/RadioHead GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 138 files (1.1 MB), approximately 332.5k tokens, and a symbol index with 130 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!